polycyclic-2.17/0000755000175100001660000000000015054022512013154 5ustar runnerdockerpolycyclic-2.17/README.md0000644000175100001660000001076715054022512014446 0ustar runnerdocker[![Build Status](https://github.com/gap-packages/polycyclic/workflows/CI/badge.svg?branch=master)](https://github.com/gap-packages/polycyclic/actions?query=workflow%3ACI+branch%3Amaster) [![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.17/makedoc.g0000644000175100001660000000056315054022512014733 0ustar runnerdocker## 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.17/read.g0000644000175100001660000001327215054022512014244 0ustar runnerdocker############################################################################# ## #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.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 polycyclic-2.17/LICENSE0000644000175100001660000003647415054022512014177 0ustar runnerdockerThe 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.17/CHANGES.md0000644000175100001660000003023315054022512014547 0ustar runnerdockerThis file describes changes in the GAP package 'polycyclic'. 2.17 (2025-08-28) - Add a method `SemidirectProduct( N, alpha, G)` where `N` and `G` are both PcpGroups (contributed by Sam Tertooy) - Enhance `AbelianPcpGroup` and `AbelianGroupCons` to support `infinity` in the list of orders (implemented by Sam Tertooy) - Enhance `Centralizer` to fall back on generic GAP methods, so it can be computed in more cases - Fix correctness bugs, where wrong results could be returned, in the following functions: - `AddToIgs` - `Centralizer` could sometimes set an incorrect igs in the computed centralizer, which could lead to incorrect results later on - `ConjugacyElementsBySeries` - `FrattiniSubgroup` for finite pcp groups (reported by Heiko Dietrich) - `IsConjugate` (reported and fixed by Sam Tertooy) - `IsNormal` sometimes returned `true` even if the inputs did not normalise each other - `OrbitIntegralAction` - `Subgroup` sometimes produced a group with invalid Pcp and e.g. membership tests could fail - Fixed a bug in `AbelianGroup` resp. `AbelianGroupCons` method for `IsPcpGroup` and also in `AbelianPcpGroup`, `DihedralPcpGroup`, which resulted in either an unexpected break loop or in a corrupted group when used to create a group with some but not all generators of order 1 - Fix further bugs where an error was raised unexpectedly: - `ConjugacyClasses` sand `ConjugacyClassesSubgroups` sometimes did run into errors or took far too long - `IsSingleValued` (and possibly `CoKernelOfMultiplicativeGeneralMapping`) for certain trivial maps - `Random` for trivial groups (reported and fixed by Sam Tertooy) - `PreImagesSet` if the input set is not contained in the image of the homomorphism - Fix `SchurCovers` to always return list, even if only one cover is found (fix by Sam Tertooy) - Support and use NC versions of `PreImages`, `PreImagesElm`, `PreImagesSet` and `PreImagesRepresetnative` (contributed by Chris Wensley) - Made most global functions and variables read-only to catch code accidentally overriding them - Removed some unused code - Various janitorial changes 2.16 (2020-07-25) - Fixed a bug in `NormalIntersection` which could lead to wrong results; this also affected other operations, such `Core`, `Intersection` - Fixed `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) - Fixed some bugs in `AddToIgs` and `AddTailInfo` - Some janitorial changes 2.15.1 (2019-10-03) - Fixed a regression that could lead to an infinite loop in IsomorphismPcGroup 2.15 (2019-09-27) - Added license information to package metadata - Added 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.17/doc/0000755000175100001660000000000015054022512013721 5ustar runnerdockerpolycyclic-2.17/doc/chap4_mj.html0000644000175100001660000005341415054022512016303 0ustar runnerdocker 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.17/doc/chap4.txt0000644000175100001660000002357015054022512015470 0ustar runnerdocker 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.17/doc/chap7.html0000644000175100001660000020434315054022512015617 0ustar runnerdocker 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 polycyclic 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× 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.17/doc/chap5.html0000644000175100001660000014021715054022512015614 0ustar runnerdocker 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.17/doc/chap9.txt0000644000175100001660000001254315054022512015473 0ustar runnerdocker 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 polycyclic 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.17/doc/chap0.txt0000644000175100001660000002312215054022512015455 0ustar runnerdocker  Polycyclic   Computation with polycyclic groups  2.17 28 August 2025 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:mhorn@rptu.de Homepage: https://www.quendi.de/math Address: Fachbereich Mathematik RPTU Kaiserslautern-Landau 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 the GNU 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 and contributions to this package and its documentation provided by GAP users 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.17/doc/chap1_mj.html0000644000175100001660000001341215054022512016272 0ustar runnerdocker 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.17/doc/chap2_mj.html0000644000175100001660000002132515054022512016275 0ustar runnerdocker 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.17/doc/times.css0000644000175100001660000000026115054022512015553 0ustar runnerdocker/* times.css Frank Lübeck */ /* Change default CSS to use Times font. */ body { font-family: Times,Times New Roman,serif; } polycyclic-2.17/doc/chap7_mj.html0000644000175100001660000020572215054022512016307 0ustar runnerdocker 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 polycyclic 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\times 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.17/doc/chap3.html0000644000175100001660000007725415054022512015624 0ustar runnerdocker 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 [LS90] and [Sim94] and combinatorial collection from the left by [Vau90]. In addition, the package provides access to Hall polynomials computed by Deep Thought for the multiplication in a nilpotent group, see [Mer97] and [LS98].

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.17/doc/manual.pdf0000644000175100001660000121465215054022512015704 0ustar runnerdocker%PDF-1.5 % 194 0 obj << /Length 272 /Filter /FlateDecode >> stream xuAO0ˁqtJ{tͪ1x `<D$"DKf5=ʹyߛ7-VJDݔ٥B;+QrHUP8xB9z_]>7+4e)-dl.i!k~d8Oh۞=%!*jBpb"ʈLXgbW iȵY!C@+n"@ĽeUQͲa)+pB+F n#l[&XSp% endstream endobj 202 0 obj << /Length 635 /Filter /FlateDecode >> stream xڽTr0Zⅅ$[*a#LJ U6ԖKEzشf C XsϽGcd:]R 8a0p)fv?#LO\ϣӗFP ` 8ᶩW3@e: FS[)1#0 2F2 E =܎D |=⢐:? Gi yND@FEGwSij:fjE܂=+oq*Z.oIDu|ƬOFfh)'g> stream xZms6_I$@x:g4c'3&@I,$*@C(V$E>X,@RAH$1f$g8$8q©"5ZFzQ&f9AfÕIYQ99#9'T_YB@&TԀ3B 8+QJ1p". QE dKζhdoӾz`p镋Fi>pZ5ފx8Ƞ9cWkci > M8cx4c)}`EhjvApUc0ىi3룟pA~=ћ/&r Olw{p@-Ǯݳh3Ehg7 Г籎Ӻ?˧w?;c3j}Ua?+΍?R}wSU_P:m?76ɽuZ&CPo:j&o_9"g5.uru g\5ܧXl#9NQL07Q"S FY>FR }vVsdK!4t&5Gߜ#c{e`k4P_)cGrly,IP#VySrjuJv?2bݤhz܅=“xi'Hmr^z9*WUx5'3 ShNm47u/Zk߯% Op{NM_Msd۞0f<ᇮaf9]y;8'"NFmW=HOg9$Dhd_˹ֹ?H_͗rY̮Ӌ ?(L$yBu*]d?Mf''*}wya_i5WWWɸ| X=1!:<'LRDI?۪ZϟIQ$dOL endstream endobj 210 0 obj << /Length 705 /Filter /FlateDecode >> stream xuTKo0PNIlUѪݪ, 1IfR73T#֫s"#mweI&h[`cR˶)/֊ JpA 0SL: ìr&8NRQb(9S6TM7'.R: "0K3>3NÈq0Bb'Y1levDS F([0nc7zx`͑p͹^Kp" m3ջVGy6.08^,b\rnNz[Y ;3g3c`+`K@e#Grqm B/7A$rOьۗ0%a) J٨Tyvzo9* 9cnSi*Gj60kjzk*Y~i׷hΘq}RɐqObz /5< endstream endobj 248 0 obj << /Length 1487 /Filter /FlateDecode >> stream x]s8+3kUGґSُN^`$`4W qv:b|Sڣ޻3_Z1AV_yJI! _z,-MZ9zD^ħw@I@ԛKA8 Ѡ7 B!9PJ oȌy;5OpI}+7o?8"Y?kgt6gH2,n$ auu:ы9io+ΐ6SkxpɒDeУm ][ F ʛ'()F$Ң+6u ٹ#Rd6GFuA`cpa+) 9@9LQ}an5i2n Q&3@ `h絉0,LXnr3A߸ԧ\YJ#~\RtpѺB˻rN[חtoDf] &DhN+5L{1\֧7uGä$ڱYv`9RuYxU tqp0Rvo!E^GC7Q@ `~uch`<+)fʛuecejдeILˍ:Q/SF4W}afD0xpd7#utv >JƟuh:dâsWw&L ڤQ]/g G4ȩc>SC=(AT[)ֿ-8o1-GؽS;z e!-jIF )lm26DS;O2@YicVϕ!USTڙ @pIҴj\uڿo}0oL l3?rk[SHЁb@H{+8FPUrwE~_:|}v=>"hAˬ^M^v Sqi7SP-W1w.sG'!VL}Yg&"w{TZڤ[tDz&pz}|}c RRU&jiSGI}mb tmq&Mmsmbe*N^CBO8=Uy%]d| 2\IODnҺ> stream xr0~ -EW$-Ligv\g`2I޾"7,R/l.wίb9P b3D ,ahhkF!TH0@L)Um&A܏s5辘_OT Se]7JѾ=ꆣ;#y.M2Ot1=Ln(sA{؎WS -lZsˊmm.iRB=89BZm<dž CJtqoLz &P,{o%Hb,Y=r7?ͮnxB& \78?Q?6wJU@3&5"' 2^#v^fcc-S½ 5ЊFj[tz7N {<֘j&h&$xr|9b-BsHwt{% #7/H^_wUK] z=\^CF ΂v# 7 yM2|~_F>]ۓe}p<:{e 1Ҟ5idnӭ]RwqX֤ȱG B> stream xڭێ۶=_#›n}KlڞE@e& I3ʒMACj8 o޼3J8MUzد4NtJkUvߣ幷zX=OldDtdOX*qaܜ%&P uũZ&\%˶nDζ W/$4Nsu>b\dN__KUߴ]dRF#}FV*NEH'aIj/Vu=YZ&vh]ζƦgZhi:_i|γ&ʿ!]oOόֶ-{=2  _`Z!gv=0q!Z^F-u;AtAo cQ:G YK3 vp:/' zՈ Ce:&]$RD-Qc^cFwG*[-Kαa G\C0:2V͡fw#+{kAt=Ӊp> Zd!ot:'O5s94G[1!'||gsG;YtA贛$3/LOhl $'CDm/xxZFuދ@:xm6ybnJo/Ui@,r)꤈ef6YTĪg (ܮA~^zɒXᏒrhK9K;Uƨs8b(ʾ@B$jDagQBҢE2VgW0# (I.Q5$TrET*2B@dv$,]u9-Q.=vg#m9&bFw8؆ nJd]L:x@ZLմ}>/Fj,O5t!K-Bgךc 0z# RS=J)`7\e#( ʓtV._cN*y#pv tjlD8RZ=Uil#1k4ݯ/v1m!I(j5 ϐy4#ÓaZ67*΄Jkf*ɽ9C_.k"w۸ x /N#-ӻ|kwi$ݎ< 8 \P eDž|ܯlR&9*ҌLqk8;LҫRp0ipQL*%IOMx @۲ CӂQ< O!eM ]]yuCO# 8)YoCJ,QmO-^. }E>}.Mx~%> ӄ 0^C1$;Z7EJZkg+/s<RI8Mo 1ߎeh֒Ee1θ1Cc+b-,iBa`.7t*XtHq%s;5t::͒אbi#[{o;K1D?g;+1Z>7!r{<˵ cuVP}5[ $$Xя`!t5ԭnvFَЖc96^g{xUgSaA2F8Qyfע*z};j*vzID\ +W ZWQL4ƻ:Wܼ^^s+9|`j kڐX_)#ҸRԗW Se4:1KQ;c^w\/D27b÷R/7 ^xG0Kd &!k]xϏBqW989Lr\oKlw>_DwF)4 wq"g /> *B){)6(~m_:*jۖIicyi/0=ݾk"=Tۆf.> stream x\Ks-de9bw\*l\$ߧ{fW"4_+?x[ig]\ό&JVfWoC[,s(ow3^~.6*atX&B{7݂quXo76|>l>>֫xe}9T(S쭢3FJ "CG([2&A˼ y+iZzΫ`j 4m*|]H`d(]b3XY P*fKFA)Y! fW1ƌ&xlGuQD$b  >JS)DNMuS=Mz%&)AmŠioX .c'/la~њNz ܈wA:lw}pVՒ df@?}{]Tߍ9|\nCmT6R`nj˺tp}VnOKm-Jlwq.=6 toeک-)ٔbE~To}]jbk(H2G)_ W_Z+uiw{?xc.t!Dn#c0rφqv֑zSFn 44IUBqD90sb (v("N'bacom$`Ζ\SZ7vvNͪ.): SE6?lj*fE/F+ZVnk~ ;5;+iztUWaHZ]9oT 0OR~T)/1-5gp)iߗڅqcvє 'j-+uqϾPEj36G>L0#u+#  &@%+@RDb~?C(Phi;^.!XB5`ݧbqWerp+ۮP *Q ۘSGGJao,|+oQbQg3|FHTYK(-r01{H9e>"~X)&qS&l *y|8kQҜ̦mg6puXzhێ}Բ$AVx{N]r-rV\ RHP&*|:Fwһ/5:sƾqM| ~YZ@Ïu,b*| ^fRyEM<):*qW\TxxYK10b-Qvb__꧘)JhRP->U.),E+<gp[܏Y=VyX9˙Vxߛaie?s626|Qm`'a3䊝BŋJ)6ȏRH-$ŗ83g&l7ms~k`ѴEB9߄WE3]#X3. Lrdz%YlO5x6= N@ZvCI48h'p@>5##(PgeG jSj_d= hTw h;[5G0_kc3mhz`\m7Y#.JɱD\~? ݣXL+3vj갪}3&?M0)'U&;X҇/I .Fn4o#: K%$yz݊ 0ΦZ*`F=؛rQG#@xou$5zJ6yJB>Tbkd~@nψJx"l,7a#W_N^*V)hH?5!ٍp{Ős"ʾC;WwULfWٗwle?$ H6P*tr$,n2 ~4^^q&['џ`Pkdi^>@O<ԿyM#' imyEX`-*֍ @Ӭ@6wLd\< endstream endobj 316 0 obj << /Length 1242 /Filter /FlateDecode >> stream xڽWKo6W(YIJC 4AziA+qBBfCZS+qR`SK gy}CdϷ7?<$$V& $,mw=CݵuW8QKPJaenpl)mʑ,zm(cP(H!A͚MF՛ʴz=tF5Qߨưze@fCFElCk~Q5l@tFD> Yڂ*3!+x "ҽRM@j5lr#dC(-gj\R W*5_aN'fKY$E\ȽS)%v`WU}a=DRDrB_Aq H+H@snMbչWHİ| [kʑ W~FNHOk OI(Y'lK KګR^B1hwc-4̊Ӹgt]A-\ڵTޅ,pŚP4<[9@k*":( |_oGYKl|SH$']U[kKJ0U[@CjbJ>]oMNN;^M`̒A }63r]gQAP Ip͂g,ҿlx;jjVL[*`C70ܯ>BQΕ)lݳ (OW jAe.UJd%9[rTG+ԏنܷv!~V4t4JV0Ú(:xCǵy '+b.'_*[4/2%ٜl) B^zmr]<*mep3^^ {]}h?~@L{x -͛ 7˥ȗ 'ax"L ^sX^/G>Fᩚ DOE* Pk[UW'7I\kř5 nvbUv3)8r$P&%EZu{ Q?E{8v}Q8.v_ih$PVd,j_[/iU7. -8`ܷ?q$Kw!yu`Dv@k2g)S.J7( Hj17,|6Fⴞ.,kgtJN0x^Kl%?bO(ڙ_no, endstream endobj 326 0 obj << /Length 2642 /Filter /FlateDecode >> stream xڽɒۺ-Rc'vrrrp$gTH*>Ip8۫$@;z값o$ͻ]i)LUԤ,Uhٯnť/Vrm6_oF ~##MDnsHj8aa|TsC*WJ ']ؕОe>l r30 "%).6 R/;mc*2o"3yeW}WM͌t|k@XdZ3i['[mHoXcQTyV[X@4q~ Vߩ2lߍg^gٕJ}9_}UDWyh}U={(-~ɣLJs>e ~ 6HL=,'eFb#4f Y\ , / 1At@SA 'P_D"2>8dG'Kr.+!%]Q{RFe`t4ݵm<~~R?+2@,SP F"Yܶ+;S 8Jj-CNSFK:6uuf]ݒX+V$.SP ѱR3p#ceW &XN}F}Z~˸☘C.` qC0pz) h\.$dOHZf5JmQx(`  f%!@fyC(^b|$lК|&XPu۱vEWq7rqVE_['4ИEaǘr& 4$II G9L nc+G >nq#DwƈU/Ϳc1TJVt+A 4 0 n IaeNp0UPxFp}Tap6pDMB# ήPuVjц^(<)4w:#S㊬咨-/eAe|]:5M] {XŤC2ԪUHg 0ܾG֠ c8Ts.yfd@8$QV*mΓ4V/ lSBߪR>2c1B" &z&\[HRPEǴf4dW_׳_ KQ3WR]?H"- ;U5F@WIO0DJ8b8`* -IپBFNm e~>A-Lih˟ \3[-ҳ69ûG0ͱ=M߇|}QDCq uu4=מ(h 'p9 l]@]eU^vZ=LrQ*!~Hl{f4#"V:>|싾 w_[?=Քo[?MLhngrArY#WڅcG p!K"vR+匞}tу 5{:FM1ގ^qz cb*J@uw2GcL]V(;f(7b ~HlRfcA!ƻzQ`a y[d!9juT2n.sٍ2WJ;vI蜲+1r~G*OP u&v?Gd6M^F}0rtԶ#25!"ij&z-D1}RS/ =sOa Α/DqxG=Řnp";m+S9kpфC:UKO#1=;G51Q3 fڌxԄ6v$#j/M_e{ZS%9J|ZV&Oł/_yͩץ9 qdᄀBJ12# ޑV[h=P_-%XM&4JM[I?O2xʽHEگO倸닶R?o(ϗy VWZ̥:iy)@B6C5D?X;NnhZ*TS`_.oi*wA*zQq5nA7(!0G/,|f|扜%(]2^aK_{xҗ]!' 2 {O]]P/݇|qzc9u+GJH<| B :NCdxUl]7gsy/G瞫@QX{0팁 ͵V;z@,ש9LJرO]NaˣQqkR~ W_ׅC3S*<Βᵊ⫖"?e3U{I?޼ $ endstream endobj 206 0 obj << /Type /ObjStm /N 100 /First 907 /Length 2795 /Filter /FlateDecode >> stream x[Qo~ׯc\I$Vڴ [S3$|D@佽~|3d4qR ѩT3Xqmbĵ" Ɖqk$' >d' P>1 T(8 ./Ks1V #^9 UY fiN&1)5U`TkkQ1@`KB`]RbVs))*.§K% %|ܩF. e6e ɥXb]\.x"BYr); OLL1hY2;al `F<`P[Օ{\˂+M`="+9uhhKeWFkU~J 7b$W[%UЮ j75|^9ek.3- "IP*|E5~O; p(R$$EYpEl q%ǡ_|=TQ` ohr! f姂(,XNJOtx4s6LV"QG`jJLɓl9>[_^]|^M_M_O_L/W=2rVoиZɖ|iq@'Ǜt8fsi9oŻ}vrtO}E}ǒV;:9ݬٟ.7&Sk>AD|eW?߯^PoT}ᾏp^o9$`qg볍{M4g~YfȮ&~pf@b}|gnvƽGoVxlsHL5b./Wsxf/nVensU$6(DM3oҫ{ZgRP1_ZZEx__NOWgywzPw5^Ԏ&R0Nόy7>ĩ21L,i0o֯_kn\J~~b./vyi]vyi]vyi./uy)qnv{77<<0]T_1Rİkwfuc#ENTo3c1ǃPP P4EP4,88M\9r,}j7^G Gތ<HI8G TW[¡ý#T7 0rzG 10GP;Gz@ILyYN[15𙎁BEeV<*ȏZOs$EV>]Cs,=k;-p KE S.XK8j;tr 9̕gӀzu6-"p8b#hdFAD]Q((lHKNچҘ @#bQ=|-#"(;g{aIICf3.mW rn ixZmxEUv+_'Rfm0 sn"4erlMlݘM Va [{\2.$Ajc r:2/-tT5ܖpo'BB+y%sJQdƵ5R*w{VWL;P׺MP\m&{h).N]ܻ2wy˓{'.O]]]˳.nOwȾeP,-yxQԀ E} 8p*[.oEY]Yҥ'Y+,Sun4+Vx9 s 4pGH"%p -pz%㓬8INVhDq]pOq3UbG^Iւyऒh[7jK[fG̞uݑ߮ؑQh qpSAZ$7#.T5.xyD1 Nꨘ>\st< nP[W0>V5pl[j;>VH ۵FKjEj| *NJ'GQdtU:*lNjWadÿY7j & ݊ur!v<,aW0xAacX m'.[޹wrmɎ@?}긖6jvGd =vWo0I]yV/>wXw?#hm}~hlX3h{޵ W`Yj $D=G > stream xZKsW(UYw6S5;5Tj2mvJBROE%YJ*@ htGxts_~bP0.FT)Dx6R|m\=&Dflo2טD)^l+FM9sk—/feD&t*oe-k_ok1eb|`*qYkh"|‚ *@1b|ʟ[xLUl6^ԯ}#\;s@ iVT qF./x&Xu6`R8W#+j9Bs6e"b%pLQ,R_%u0̐u2Ls:?=j*ߡ opfMg}C*w ,?kce߁P~5W7??ܽ 63;|Θ!p*Rމ l|7!xZ?472G)lE?W{,8 L)M҂h];}蠵l;A[N?ldxcoWkZ= oS=EG0&۹qGV{EeBcg㎆髇ٶ{MX|O"jtW`^#zL&-{VN^nJ% r V/^bT>Ax֍EtC~8d4sB+0k'6vh#ԝ7u7!Df} pmwZhmb9$a4 y#¡SfM^5h,G$ZP VI+o1I/a w`3I#;poq1\'^4DΦ!4CFN\QهҮ+]\^)]5ş.`0q^ "NBP.&8K·kqJq0}| ǀjhފu n$]k"BK_Ơ9 DD2OOyOx_Gbԥ:IBzh59s80īᘢeX/Sr=0hFoC>\Vfm )$:ݼ2f]O㙯Gݟ8RɗL/N++uv+M=Dap-VLuv^˄mSowR>7 b`P婠a?qpx|>fHR)gN"mes=e _2xT MpA<x<'!d[d8nN5{z# D-DV<7305:?|WizӤ8z%4-E6ݍ(](Me ٔ)*SoWM◕NArkg F =z3~T~|$ZT7.38D߰s_tuA OO  QZv6 BACK&) "ٖAKIbs?u~a^/t.#s\ V~b4f;Y@ ]>6b)o/AP ⡼[g|֠6S .zSN9»XlEݘ|fԣOýsi endstream endobj 353 0 obj << /Length 2190 /Filter /FlateDecode >> stream xڽYKs6WHUAqLRS)Ɂ`TT4^|3 n4)ѧ>|"R(SDUqJ#% 禺;"kQׇ,2i!3j'laGF A3h& J&1Dw¯}}~N8)L?|t%Z9+[^*ckXT/X`7#K3C4aFTwDO5)GB A >,"dnκÔJ±D*QB<*x=-PXL_eD',P*DB2:ܸ?jҖOGw{ʃ,q1ىD+kk=6bƓMۭyf7"e': 1V")ʆ1m1q $oIazME %bʆA q0@`<0ytWI(!]>5]J9{Iq[΢Dڼ a=J >5i۰ e Y53B6sSw`YOUv&m3SWdcۜС{{jRy&א=qK5]w9kgm"+“l,R>i}X~-_N 9F{M[дnڃu.gGiN~N ܆¸m;eR}d"D|É6zbd 9L Ɇ+3%1a%vŦì<`.z>`u^LlIBW >ABr]l^-B S:,e:YdcLݢ9=0=OydvH@$ >)J7OKo͠ypBfTΨB&dQף3*Rn2~ΨQeTG횬<##WE^m74eTUJX@Xs@;|iŘ):0q5 jfrp}s-ܑx ¡x;T*H 1sWPA/M_,S9HA3c_*ENPJ CnLЮXc3fLrjlnL-3%g{]h.`̫aflo0=)z %HᙻG1+neOΪC Xbi.É9lw7T(wTW^r-+d i0d.io~cauW-Bd1,4wo]^zi(晻I p*!AK/.%BO[. gg)}kL?NosV rש::ΈfCUpK)`W>0S\pP5G M<:/NXԫ!D49NJBHP9CTM3_ 1& ̯5-Q32>A!Wf6yU\@>l5lb1_W^ӫ5BP$ VF- VZ }*]#銡 eNW82񇫳4l!8i>OGr3`ºxQjX|ŦÂ{Tdy3":"rޠ%fʇX[B9w) K5墿VdP%zOS )⊼ .)g1n|:w;_p75K  @/[r Df` gpx "ȝ=3$=Phg'^<G yg7P\l?̘5nK0E38~ xXxF!/;{T@Yvuw`]c L*OLnL8"tǘTv sOlakCz.6*+ۦTX`Lst{]y^#ۼPZ<$0| z16X6bLq4"e s8m1B> Ni$%ev$>y$'xJ| `lCY$ kHJitX1%@*R~?/۰= endstream endobj 367 0 obj << /Length 2273 /Filter /FlateDecode >> stream xZ[s۶~[ Kz$vδ9;}H@KTUIɦ-.v?,Eg?\BFa\8&JdO.gч|u71˟) 61p&pF520~OUB:yZc*Fwmb}|_ٺJe֫HՑՁ2$:\&S۲hGI ML%;^훍?4f/<[l녳3MǑI# # Ĕik)` (THȖI"Z)"OĈB ;>mI+u,_ ''?1=zI8gu'*fA̝5Ԅh"MzIכ#}lٿ}(kEg &(HhLo>~ )֍I!>J~;\%I1FjPlVYwfF "*EO$>fCo '5x83HA : 2Cd'.Ev0,dH zoQh˘eE5j+ c̓ȯ e Q|{8:L#Oq,/c$z],-+]kMoҪX~ SdB`nhگXAzq%#J0lhCT۠j :3r5!J5<w%6F2"F?zo+6j k -(rg}.Q6Oq]jT =_aHZmú-julluvX#:>ɨ}H #5Ŝ~r^U&/啭Ѹ&.wsDA@?9xM&~k"BJrFF?Wc=?l |֙ [)>m >>*[<Ϋ\SoOhYPv]Oko봬=^!v2d2Rlɦ'L ʜ$ NP~Z ԓ$Ĩe|Vt% #QdնXƝ!j]KF{+PݏcHѺ(zlX~ĈC1v]@'"xmţA⌥N36H/AR8,0>| Z?f{X XD: q6D:P2D[c=`GOYY6~J(6\_)iM$S8H}_RCw)i;cZUjL1AY-U~ ga-PߜBՋ刓_-*߳ᮅ7Kp Gb4iJHRZ@2 }o¨D(G+]pȦxU,E6+N|v6| p]lKTN&͌-{6Qo,w 5V?g17<ABJ@܅qC /ϝ endstream endobj 377 0 obj << /Length 1925 /Filter /FlateDecode >> stream xZKs6WH͈(ޏ6!39'7Fd2Rr.KRx:/=Ed>û-)돍9&`~?ƈ1)ofJ#Rt ֭2bh$I؛pid+Q=!O loeu{ oIWߤD-Nϻ.fذfLe.z4@$$H(i;?(?,b/>ET DDEQ yGDyGDyKDyGID\QINJ.qۊZ̝{oh?Ebc:g7C2D08a~@sٺeNutK p4Ru{;U_I͐B ӫ7!W?/}K]T_oll%Nш3$y>/_i#t{(~7= iG c,b8i!:\`߶ #^#ٗS F{W&{'aMaM,UjUpZO}x#_ÐhOFT} )2NUio)}zT#\R .EVaJvo.geK@%%B3y)$F#2d$I ,n/Rwym2@ itgm18h3 E17!(z00x,BuH/`(7/yL~@U/'gY/w(rls٬7.U!|еI"8D̫'Fm< a2c)/ݭ0U6mӑ]͇al$p>!Ogb%#NJwu)Y4枒OMųM$bR= qN/^̛?KaUw\e1uF7{%|>'8 Ey4R1b2!RS>eӂN!ug7{ 0wq ZZ?iW-x}WО%r2!:PBխ} _\Q-4T:4}i :{#PW&Bo-jqPjatN1 endstream endobj 390 0 obj << /Length 1784 /Filter /FlateDecode >> stream xڽXK6-23|KDOۢa!AiY2$>d=,?0rHLRp87Mw-=DHEu@#8A$ RU%|,tFDY:N`rC+jf6a㒚sJs&P,;Đ?p6A]eN0_;fI>SÇN}5#q8Sbl0 vZMVg~WNeæ'H.+]qMUw^S>6>#8PWڧpn3܅0$rGC6ȿtvbw(So+dJpxm!Xpo+b$(+O+cLѫ[wwJf:Д/ܰ)0?#aY}*%E\ML> IE{*wj^'{ET0[_Lv|*Lt/\{}D6[۞v˺*nх?fn"?lH/~j!cW IݕM9bWYfѕ(6.V]6fS!3: %B4g\M)1qc`9TI/|^e!nl)11d6'_&vȪVq6]" o vt%LEYmr9tuP&_dt!SϯoS؁fssȠ+òCW*u֋qK~~du؅! Ih MJ()c] ̛QA >=mx a\$xX|KVcޙv@@/°O<>lrPSQ2+LzջJS>Iq'7#7~`8.-XQ߀۰MkA=l}߈(ɨm|^Cmզk&g8aAo,sSǹomݴfճqeԵ'}I+q:2&@pX as[,haL\eEaʥK *. 3pvَb)kT$z쌰p*d⑵@ڣD&\:PChwt_5@SYs??]wKw\t-x> U mS?q*O=SKrgP>GIʗ!#}!䨭x#%l=B\BF-D #3r\)+Ba>f1SRqd墼 ^gR/HNSѤuTB4cv"bаi 9>q,/O"5þ|ŘB &}'^yU4Ӫ`̡xxCc_In-,`#I! ޸b͖x'BqnO\58mxZDoa{۔l |i(fdSO#;}fpN)A[3`@XL1Ц`^M|&ֻn'.&: _xNXf[ m8dA|;H*ٙ v\HqW웦5@y6&^._SffPɃ6GÄj\t' endstream endobj 407 0 obj << /Length 1053 /Filter /FlateDecode >> stream x͖Mo8:JģMHClӶ2$y;PM"{8F3/#hr~RQNtNY4_Gq0?.j|h(TgBI5u0i.OI"RH)UBx6{[;4h &/ir)'ۃ91#L׺i1f6um0 \ nK8vU}GmpRTSD>ޚq=?Lm}0] P.+Scxc긲S|0ެ Qg`47g@21j-wC1ZЙIRYmệr?UFٚz,6a뿘Մ IxÚ͡v#8? RΆF)07R$ǍOh4<&v8|N4>*8@?V"[YceʙBxP׫9pKx PE7u($%\e[;LݐܜP*@!qq) %8C YYZ^ 0`E^HnLZط<-lsДJ-p6 N2BpFB IFZؑnPix#-l[m.|-v ia-w0ª9xD!-(ɔv? PexV,*Ocf n6P$)5)b"\3}"8GeI( > )o xKiض;Խ#.] `~nڨjgwoH0v+ jJ`Cm0-;0N9ve1˿oMG3s_n~IA>FȽ endstream endobj 418 0 obj << /Length 2217 /Filter /FlateDecode >> stream xڽY[s۶~ϯ#5Si_N3N&}eXB$ A,I΃MZ.v?,q/po^}3WŨ(n Y!DQ ]>l%yWb훠#$Jq)¼vG۟ ̹ciFAImLPDD;>i} q'cwx=ͮ>G7L;;pZl)8TΖpxEB 3,m x \T*b;(a3UHW3jYmpkegU`q Nq|HcxK9E\^~:]-V4߄4HuzZ!RØDGUReګ/A( q[ȝy1m$cu c3-J "ʩX딜­C=6],j6To(Mp̔ƮQ"(4!!Oc7xIr|,|hDZ`.[VW^=b߬XH} <]YB "IM)m`8lTGMXiM_vQ|aI]5-M{cI2ysؔ4O3`_q'tȝ &}3d,/ۧzux;kw{qKwEc\[5Ya(]Y*ddI(nv\IX{Y767#/|W=b@GzGD$VM f5'B@u쫃G! 0rffN.j =+ 1\:m+lS{Lyv%^uקt)Pl]ʉ@U"B^,/9{dRrC*P:rRbD"M{[5 X$|B)Y-eϗUm3S)hpڔm;c׺-b1V ǵKɼp$t }8trlylͭ iu.?SyGcM_RC}Z]-Tچ)n0iz?=k\{uñƸ8YvJ4N|w\2,W ٗXRO<;Sskï?%S_n 'hgZԒ!e=!inqlYf+dO@ĜDL/E7^ Ԅ@P4.s8E".ard@o5ȜL[X+ (u8Hx!j[DKQj mWj_9O2F6ʠ[d)VEErI 8f KD@p}uxo|-g$]Lm+n8s$]=EXvQӋBi&#%˚,YmDTYƴ3 !-z\f\ܝg>n7+$71E~uҚb3ZP* 6 2kb4lF.|r_V@7,0炜8KT!d) s0LOh|rQ9,Ӻ!d](?PV}1c{pk,>h끛;_7jέC`*& ۧS٠eExrd?E!MC~eس3.|O7E*$9OBtjj4':oE賏=r͊aїƂLRjlenU$} R: +y@eko}ֲR"D>k0s]';Fueg+8asU#d(uD SJK}t{:y$+Go/ĩ; Gw!륄"[&2Kꖛ8y=t ϷH/fƋ x?1ʶ몂0K=4;;߸ `G/Bq6jz(Pmf5gCG3:Tp~w~;Ðhڀ UH > 56~Y(Ў'~@ dn{d6Yhʇޮ=DuS}o3 h϶B|BD"տZD endstream endobj 430 0 obj << /Length 2526 /Filter /FlateDecode >> stream xZKϯQ*GXv|H\;Sr\.TGFLi1)y=ލL$?4g3<ͻ[!f ~3#NLI rZ1Y[xF02؄CԿ@t4nPz/9#9[2%b=gͯz}\C>b?n苎gK O)PX>Y]OX7CTa%KN0iuYaa^lo9yi`P2e*AJ?LeK*|_l7e@rڧVHBB%nNy0 sJ-%FU25@=l0EjDsxvP㯄n*⋁Ή  ,X u " h":k6'aЬؤ14+}bJ# RED5Eg8EE;|EѰj`IB&LRnQZ,U @iP()M2H=滽΃c6_a*~t-E]]^D7!q{ȄeDHr\++pOߖ͂yYVDJFGKJq?m1đt$?Nϋ%p wk[!6g=<,(m\Px5[Zx\8fC!a{IIrM JN@5Ĉ69  ^Sdh7iU`YOp& > Ue\3Xכal߮m@^˖߀ DзUQpm:ɢPqG4;&UL(x[(sIJM^W &c:oqXf2n@lbIߺ \KB(ҌD.D)RJ0HÒza«Om;beÿ>DAǫ9k[^\ 0>Z}F$gvCHkP% 'Hұ([%dUҗhبU8~˜sG8f]Nۑі~@8;l;aX;s,1@f3=va\2x*+I/7Rh7|Au 1wxP}CWs PeUo}(@!YE*!J^mmx͒:wDJ CoVQ42^7@TeHj## *NRcQU7!j䴷["j|Wo͆T u3B e>=P%MvCo&RqGKI\҇Dc] 7~d]t~,wYȡCbH^Tg<uE`/ VGg-``b@P (i4g1RVsG]jE)L-FK2%SKnnHs":%lTD,<4DV[ViS&(s:fweqEf U.mV[x&җ+ɰeaP%]O^}|2a\ު(#*CD25NhZNl#p@`뢏_l Fo$yw9}=d]z֙d`]Y> stream xڵYn[7+l7 -$Y5p X@=CumӖϙRkH4jŅq5*8K9 lk ,J ,8mk EI +FH-)- `VMEZUQ-XѼ,14oQ)v˸Aa3F򂭆6afp#qI w\ЍzDžC`ō:U &G)`X_xKRbF'`}\,i i0dS58:Z,_U) _=|<ٳLX>U nRp,f{> GGay캆zctb umȩ Gnpl|0*Wan*gһbί.!7겇O/_ÉЦEZbm1~~F'=&Iz˂[,^?:k|#?-_Pp0)5,(N7үa:`pƟ# L^c!+_ݺ2b m["vr!EpdF7 Kܢ b3hCXI1]J@sJcxDJUlIB@-l 7B~@A0kW`>\QP^ND4KɊ+s闫!eIEuPĆ*9jI'aYN7(\`QRa܄1T4*CXXl_1!l1Ǒ$L e(x&>5Xى$O$[^2qhXǰ0rxlLn>Â۾;x4YnY$9MsOڿL+*T-SCf5coi7-Gz(Gx8.1x 9ԆyVUZV Yϼ9Y m[Zz,ZڑEOŖ[jf@"}V!Y<Rb1{}+ ŭf?Rt*4֯6W"aA"6%^1HS/^{d-I›ibE>*$~*4ЍB*XKdm:fؾ %E#ÂE,gkO%BWm?%DPTcM,ߪcweb(baڒǰb}7fCnX 1_Ud]+md氦`AF)- 2 b? avCXAbRsdl7?k endstream endobj 447 0 obj << /Length 1085 /Filter /FlateDecode >> stream xWKH+|~?͌( ȘSn x(U8ZD8z5 )d&dQ4R Cx4Ibe6;n`dCx@BP'pF52J@Z #p0&c*rW뎮ѶG a}Ӳ-㗍k;߰^趸)ֈ/N(dw.D0V%-UV͕VH6H )V2_D\}~a]bSٝYU~_fS@qF.z"LS ݧ r7L.LO7i _ؼj_iPP΢0$j7yj_8˥ w3ۃ uuӋQW{IQ~T#U7*@&c.FȄC:o^f,^[3踮}"I8ׁo,~t>Ov264.^z>sQsnnY}s6kuU nwsQg.:EK.&h0L'mWE64{aZsSWlI RP>ƷWk#Pz=Le02{S\+{ьˬ EaA0ąpE9;͏[tJi#+~{-5a5u `7M7,o ='o  endstream endobj 467 0 obj << /Length 1491 /Filter /FlateDecode >> stream xYKs6WHT0ޏc=!4)AhSԐTS.=&!C@~mw{!"$Z=ETq)$6їc{MDį.K~]8"l6 DE79눀@!] jddi)&(2~QF7ї% *uRo&-G'p{oP{-) o5~xߟo0zA*$j7lC2[rHmF%@eqAq|vk0$K2e^-T~NiA >M}'.U[ݺ]җ4C2Ǣ ;kV?{ʴڧIݨ\LTjYmOJ08t%x_? }7_'g@Xgb`,p'uVѕjt]7,lƷY^v,c;\S:$-}u1R\ umC\gU9}a:B"@킬qJ K7aRB@n>&=\o@OF4.FA`t6{qB7I"tYNaS/Q8b\6d k5 }l$T% 0O-Da_qaΊy?mRg(o t0NHuw:߆kR=!1#\XMA\abBa?räȫh{ypMѳhKC'}ytYBDK7Z4R% AǫCm t![+Tt7MTVg? yk0y# nNfACS M+b[L|uR,X\sŅ)9"y ,9L% $L11 AGwj^f=y*I y}!CMR|ZK1 $GmlpЩ&UǺ0I=JӐ _"nҵqFși޼kŤd9/:b!SDXo_JjUu (@gԆ0i5pTcI'Qq INk.=g0Qic c5H)W=I:]JNj=2$eկ!=;ɭg&) R /[>ٞY Ϳn9* endstream endobj 484 0 obj << /Length 672 /Filter /FlateDecode >> stream xڽ]o0+|JߎbiBlli7.%s'eUF!YEıssBɂP'ՙRĀ5p#ArNf`$Y vq%O$(ZN` ht| ^T%0$eD`4d a#X\f (:ೕXk!4)'UlEӶKMwIPF!XJ()0\Up )d*~Ec2 rOx1Y\ΫUJ(HF}̑"W7=˳&y̋A,Mip} u܆xk` Z`BN9ԝ_'UNoɅ#ϒ'tqC;j֬#D%ݗŤH voa=G}67`o"xw\͐|Z;U5h*hBjbxt(ZCd> stream xڽWMs6WHT0?C҉v:O@QTH*gA)Swz Bb۷ N\_|w{qyuB1v(1ju)e;,TT,#Ŀ.~N9RZ'Kn&{El׭ɪlUu7 *Һq;W\i]󽛾"hG{E(DEfAtZ?_ `/Z $<~M GD"&ISgI6 |4OƺL;gĹmIFVmةϽfk܆0$-K?mFJ<|oGͳpUdKH!5,5Ȋ)$R!,K!A%&v,)O$qk 9a!zzw~֍*/zϤJOW"kۆ@MZc>|NQzTmΏz`ܹ~by 20OcF볌xpAC"kcw<,}_ l nPR'ɟ:Yh(Kjք(Zaq3sDb]s)y0{oyef( ȟarh Ϝ/LMm -U>6SЇ0 QoVM2!Ol^|;ݤ endstream endobj 501 0 obj << /Length 1187 /Filter /FlateDecode >> stream xXKo6Wh+zb(%G,zl6c%<{HypfD,\}_(d&!#Ni$A`2nF_y6&b@Z ;3\ A!gQ# "s_?]7!Lя//¬ydҲ+cMX[,5+ 7)oD>CAڧR$xN7`\ EG /17'#dF"8R08YϣbR.Fcd71DN3{EsڗjcS?a-^_ bO\S"Ϫվ`nJChbSdT2Ry9Ҭc4] a90ըOci"R$j$, î[ B̟"6yTTK1YF4NUq7(0pmhC@> stream x՗Ko@=TOvgc+=Tb#cTw`mcCpT= |3l8t> ;W}g` CCȌD̆%?{Buٸc8wAMJ#ĵPne *k(iIV@(MYAEs޽ylϒw6rwg/!C0.$_n+TU5 -u5W)%Ȑ*]u I'?|-iSp,Y"T; _u^qZeWE%#B Bs0d2CIhF4A١=dѽfGcg6_9-Q[UC}$Assx?z$C`5-oN3%r@ ~>%g;A<%cXۓ.Qj|]o1u48>Õ[!/ĄnD'D>] ֯;m)!`By 5׾ endstream endobj 541 0 obj << /Length 2335 /Filter /FlateDecode >> stream xZKБ`< hoeʖ+;>9p$IJD*$t70 ~`j⫷~|xcVJZʹ+ zح~~۵0Xlx++YS?M)y Dp+N|F'+ұi(ÒI,W___ꗍG/z-FUO=oϞG!B,֦crY&э- :f)%A6 !fM10 ᒆ*ܨ4 $Sf#$$*ls{w2Q&^.2]lѓQA|̷mQCcLTէS$b.3uǬ_YMnΉ?G ] >T-'uŧ:Oٳ: uT ZK%Kip=U*u2(/͎[E'OK.jV( N]f>LxmA͏k 6'WTUM "0QCW9ČKcTE&Y횐053O1];A">Ii)lx<>]-a@ ?ҔKzڊʝwDyx(8N)qoON@6b@ %Mx$ՇJ2\2kMc:na3~OWn*cfa5@l3 ,1.d U2" !_s:č6- tǻSRV0ƣQ<R䍆|IݛjZހ;@.0J06$\R@lag gǵ9J,Pec{:> T9GH80Hy{NCIC`G\esJrB ZR7 D{Ur*@Jݡdaυ#uNR7v^d#Ԍrt΄h/|lEzToz\/fx  r䪣[1r_'fb6g =%q,><C R vʳiWOȄeUNtX(@ @ޝzTkGu{c:CKౡm?}JslTP BVwPۻ :ns ![y  VÉBGX[v8~@)ODdPTڟ[SQONsҩ`o7#d>w&aW!':kt.ww&IBϰ-7663Ts,7 Zl !(9\GSM,ML(8ax(+w8GoG>#8? ]jU h݈6/iz-/Ǽ4Ph[_CKqK3oyV=O$çx_ų_w#p1 ׋-p 2;ԟ8'dw(+0eR]Jѩ99ãlǷx|][c^`brY1cM8ʴbU0qܰސف-~..QV>?RÁO# WA]v”ƧmhΗ\5Yv}UGBn%d%VT@lb~K~MMx|> stream xڽXKo7 бhER$hɡC"h[gk c9̎fSjTR+Uw,qS{px2*Idjj1kKO#u\J84*pi &*j $穲a)~DF Ƨ7NԠ`m:f5LRV`Ij 邇9ʗ؆:#1 `t.K @Y:1䈛ũ+X<gyCvA 𷖒DNJ+,Jb`@+~!o`Ίa+Am5+ W*$ S,ar0QW 1tV *C{zP5I]识{ {*0Y g?-U mbc_kMp˙ ^J0RwtjnexVtT$6D«ڋVg餂gIoegErŇV} KfWǻy::Jcuiu?KR#c C7JWWȃ\;W8xy:Iώy?7Sٞal) _l޽d18K +>^kO!!p͘ծScV3~ِ L’e֪eB\sªK1Hu8*\> r~AMbncR[XUr7){|f_oxAɲb;%_~[_\9_~k~;}9]T^\~zB˃s{ D[Xrc_J&,9"vBHv/w >m37nB*"їHl-kH:gW!6: )25lV95,+j1 WWڄhâj1hraQDr DcEYnWgqKK rr>ź2de_0 m p9~L{,6Hbw(U`-ƗG6gejEpg*$,&,N|˟}ڐ֨h[q??~:;V|bUٽ֑i3leO0lİe;\o$dg0ŢXmzL _jh8 Җ[\ÓN 7_Ng\`5,{~bٛUBKb T =ib*V8&)tUbPܫ҈ B,#c\j}3ZбɃ~Yz2Tυ~V#.4_nk{?CV K0Ԥɞaɮ1jXא. endstream endobj 555 0 obj << /Length 2361 /Filter /FlateDecode >> stream xYKo8>%Y``=uAD YH[ŇD)4S*V]}O?]UNLN$U1b\Wwm0~n?`]1J 5nDR \OԋYm%D( CdX+szx*u~xuY)%Ʉ# ;V@C-°ϩ)Rٔ]1{ËSi e]f]|CШyg'[V<ՖSEb-DIٕ) f\ADl@#kPRk+J2#nlt<.j~q㶱3ڶ}9оEa5vL!ݑg:!ɨ7r|OVľ :)7zܵ޵mwL3Ou̔(F s腈 IB>c Nk& ]a{-P%N} ѴQi2V"EC̄?h(O!S]Qe}$F-D$a'zyR82sN,7DOf'ըr.?2*fA(F£=yppȡmCtps(Rb=e[E&@rcMsvAmF*0nfo{^Bgt6,#2<x}ɶU: ֆSx -xn|nU,a#ԥʢo.p[FߟlpC8yN4`XDe66iR ?y9a+FHwTX`O0j6/KW}:1a̱4&@ mAΗHƔ ʚγCHd"rGq=fw28ɘb#on)Tsܮ6#Kj ($f^@QEW摉HпW,qI>uRX/fpn.5KSodgpndp7Oxu3aeLS⋖sK@RSL)n eǹ0ѻ ܖsYDFN O p|Qmy@Ʃse.xB0|p~t?܃-IA [YιR)lK]!>ѭ&i&`/:R>+ zxH.x(ʦaiu˭S%*B. \s2jo z**/RCj+t?WuZ U6ϾD?~qƇ\"0:6 t9m/i:r7օsY Hv悎=4Co'ڞਇ3t~:Wg(+q4}!vk_aI{_̜"dg:YLqF= پ0%QK?Ѫj;Nof}NhL^Q"N_u~[ H{22*IMx3w`Wp_"iFwk#?Ot*П?dZx*&C?uH /~Mg̊t񝰈nf5zې\M%e 1OyWe@ޮDs endstream endobj 567 0 obj << /Length 1022 /Filter /FlateDecode >> stream xWo8~_#c36~=mө]O>P⤖ٻ7` ВmUؘ>ޏ31c]2L+=rg+S#K IR saZ4oqv}ќ< nbϘƏqP!"TISq4BINL`Pæ77xǦI"S OASvfi#5?|MK.hH0yM3@'=)RL-L醨EjOCˀOŋÅP @2[R~)y.7-at6:bsn~̊v_Rnm4Sa$|_t&tjwa+UG!]kU0j:vHTxnU6FVQ 4-5N1.Zu a2OignnI.NUC3RNiymwO>!@NG(!%kIQ[LlImkDϭ÷ߎ̯I>8 endstream endobj 585 0 obj << /Length 1951 /Filter /FlateDecode >> stream xڽZKo8W=ifOi ePٴPVt0H7áF8F8콿"R(LIDGHI 8,F}"lԿpD02ظa! Pi\G#gQ#HK n Q[Me/q"MӗrG?QG W9Cg\oX`7͑hy!xDh0d6EZFfbuK3d]2oqbW;맽6{b194c(⬜W Ϡ<6N t%mY4Md5rck,3+hDTlT͂.4ZE 1 MFf iSb 0pahlЙM׮SgFTR9G<4\3HH^p[>b3:Wztۓ+A19 s iѷaAX#B b~C?, +uF폩E,s6OG]}rWL Z TYV6gm^`FrI\cS%vnRZ~Νt|Øtw6/qh@5CTFJ7{$d H0 ah޻@c<"F!츇ko}pTd" l|pHD*3` QƓ ")" #8ds !Oԝho\_؀D ryXvjFSʀk/fx5|p1? 6 ؆B~EtYPp|xx Q[ d ۋmo[$6KI2M )dC!)鑡!5ÖuWqv),!ހpx2 k᝘pq G)l`7Juf^>"%EF8>ዶ EĦ#nąBXvE#i_7E*MJYڌ/8Prv85&lk>`<(;= "w֠苙6%\ke0$Oe #+ߧ6P=EF@»=VXAa}jHpdjCbը 348m9W'aA!##QH߈lLW N츼;T.'jH$mvB4u;ʷ HaeW|.y5@]1ǂж QH=kVtqϏsDGi324n> CSӃGff 0mAT̯aZY:\=L9߳TyzL`7+J]iHqp|!-UElMUmu_= 'cXV?%"0Cƒz؋w8Y&8`4[Iؽ}U3A&+wHVbkCN'*!g*+;[? ?Bx>/*pubd8AFaaJލOغS}btA)\LZ}*dMr)]֓5؊SL endstream endobj 593 0 obj << /Length 1992 /Filter /FlateDecode >> stream xY[D~_as1$*. Uqϙq6kvPx<>s뻫7{nQRO Bxw+.ώpgix3=QCc(!5/P]a͏wW]; I[RAxw#Vg#*ՠy_B< ϒѨFp,g0> PPa$@8t.rq+ubo$>/įLJM\]uc/.ѫi2$.qALo]7׉D9WWno78SDXhl)F`z3~+wqs9|6d7^S/+x" *J>|oMm,ǯF|W6<#QCP$˸XD%a}$>up%#`j@z>:u3SHJ"PO.l&lH^>B"@ J=&!Qa3M>0RcڧrAk,WHSK!6kiDTfEo>S%@SyK jot#*Mp^lpS\|B@ Gzuӻq тb&2VG{shxգURezܪ\c_D%#̿&U#tCu4k0M=@90`D&/ad4fv@OZ\0A9 # NDo,T~Z۝S`UyUYbn6.5f_!.1B*9 ݯ:b>K*o>eٛmeMX ӶQonvmm^}[7y\æ;@MF PmC@%͌, sQHԾ:v_DRUx|լ?',tI%\Z Xtyg]DxS[7|1)!H0z-opVS d"S<A:vJ\pxjAAd$L(CN+>2u}qаh@X0@揉]Zs=/$ʑiOqD?7&Co$!&_|!I@lOjQ)J?rϵs"Fg+QSPۛMjzk=MAS #@sR4oxAnCUm/eTmc{l ѤC ~F<33@,`ӑ^୰B &83H T 3zy+P/#^R@o9 }RȎϴ &۽ZCMVRtoM߻SG*'edް==0M>lmKNABkeΨPƉ̇_F[< ۆai3g}YyjىuUuYۜ54ruPyY'W]EeƇ,*ǔ 2!F_?FYHM'Sab`O]ن_jmn_YdTTS:/]źd҄y1 xH\m%Yи^Sx0p!4iD<4@/o)AO'gKR–Hf t88#ԐY%L=vScKg`?L: ʼnͣ=?͸ ?V)@ǐD<au= endstream endobj 607 0 obj << /Length 1741 /Filter /FlateDecode >> stream xXKs6W7j&D~t7=y\@KT"URJ)Q8I]|dB$ IrLS(I!<[$қj0>7|/& c ql`R?j G3rPzgDL2&" H"!*?^_%21NmeT#x9""?j n]4[G+{FIFተ`"eOhkt ]wiۮ$m1. W;[*cIF`Ǹj SR,"D`"o Ce$Ucml JkE;hVѷj:]aʵ7bsuR^.:S! $t niB{|qg73)ҸCIݲ 0ʌkTG=\ шQRBjqH  ԩ wS'@`(K@ymކ׻ڶi|Z;7{S!j #d]ZH !yDR43un,DS<]Tۼ(C{:3&l PoE wY|7M!CN.0*#`Jpl[)* |SUO춫]um?AJ?r={_4M +Q]xll{ c˱cj:vjJEF>l~]-E$T)Hma7v O G I՘3 gpqưAP@Tw1+~ &;A(\^D1ߍXk;oX4y#e N4k@\j6 ӲWBeDzeFudJsspȒP/<P{XC o'vq3]{QNTeњ@ʻSy`0"멙ET`!g`:t~ N Lf,\`n_MF"9׈ea;xaa&8Up{1u@&s_m4^6p2$fvv\hxЭua^MX8ߢ xM ]`hʔeb9* U?' ./$c𼜲1DGwҴ%5HL<뤇@&]/ff.srR Lc]5vD)X%xLX ~Ď^>iYA(JƲۣ7 9jg$Sb2ȩQ.7 Ӕ)I& ]N a~ jyV~N'x< 3x1Uꨥn8>Ezc\pZO-vƐmTWCLs[>,M`nAkk5d~`F[=p 4}Kھ-BZ}Y| QL?9z,@pFq@ 6E$2 Y^5c)^z> stream xڽZs۸_GiBMv&I/K݋3Dl)RGR RH[CEM@O7(Ą7뀇H΃P3b nVȞsfO,]n~S4`jDRDn S8p%ׄ(Xn/nhw?mLHThe׋#2 1k!4SD*gew0m^suб//'&2EDI ġe ǀ%) [nW#Lm8"wGs Gۅjv5w_?҇dUr7_UYwߩcO:j]-્!l (jLbcF(~ΊreH o g.^⛻m7rOfz(vfɬ4a^cV]:ַMcKruꕵ[ )ޝ7>Tar:H_ir?afS|'3ӚDK̮>|;L`bDpsQRspV4*HBBIdziٵI"d0xHv{PHoFq4F8Y!h O:[6@a hN@CBz/TIُMǛ2}Lj];Lk2(%.(4\KNXXidq&^>o͗2ћdgH@EtB‹+_w2CquH7d2* IJS!m:Ci":zX!<.BM?=}EH 2g˭b* 2$jmMbh|Ǔ7ߟ,%_${ (`UGC*PwhE#}s;.-ӼN|cv#wncH7_#(XcWY4K$:-Wox u,$OOQD! g]eul_bDk)lIV#$`d67~GgOsfd̓߳.;_inXs8}\ "bA`P;Huj{LGjwBpRvTV^9(9S,jf(-jzsN7n7V^gdz/ʸ=P%|z2IdQLkYVo'Zhz..cHI9P8b!6f;,e4O|N@8s;"/,Vuh9q -w:]߱ :SHH=8|b}WFvd Nz~ [JVGe\*qJkMAq<|"c%(]Hۇ4P~|.+(\qwNXqleNR]IүU>w[Z"] C59C(aj!$HPnʵcS7Vp1"ihwej:O;Sy}tIwC{=a&YygZ*~ϰ;҆ !}kԜ}1DzQ^drMGvK)cci*/3꨷3 Dg@oS$ ^*rW77,46̟E"N4a&ЍmQA~aHj=A|F|!HYp7ol 7;@qػTRBR\:W+ViE rC'`\ hP]@j1Eǎ2kosw& endstream endobj 634 0 obj << /Length 629 /Filter /FlateDecode >> stream xՕK0 CZH=BH@"ȶcnۥRgqC6/"))0m O%T3j$ن> stream xXIoFW'S@4}iC-r(RR7J-4)PT!).ڃ .7og._ի7o(FRR]dJ"D&Aj]?l]_v%8Wgïͦ/PV ْ Ur%_]=B 9^æEUD1^ #w=w-d[uU4wf{,y8v,AI׸X`ӏ Fb)ʻHm"0?;H%1 "*4MVCB?,9@i@uBJ4HpDR'80@%UNĺm}wX.PJ4/q廿lDt a FLsUYmAk†H tZ +Eت6eR$l ,}XmMWxX#z4IfE?F*{ R1)R|,wUDh '#6ix0SL)Kc\0s:"Z N`k=&Q誵;g ] %Fr3L&TW=M.H| s5&=PU0qHE=\سCOulf/NMTE1CiE0>klgqvOWV endstream endobj 552 0 obj << /Type /ObjStm /N 100 /First 870 /Length 1296 /Filter /FlateDecode >> stream xWn[7+I0h im,T( va;@=#%u ]HW:K+1xBŬ'YLfakQ.m̰w7k[BxRZ @!jxl2Dja> K!!B*hݰGI'4^$l}n) dw$wx@r7rz hJp 9pŰ 66[a Ʌᗩqba1"b 1ܹuyQx4e`C$OmX@޴Hx{i=^+,46cEb:*}'f;A ## 2"XhaC& a `@ qp/ aJ8* 9pym$O-4q,:V%,Є_77*i…cֆlTqET| m7${A}XzT W¢%mP<J4)vTen5j=9k'SpɳZt|.DҹI"G\LaGf sP 8kל\ &Us&s\r!S:Mks6;t$U[: z'S "}n?Nm#o %Ru߮aAQSTS)G)tbJ'S~ '-Ņکm -Š3Ac fte4ql h깴Yu3* 0/fG;ZW S %B[B;W5lֶU,Qek%ڇRiXI9p7Aִʨu2VZFUf}cUw:h>2Y*dIW!y U1rP̛CrPUMSX =#|{,z|hp|B(O+?(?(ְ+sZ ={A.]1M_ wgo?w endstream endobj 661 0 obj << /Length 2791 /Filter /FlateDecode >> stream xˎ8_ؘ[{"HsJrm֎,I9"UB̿$+z B==ڷW @;{M,HS8f)O"E"PeoĄx\,*2p6ZG-*َ)$Jɤ*1,0ĀSjFw 2( hD"]^cATDT#RLBJ0 u^3Tl뗼,ţW>czucHZL͗ Fr*JK% 6RjFW>8>iq"a(d+_4Vāi:? 7W34Wlv^qKicQK!!e&L" OƖۙ4*ȍx44їZ):i=E  zWK/68$elOa3$ɸ|"|ԟp N)pte%]M~qgF[49V2Y#4NxXҘuh*iW]("g($aԣG(GlO%הl淰\5-@t T6ҡpv$MLTIQҫ\QujH[.BHt%FT*C-tRs@Y)GrwpKW¿y)πbH\nݼ5J&Ww} KB9hlAc$$w_ i A 7hQPb׸}F2H+Hi8 fnZ͗NR<S:I(eF3iM}k%zk+ٮdD{]fUCD# Rbeczv!ek`J1A?'>ɿT[5i 杔m~Z-m1篨M2oJ(3Nx86| 2UxUU>eC>Kr~P3kddPGqw IhD rwcN|w~ʐMIa. OUpH\sjˢԿBEv5 C59D]OSC˧UR~E"et.wxuP/BQAyl ãD"]w{c鞅L~JmMreUc,~Ǽɀ+ͪ> S4Nttpq{E7#4Ի -%nm_u5(yJsUvz\(x@|,?` @bHAFA&H. 6!f.kwL´VeQ7q5Lt ;dn/,O^eT=[hO{]`ɶI-mD{P|7ml^!(b":KXyVIeU=g=qiby!CuPJIoP{&u]V9򴥎Ud*MT|[ <|Xm꧂[3ʂ:m+}vy1T}YٖsC=STK'/GT4ͨJmd7~0XJ59i*줬 n2]]nt6eЀ;k_}c )hhe. !K endstream endobj 677 0 obj << /Length 471 /Filter /FlateDecode >> stream x}SMo0+|'Z)R/Zni@|DͿamh{of +rɨɀBx&dQ$)9~ꚏ*a*J~_ j M*8ѕ 9eNT/T F$Z!ISN9( W]~;E9e?*Ǝݛ}Ӑ<~Uhv )w+&)z4 W OP|nlndhPT, Y*҈ .s[Muƃc0Qpӭ!\Mk =*.َwcf1޾K!SR2 `yhC݀-fuSS]<C|cךj9KzoUոg.xFR&r"+- (@z.wپ_٭L88Xў1+do[/m5/j~ endstream endobj 685 0 obj << /Length 1426 /Filter /FlateDecode >> stream xXKs6WHIǴvL@ EjEʴ-ۥqptw󇇛w\E$"zF2A(1FE)q{ݮTXzXf *MG:j`m}Eg]Y:>!<܇sW9~V^CZU֡1tN@<_"lE1twhYYeJOB6PBP?$AGk>1oifߘ0Z7?ǩ#h^j0"AL~78cCF`JJ:>9ôn2Ae1(5v4%B;E=.JX|(1o\a:Umf6BLec^;`n 0'NrrcJ{eBx* z܀M 'h\K0"#aĮ/J@=ӿ"")) ^ eH{c%aoaL hWÜYvĺ {9[bpܴ"WxL8)|,qE+yJWżfmL'˪'~Eyd?Y W`o/oYkƘ'FRrT~& ܿ."W؎/.e#Γ:i5a[Ї^ځ endstream endobj 700 0 obj << /Length 1682 /Filter /FlateDecode >> stream xXKs6WHX$iibO2ij+''Z$HBQqxQiiܦ ,vow!L{o""L$G )ƒ8?Qi4FoÏ #XP 1oy(իD LX )B}M/qx~Ř.?t|tP7JԍŒ0Dz$s0 I:|*nl&1b1;@ b kdY;j>hzlCLF~FD9Ky8֘R1~µE,',SB,E .,\S"yKZ,eR%J܁(_VIZ$,hB](ĉ8A:=P *}x˂Ɉ&5?*A`SگYiEԃj&n"0 AnF~z.ia%"IK ՁþU÷O5.pz/PNpfqB9fK*Vbi:ZRoyH!x10hx9ssBg`9z!mw>fD #ְݔ3qYր;ãB^l:+wP+M'y; )'#ipk7(Y;bRné9SprATh )`}2Ѭ\7zXx*++Ęa1jOfИ^rtCȨϐ PbՒ .'JDD`3͆6ң~q{dTHpO.{}|[a3ofIQ]4& ~ݩTi ap\ɟ/0rO`` cV(/T=Rʎq}|ea֎ Ζ14X+Ŝ H܇ Sg1L01Hޱ {)q0~ʵvATq (JaJa y^8`;FZ<ʢ,f(ǒ`I>ՑlI Lj,0L2>Y 4f-fԁt׿ <Xr^g@p:͖Bp/um+(B^ab.\A:U6c%9j#ëAAS+Ҫ n7z 3mr0;X]Tk"o(kbDZv쪹 O}o~."[Hz -9E_} endstream endobj 714 0 obj << /Length 1853 /Filter /FlateDecode >> stream xڽZ]6}WxRYT}ji)m_f7OdC<v},<$F蜫+/8G8^ ~]H!0fUqJ#% ҄Gw>~S.'C"" ?pD0Xi!5'0i [&q:1)_7QvV]$E~!?L5#U]9l]glHpʳ$AFXf!?AfHZLPYc?M!45ego)cnD6G#*_.kCAÑ"~ަ70YHwC!bc kZ*tƒr,˦Xζfb h̆U=sΥ?b>vUYi7efbaˋ9; T~0;A!D8jU-p~D0jֲMӁ,M|"Ժv~vL3C4 T`"I2"иɎ-Kk ³m1eL#PPB a66۪FbYΙEEqRVU^v>ڔ5Ԣvm|ONhoWi. h .ԨzvviYY#FEu>*/ 3Mi<\o*6S@alagejf٣X.m٤\un>k~ym` yM̮ U iҖ4I4_^ɂZ@5F&;dsT# )-N7@OC&;OB=8QB&@(7Q)3 53Y;5uE zc_  LDC'M;&$zG.#[DmUTڢG#$ fW"Wf*sj b Q$ ,8xC x҂U3o AD\/!@G\ B]6(.9qӦÚox˦+7֙9}̠RJ_M(!K:E~qjGzC91 # I Nd^cr>pg쾏R^+ag,/i+\[-/rSe+E,8]ξ|U2c ЃC%2X&VIw X|8JxOLqv ! Å`๹Wh= !s?x(?4i\):}.ѸAt:# /IWb]^no5C8&PR3`ЧD_5QˇCHD%cD#gw8R7 ;KMpRp]3C )JyJf~HsDE'n?կns ʇ[`>2  ;DO8AIrO\qoLjÀ`| X? 01 xUq{D^T]bqS+Ky Ο>b.}hx-+{)HB؛rTnKD0"y~mEVg; v@pA7 Ƃ K~k3h endstream endobj 725 0 obj << /Length 2969 /Filter /FlateDecode >> stream xڽZo_uOQE нvCryPl*KlrHIegMA &g8?9ή/~gfMaƵ$N1LnV|^^25^WEgC NHj tA=~I"dpB4Q8If=~.t/Wtv}Q|ݦ^pռ;\=xA$,ERFK]'h ˂RCC=Ic&ʈ) 1* A3xh3H& sSDIӦXnxZ+߃m/DapՃ)): #B3Xydm|cʶye|UTr]7Eقa(-$(i10gKżhLʻUNnr O}~|V1aEFR'㚑As a+P=h񤒁3n$ mWTǢ ֞z[.l#̐|y#l1MZvs:leeXl+?JyUt$nc&t*1NfkQu AcyMf׃]5*j߶l%jm'kc˦:Zt"';ivWW+1;K7hl\&kVv5>.+*&20?ٿXOb&2'nf22 7:\WuG.''W4x/,S^fveqk|`bBH Cڨ{M^xЏCUK V)d` HAro&i"+صLloqTxG5aG+05˫ֿ%:X>~Kֻ!a 'x &!D.gĬM{2Q3d, fR-ӯ rM&xXn0ZĔj&L MuUs˺ņѳQ)IS~V}rB2V@)G-~#E&$:;X).3X6}޾jXq2;sհ+(whdh6\KZQ0~%qgɁMՍ^'ǬE#$B ,q*n`FϓFу1Wc!{6Cz n[SHYl||2:7oL:_C/y_,Ɗdj~jመTvA*cS8KƂtMk7H3eS8ӇLf7_|FrAW <4#|v٘Rn; 鱀- K_FPYH{U`e]n/ Ou9-”S2XK5dɽM?F>/uŇ_X z5 Qۥ4{@NgOnvƌ&Jy췋E"pe II?vW懜g%5k|sqbRs@@UYBX%ORޓ2Hdv|P1p:(Gw@\_Ǐ_Mj:.}W޹غCw@b9ڜA82 8{ݶrGJk],2ǾڇsymݡC!0Gij%f]٭]R ?0x~|6:w6nX箾B2cAA_!)LԐ7H+ך=خ՜_EM/!Zb gV^Xm>?b3](oYONtoV·p'N}LIq@'SnGEzwuB^p2&84Ì*fIig/PB9o#q36\I.m5•${mҿ"p?M endstream endobj 736 0 obj << /Length 2352 /Filter /FlateDecode >> stream x[[o6~ϯ/`sx3E'vҗ)(o+ɓNb=5}$9s!hիBD It;S)I!<}F?染k"Fy\o`d186 Խta^:" Pj{O8#M@Z B1QϢ_n ޯEL[t!g 3&!j,N>דHA ?O}!HsE@r} C̲*Aw')PPwi^AjdXoʴOC!@7eNdBJ7 42K*EiȊΑI}5D4=`(4OwY'k2Wuh5RL{*՘'Vѧ,$PL"^n@d fLm(tTvA:=v9 z-Kʎ؏Ylq w۫J*f4Y&Cp6a N˻4H@NeʷV_첝|ocٕvbG1/`/縘p)l>zEEz#ҥ)yM%jd&7̠x'n&GyZnEQP@0`M"҅Q~Kρ4oZJt.e-/ԑŕyq:[κDA℺M^\u2:B-}eegYCVkcAr$bnnse߁nP!D$ EP%{=o?Bf0ڳpyA(L?ZVd}c+lYm=βbu׍roBmq*W<'[\a޹MFi^;*lc߭5( _yCJNe^:w7?yfk7ñ7.!m+۝R'C5()&RfLQX<]+~m q0e82R QzBU6|` Gmo]Cs1\(ºyj}}07 \(|ѕ8%SB B\Le8LPr~p6],]"DS $0*Mz  )OѨqZ N`e xki\V+oi[oT+ڞqZ]p>zV4)[q:Z>,!Y|7+Jzܔ[* 絽! gC;JhԪf7?ҫ]gUWCԠ2ZRuqlxllw:!+]!h06hۑNB^|76 *0xix66KAtCuor@\Z7HUSucO8?Ir>~.o;ˁ۴2c )N>֐84߶hc蘍`UQB)0l)Nے B# 䫪y\ClN !p neYif8JʅihI$ȨPa4T)hjJj}h'E$VOL^ڇH ˰ +arY M0MR ~|d,K !ppNpFo J,Q'Y7j!VJX=]ȣF>GU-|,z@ Z;RН/({PsL< K3|pD:n/eMRnONr뭩/|ڤ*z2DSǍ{][{M#2c Cn3҃+/8(}cp"4%Y B9u0qI,Y=CSAZsc=CD- srP endstream endobj 749 0 obj << /Length 1644 /Filter /FlateDecode >> stream xXKs6WHX0^|MOm&N餝FIIH꺿 ,H2X&v}| m#aqqu$QFhx&,~]4bmBI j7,յ#&)7R0‹4i2"pQ5"z7gUyBWٻ(5v^LUW=9Zx͹$z6\M՚U%NR5)v~݂ۛjuQrG|vnbv[4+?;514a$tӨuUoQ2UvGQUc{n綍>Z7I A}*{͌'nzEbvj? q>WErc1uWS v{{NhΆߐf$Y d²x;Es7Ϲ%%3"x>m٩3E`4! "S QBB8u(RBPwF(_ܨVsͱv`i5Nd;+{Y8{hQBK{ uџ`0i9@ ?Bքb; t*ϥ;Uk<@yr =~P\l.3jĆSw'7 Ģ JlҞٌ$B!Exme^)|º x fJX) :J:85 #`"E( U$Fe"Iqf` umm m^"Xcx04LgYZKnYVv!,+vnTKK #ܓDc&,ʣko>(N({J d$a#N0޺;(Cp}^!xkB׹Of'qjt-mN`x@Dq݃0[eö#3$SI@@51Oֲ'B&]O}8'(}*'ȋ +H OYʢ?W 7 'ـgO|y^:缙 _~?6<W(XKC?15T>oT+㭪)fPOOvJo ,j.yfLftgqf幮{F{99KC]8D\mmĝܡA<1E]M {ⱙ_> qD+<$ +" ;e7D&Grً)=CS'A'?8\I@W zsBkTqO!`V4~U'(G }vUI`∇u.HdZU]u~u=3o(J[cR3de~9sOy #7)ɬ<8脒 qjE@3F:vfrU֜ijvwCZ6, ƘV%+Y݋Y!ikO_^xȐ\3wCGqq:?z} endstream endobj 657 0 obj << /Type /ObjStm /N 100 /First 876 /Length 1665 /Filter /FlateDecode >> stream xڽY[o\5~_GxxƖJP@>Qt"J%T=ߜMh69I>{>?cӞJZ*Jp"_%Ϛښ%)$H**0߁Fh%5xS;ƛ^%*fiK"ZtB?CI`+ [Jс?Y\|YZZ}y:f(/X>HNX w#K[,h˴nj`V7?u(i\2mL뮹xtdCXu [elBZhо1ق?sع nQY;ۆ_{̡(^ȝUVDegsG^N@難RἩ{s5Jy\M}/`k$1l>EE*!=k¢pQa(wD:&+6ibSc1_]k6lL< y._JB_j0y5l{-39ϙH ~}1mLn"2U:3 8 a%rC1,ҹv?E ʥͤWى?Ǜ}gAoކ{R+~6Wa;(1lEV|+nrپ6)_: L ZHN3UIqRrZhXǹV>@!.(J1B,`8!+lE2a31hT-v̍Wv$g$ـۊNZC;'n/~M;7sM6]oZ=oyk=(5ŮeQk3]Ҡ GT- ޑ"JCqH)Se_ii`S$K~-hʠ9N|خ#si0rp!=㫝eOgOkLF.h݂ޑi簄r35$2Ֆt[YAl,k 簊]ÿ0D endstream endobj 765 0 obj << /Length 1913 /Filter /FlateDecode >> stream xYKs6WHUYX<حkr&V%sC In4HC<ԨT īÇ̽ǽo/dqȅwd bTt!H<]|fqOp)49nofRF[/2)MRKz"fe Zao2o3Z>UmhLB-qSW-RInv}-768FfR]mU/xW`6>v !ՄtX8#@ZxK1Z˴+@m^f Kӏ"@FDuz߀yr?_ӺΐM۸I:ku\#NI^89o2@ ~.N#&$}AX[U ~%l>[Gߜ&f rg̋DRh0 SvGR fM  ^Woa8<K9mR/bb2NDvlpDκiF.hɋ 6 ?*.?uaA;G~ M'xҶu߷Y7LǎQo 6Q 79f4ԘP/?}݊Mf G"ݥ;֢4vw4VD:Н#/W@ptV^;ǹjQ5d>Az}alrV*戕eu{BJ>rȄ#ʀPK|qŸ8CFАZnזUnUEȟ9}_$M5WH:{mGVeq16)DR-7 '5@Y[8Du"P5/ipBՉCG0Vqt$O>LT(뒁)? ϟ3*MTLgk5>@i`Rl:oB kqM7GKG>(Je+JTK GA6L%*(f|Yp*F>B* $G'S>Yl8Ƈ3L|ELPlD&0 B^  gi#褾һt>S<#IXp Bxx&ÌU"zx(FBWDA.OِLQ5d  f{= \@`r3̂ u?\FuRic ^vT, dd|6 wҠ{t?\sb ݡ c )mݍNW{w{R}/ (xxHŞ P_T z5TpTs DEB`B6lAC"1Vu6;fu|~œ4\c2{6:7&:z-a&.w꤀ 7|]L5ZrR_BƅV,PïQ}exp-)|o`L.C$  f9uk&̵hCj8πܕ endstream endobj 779 0 obj << /Length 2048 /Filter /FlateDecode >> stream xYr}W𑪲H*Ixgkv8O^o-2THj=4.@Jl\6O7A-#]]QtItф#NiH4}tRvn)4181.ſ/t[Rb}qs{#UdWjP[E_/IT]5W J.6eUn7"~ʛG+ٜYU ֪SQ''84$p2AF0mQ:Жs+0эȧ,>VY+ ?zA/i6z7'bMA8h0Ey<:{ӷj$p %y7ҺaK^,xB*GǸ$3q\5]]dfKJ^e6)фH<;9l~=jdF -9т#Ft]'k;4'(4ڡNE+!K"C3 #C SHHpf82ZV3b8&8 *p\>W+7ʋŽo:]AݓV)5 #n UfB'ߖf~- ҵYUkĿ6ivn5Z3섑Tdڡk7J9ajl" wR'qJ]~ {<|qϏ0y<24 8^T%A<ղ ׵{b fV]^,73;90ԻU]^yuA; K1'G߮E :MUG*lB hF 2LAdBb #>٘ ?.Z lmBm&3ʘBI HIZ1 ěv7엚{q ^w0Gpɪ@}Y3 @8ٜ%b;fF6uaؚ9z 40>fKzԛ IFEN|lؔO&Ery|j(JDz:P \*_71>h$͆5g ċw2nl'Gfweϭ%Nwd~EX^6]\g#xx(]c=ƔW'=ufP'g0ʃ&#7WP)dH|t}.gOVFMisatۿYX oWNxL!9 VF ܜqYvn-[EY"x3!l~:Q#9e <'#+#@~-fdb!3GP:}~E A0(EgX1fW(F`*!jCx_#vb)2!L'p_nC8 El_SX_PN 3 *'KڔMV4wUCi(( u}+8S}f BjyƂ2BRUY nC( *1kL!B8oW%ᚘ(ZN]]_o{<͘@"*޺h1v\!AɋeewwrqA|GK:hipvh} Q:I2(`EQnDHl9EN(2VtqC`; F ߳=WAbF,<:{Q0^v: dZݪPȪM%W?OY[.+(eL&6e5i/4wD8vI4-7 Vn?|Kכg#FDP_q #зxE8B_OT᫹//UWѳ}͚B e.Pf?9L^/ݲAא C}PxV ̑B+|w՜]!y[ Zw.<`<12ym4[ endstream endobj 794 0 obj << /Length 1552 /Filter /FlateDecode >> stream xZKs6WHXiędNӃ-A2[ZTIʏJ(K>dBx~a#||<;"0 Χ8$HO,}i2^Y8 i4&pj &́Q`KJ4 ..q0߾1j Fyi}# |;ywxh%. +*ؓJvg^5^Z #AExO?/IQ4.fpDBSyɭq0wVQHp ܺC^zз@x}@F|b>GYq8Jzc ԰A}\а pt+` +iCisN7^l{'̽1 %Crz#(UouaIu$6Kht){[u\`xg-25j3BQ$_jWU3*w,ϖS))ERckPuŦ(y!vݽy.ؔ|)Q!vNr y(D[,-Q ̢oNǨ spq/|a.qdc^%-r$DŽK2!%v*Zb&* @^/FY^EHnܮs/ա~nvp.qD~3(QsŮUqdθ"=m2:h[E'|p?=`}Oֽ'|Ha"6k#LEk,th8bXfE4zƔ[qe:p ͍ FOH37i\Nt[Ac%x)ׂ[;Kc3]vuH¡7RؐVoH+(~JƘ\xniXSd`V62+^.AÊl&*2%3HSwL&1F|@(5ߚޕ&AjL[zܶnƕd E1U=4 Da㖛rϋ&6 * 'Ǔ^a1.]+)N6H*3S!$]d%|,ӧH2p+aG -/o'ΒyRmg! +m/!E\_@a$0Tˇptk5xJwz`ӧᮮ~/\)jkGmIXq\N^#Ry] aYW +A7j.pdvجOSI 4]s :a!x% r>m7uzz"MOQ# endstream endobj 809 0 obj << /Length 1932 /Filter /FlateDecode >> stream xZ[sF~WQ̘/3Ixi7v^AAAY $0j{|ߞ+{zoB Ip7 S(I!<r4>ɨ؅mc ql@h3c F&mRH"!`u/qxӧ8\/a.駥 o9C7,pv{%).͟ $*W )߻(MtolQ,r0S0藋Xvn0iiMmrp:*NM6b~r~It[;>&iYJ"$6zhڏfNj6&\#FG0TN(I\,{LYңC" x@8>a4ʖk_EV=Z-hΕd WW,NOy!x?ѳ Ӂ>GKKysa]89t.r|\0-ƅFWI1cb5. fQpWZ%;ĻFIEę<`JrI@ŞVDr&U"TJ nҸ 2 %ԷBmZ/$9ݯ6v6;Xp6/Y^$i5BrH%M1ә|Fရ)ƣe:vtjFr mv"y|L2$i ȵܦdNpnG ܑvvB!r=8h_St8^CuG+\e4dgVvgD[3 pFW\8T+:xW.b@ov-"pWJBD.߾GND UI5"J%4Z =b+h'>]7l@"_LUpxU-{SO~dwyUw2O{k }I[.0[Xɡ2^5⚽ԋ-4O1yjk"\2 Oeha1z0֧9_ W2J5A " mmXsu{,85i5)XN"E-{/K$""Mi}FDoΈN?)|ҭrZPpzס+u)[Cuu#e]y[ATtt"͏ݹ,]_o Su'df&t6R:l*FRR3pS?Z#A_.Hd1# >G-RB8` ,LpO#Bd˯Ul-Ϡ5 56ň߶gNRHW6Q!\! "D꒭M{ ]!Bi'HbrqƦu@MiGC\iv9QHq rJ}qƦuP_(ѥcq/شW<b6 endstream endobj 820 0 obj << /Length 1940 /Filter /FlateDecode >> stream xZKs6WpjB~&dI>#qF"U.D%ۃ%,v?.v`իB Ip-#Ni$An/x@D}*مj6Ȓ"NZҁF] m|lw|c LL#F1Ln+U\J`k&h~oyT$6.;&Ͷ3"mPFJɋSĤCpR;G1+ | B*MLu>iZ1ٛU!c6xh*ïQ:!8\,÷A]V)r##T#0[/W[YnN9$F 9rboyfz c$-l*\u:.,cgxqXxFgCR#VE̓f+pK?=hˁCIMx7KJ@!a`*>!iHCYXI%3Fe=A4(bbV*%ܼdMQ9$|Py\4\7U>/xy\Ġ J݇ѦTWǿqQ tm cGsiCԴQ 1؂ )LEpTW8{rwg/2[~Dh^{ln'KŠҜr+YdD"L$$ ;7q)\K2'eHrqo}wpb"}+$Ŧ>Nr3:-KRy!b*|aAnڶ>r]'cXs!8blL3*& >IӾN8(ể|CvtKw4ڀJ uK"Hz/ʆ!D{6B"*PafqaQ41gLm$8GRK.Q(b f{Tk.ag[euuoIq@nH_~N&Y [(]Amj! eor9Y:'=")^;:~J,v =en5 Ez*~Σܒ̡/Q9t7vCS LnWz~hTPJއMR`I`pp}Ͷ# ؁yY`eiUgMF:CFk(1Ad'\6Niw8*!:oX]L"iL/:sgWGʡʓ(Ո]0:V M:CF6e1"Sb)tCCO;_b&QPBhUݒw$tͥPCI30uX>*<ןqb U\ endstream endobj 830 0 obj << /Length 2607 /Filter /FlateDecode >> stream xڽ[]s۸}Ա|؝ݙ6ngOuYB$(' )ɐ=dpq.Y=޼y/DVD_"8FJ c>1>Y$+4g!5->Vh GLh(kaҖ불BIoꮫ9ƳqX7-ͲtE5d@U`P#Ru`0_9$>5jfK#,׻lJsn\ʹ{ԸXt!lZO޻*^E!S_N#O]8L0Th]MM;z5qKu8:U,|TR?&]z.CQ`js#L85[HjGy.~bI~x%7TE C10&˛8~0b02_KdhM?ˍ+ -,7],anxm\/LeF "Rt}U/ Qcݲ H ӄ\b;Rq|&pi#x]`6E:@`& Sn׿mRHPSxK-)l 'E+.9#^A TkMbLFRl.ʙG.P=.[Jd}/o˲CsUnkkqL@D'{k ]4 Ƣ 'C#G<"룬 MCzP85͕a2fZ  4r0 AB^]]pO``@Xͭ`,Zp )pKhd|&pЦu I~%@MIdpQh)%"IlW½nw\سB1=AY3w={D?֧~WrNI/\V1t\˳9•Vێ3,y wq vxzG yp.ID8 l$z׻uL!<,q{F~V&޼HYE˓!fYL.er_w$5ɪń:JM]y%C*0Z-W V0̙ɷl6')_tYUqaqY4VtHN&,kKɈĬKN2.gЛW"QAKZ_R@% a1zp=P+#s zL|jvd]WF"fG"d?;ML27 UrxDj_ iFHRa$P:+rH14l.1l:baū>d_r:fߔԪgT15Y2a_}*odth}n)r3\lz`ؤ{Ick㸱M_Lօ[BvghA"y~FӭPiKB Lx'b$XWMm/<-(k[ƫYMv`sX6/w8Xiw2eȿY%Ǵ=,")x?_~d2ugyޡ6&#EUX i|v~,hn1?ǧy$AOđf3c}A /h_ R N_ m}+ĵF~=Vlj.|b?> stream x[Ks8W(UE w*[57Sl웓-w E9ƃhJN{I`@wc^ DP0 n8J΂y:&b<]?+"CG |nt]7?0 P[O8#F20B)Gb;_~ &cƨȹUh* QBЧήMGg F;qYVPD׸8]%3tڳqάf;}Q|_RSDáϵԀi*T(J<D4zLǴxd@W'97Y*$oڤE/HYxI:`"*`o v\gk[a6 =#_ 0kԻ”\sJ~kṅ6=JM؃=mR[|Fx%џIeVm!{yİ[L/Eeܛ$E5y+yՓWzW Z.`i U }y/gxv4RinneB..V|R-0k{(bt0FīY+`iz6 ! {Wj+ 4 &!Ua0]^} ~ˀDm"G.2 bCh=3J)]{+P_D:f>&E$ { `p=*ND 륽#jm1!oRqGt\@y=6E>O7tedًN_K]dK{ԄZ {H8 a\+W9n&.H[g3Km;}#j׭5)_b׀&|2l)9m[Y1l+ ZG 5q0IA&8RBLWYM5aY}.CA FTZI&:C; t ۂ&D Xzɬ5 ҦYTӧdlP EqlC ʇ#(byKpw-C{yV4xOkDc8Lix{q¸ˆa{켸jK|i"d+x+1&JC3D(4" oycGG8-·J708&{f45H4uQ)G+ M6b B>K6/NRĂ 2d!L' FV$xGX5 q> tŊ-A][1⼵tE< NB/Zo!]g7wO~.!c3Oˋ ϰ9J ~~0BFD\QRdߘ^`vhU"MVG6&5[:*_ind_l3ȡ8~LoU#J>0dRlE j'p1*QIaHZƬAtWNf,11[8[o3"487N= R9 ENq~E]*KE'6  tQ^wΩ1K4O̓Ls9 }9+ԚOb{)~wNqcBC}J K: Ը)tnⴅaдVv 5GدAb.+_Fꆝ/否f~TA&BX@Y ԜM~4k3RaߝO0 7n/k'LۋIQS?G : FmÚ Qg\ɻ^^ "Aғ 't݈$bs3DJGLRDEϮ9'mpS2);&䰲ul41"#Ň9Б.׺i8 }/5"@43;|~h qjB KAq$03+tӮ!M(OK:@0GaHygpH"Ol endstream endobj 848 0 obj << /Length 2623 /Filter /FlateDecode >> stream x[mo_f1/-zn~ZGu$ΐ,Y9~Dp8p8H"՟ﯾAg(K$H"9Of1?&r>x QH@@tE4W\1h3 SrN#Mw %A Po|{PVY kB S?LgBo•Ǜί<ފ+^$\PΊY$;Aj"(nKE0I^\[s/ol ß"sZ䷿UY,ev^>_矩᡻p8 qh R儉.ӌ_/TB-fj@.t0u ׂ̾`n F-p/oSr+T`-+lht5PY@%٢,^a_Sh#4+7- Λv?+CsХ5dtXyyk ?{BMKV-Wڂpm+; $ [b8,^ؕhqPBE%a Rr©v6ka;]fm =NNwXܷ?8N i2p#!ɴ=&4RM}$% Mwbd%e5UU.?O9TYXcty&V $hq'̋5< 3ȋ|HQS4+0 =o.k1YwS8@lCظ&5#tf|Ȳ0[zi,_@cdC#IVC: s*Z*:&^3m&KFOe/ACƃAaбL[hۣ}ЎIaB-PWCW"aWv?exI oq(Y."jXidhVn,1 ] S@:_d^?DZw.ew_"N:I1;a;6P>( jڙ9pzc%x@,WhiP<`^ҭm-xomo3`< HӇK8ck̇04L9j1!xѭqPiAT/ aj}:i vT:mS(]GBfݖ xS ԫA#i]AnFNF >eDm$Db)$ G}I&7iz[^eg:a^(sǘéDJ@Fo$f?WJw]z˧VPTÃ+z:hqRʍ[LeieyGKZRQ׌ zzGU7YB{AkZFk_,CL\ bN*}vQ#ݓch.{hR}ՇxkW|J@$̪2GPJ&<= ~˴1 ΊIHGf@+,L[ O(!5+"$w4o>&cF0z(K*-IV{Dz[c*(B!HаLE4 B jQt)cCG( P2&ǘ?kA&e4>ajhoG0j{@]'45Cuȃffgb 3ōڱ.pKC7.a|ÍK~žnD4 p߈ƎBΜKV-LMZmb`u}YCcsO<&Aoc24tJxUD{n]m{;ǖI=~<`N1}8ӡ& b'~iůOL2$ZD6E \Y(:,#7o}9jr05L_t߶^ VN%}>Xߗ m^!n·ѶWA Y`:UW@mJƐp-/5)U'c-]W h-rO[nA`֙Ly^piV F Nm& |͚™9 YLiCz7Em.b ,< õ|; o˛/hEWYu|?zPFac?_ endstream endobj 859 0 obj << /Length 2072 /Filter /FlateDecode >> stream x[ے6}V^~ً񔽻mvREK $J88_Jȑ >LFt2?g7߽"R(LcDGHI l6 *OfaF71ı엹zC s@2R qIG }3:A Po})UdTU H3$ܸd*h-/Fwl1z]N 2Ҁ(.F] mb;~΀D kb~}WͫT 0w4IāILvj4cۿt7)ۭ=^H8qp%9\M_ۥM[ ƒкO׍!W]ݵ))/6|]INGAOiDv腘_mHV1?)% 5wmWZm|<5q,/.Õ#;NdPazGO䵀Ksq2z@^W21xr^Azr i:zN` '"GFtjTLFDjߓvvWf "͐ q X W9Mz%.oV%.IK{ Yc;vUWHLq&s+d};!<.#u-BDgyZ$tS^|x߅@g}2~OE#CEW8$2 Rq6> A!עͼ!H}dACTY%rlCE 8ǡMiyoe+1rpepجr}]~B6Zsc.s3X MjE1HҵL"oEh~|, VQXjڅ=@>־vVW/1g}[(;Ґw+$(~x| ƥ dq&Ĵ WAWW*1!inHL4 ƥ -3DR2_.߸=m@PjlL[ca;iᅟ ]P^ dji敾` RS(D=T Tk̪7cVrvyށ;`;@y%Q#ՃIVyf_:%. d! _\ I 1!W""'M] qM1l!0n_I*^Gqq_$c):c͠z1ejv)],l SZMH]pK*pDRK E|^B\eUZf*c|.r["*ج]Ua uU"x3*HOm=?uU)W_h2TiJdy9Bʫx!+21=/A=,> rFL ,՜VI4i^+QCzʱZ'g&.[]ihӣ ۾]Mz|: ~t:AG}pk:{kN^ƍ]G; rzrtCpږdD=p> stream xXKo6WV)" Pkt he&jS^IC9<'wW?.oH dP4Q MxX&jR̈H_uY>-~]8!i6 \;+\,!PDLLL @'ݯ~J9Y]4Mm]^H]s-7 q]fi׷Z ɜˆ+ N&v>h4)7P+8pȥ#JDnJ@}lPt@whv;u4C*A?Ϣ6 &E4E`h\e|' `{lC4 :GF#A8hFLI(#QejJiJjA4z7y,be۟j2B:wx}A&TgWF)(4Š^!tFջ)ᕊ}qE.`e *M[5(.U:#D3CDw<}xH%iX c.JB:;g ˀb}s8L zl9,C*;AY J&apq jM. 9Mtiڼ\7a^Z!pmIoq6:paS%Z$r zc:ȫvH%:A y>Pr^+'PHiJ@&DC&L/@﫲5+/:o2WZ;EC/+C޴fBqrg+nu c$`!/߿rvZ]Ao:2BJz5cEEy2deXv3pgDq)n3~.SoJk4 w;);,.gW X 3#ANc PNM*rN%GfaQtc V?-36e97 endstream endobj 762 0 obj << /Type /ObjStm /N 100 /First 879 /Length 1590 /Filter /FlateDecode >> stream xYMo7W^(9iQ$9 |H0XuZơ%%AXq8y3PDJjIS%Pj5û&aƳ&ejK_ [d(U<9UN$9S6}ңQq =4uA.-:aFTDV4zc)y(. [ [TN,%1s50`buO``jR[) /c0Cf|0V:|10ÒJdOJH&IB!d dh$2I\ߜ!r]5W,<6I,% =0EgNsm @G2Ih"h Z.4< ɰ69Aג4;^@^0];$+^xѦ١hX2LFPq7+Fh aT ;kMagZ fs,*-0mTŢKeuv z:NvrYܻX>N/:iZl0 At͛bٳB,>&fأEw/-DQG' d"rՃHd_ǯ+xü_'VEZ(-_*=:^Z,A[bbtvdvJ4ӧVN_>\O/bv떧PQޏ1sHf׌d@ '\+X:A!uʑ$GBCXkQspOLd'ۋoQ](|pv*Lٻ?.'gˇWira^"dqnV3# W" p&?K b;=)۰Ё"B<)Ri={FЩp쇴i.`{.\ǰqCX.*5BcX9 5\3+H;k!˦kw {^fw:d|;є D{(xlnwg]lz5kcX<@5Oǰ:CX̅:Ĺq[x 7e,uL.s>Ų2ЈHH6j_#yB6lr)>:{V 5")L\cmpmjʘTB_^g825BIHÄ܀~ !)1yE@cX>Ut 1hE]zx,\J_,%~ɓ6y_8ϦR}baR? lf TsBꖋDtذ6ٵFª,!}L)8=)o>ͼ]IE"Uqy*!+ `h%7{+?zS> stream xڵXKo6Wh1CRH]l-&A+ӱP[2$9i}g+I73 IF?]\qJnXFd$it.>nCkKw\PBa˭*ҊPDJ4j[]Dn\.*/p dp&TdY+7i33 0FvqP"ZfP;|⚁m[z5ZKwk!r$SWvCBdIuL$T fͱBa*<)T#TuMbf܅_0hĿ0GCJ0;2P*uΘm'=m|gm YƮ@W:kaNScdf{LR"Pty Z r>G䭍̌k^u l @} {;`s+ 4暈R`ZtTI@^y p]%6zq$œ$*j#DPLl~DPC^1k{S3Ycc (J Pf*.ɼ1q%5HzKnC 1OQoqspnKR &{+#;I&)gSN\MhRAY@H>f H~cMh8)Iݲ|O+cw1l3Mᢢ+mYˆ# X#}q:d?̮%Pube~А+I5+prw='l*U#@t6MHx3wF@q 8lbσԋ)gр+A*'\K(-ڱO#xH.lB1a=QD37SY^z+:SW`Ӣl&oݮ7~ Ư 4+R,4\FGqW`$% 4l_WAZm#jtm2aM{aeVW)TRIO#R$HDWto1'b&XZZQ!I^B 勷#=}}a߷7S2|a?r1BW*i.Ɔd:;wq^>Z79/߬*4- d2H{wЀN序yx~+mCpk꽭*uwXOؤXy$cFhjKe|֧S endstream endobj 896 0 obj << /Length 2846 /Filter /FlateDecode >> stream xYݿByh<'thEkH-]f}o.; NkfvEW?xsJ44eגHW:a$cru_~Tc^?WUt(hf A$`S@1c|,JϒV(&.ޗw9̱ zo'eIIaNedV̮fE@+~⑦hˮrƶaݮo˼ &\ h9a4E뛶QAf鳨Pwew:B -o fD Sx@L+ ic'2 nۢ.Z<~Ǧ.꾳@W37!<X ،bޖ>H n>t޴m)CUq!H!GHC$1elm\+2M  3'DA[冫rW6뫃̷]hT59|Q-~eQ[ŮkjKU^1" F@ړߗEI,VocCK .:o*5X 8QW?rT^aLm/5s w {ON<ƿĈ 0g$1ENԵ]S7GHzDkcH$ .?N} $e~e}B9Ǩiy)2N <1 sP6 z0k Yُ`d3Is$1T3oq3//__H2 %'ֆҔc 9D0)Fv5|Ӌo[F)]m]S5^%6,]Z$uGcxThv"RRAɶZmm80j8eBDϰ0cgwiBjF8ߵv|w]T: J>{P -PMA~2v֬wcP E_Ô!NX%F*8dD&aڮqJEpC 99 dAr9e1Hnzg|d}uJBXFUPϲ3P?ƫsTQ&`uxg2qYv#bO=i9u][b jA ,bzSS̋4S|9= KjԟRB!u)k3')g6xr/V?wwXc|}l">ZٛPn ڮJS"olO ~0 j]U+MB'9)~ M/OެI1- fp) XYo$]`\W}@P,[1#3+CfC{߹PpU;/BNAHzB]K9zߝ+a-vJ%Wݴ8M<[y_,3I@s)H×LO tU8Aѫ)A(Ƈjo8b|eܢ`-g2"NB-XdY&مfJJ mk@f%W0Kf0;gȏhGlPf(Գh0Am . Qc^wp3ϓ%lǠZ&DN.LYq",eG񸐽ާC̕2a+77hq=!<{#=BЋ)94~x:s8蚏'}uNv-4_=f~ˡJYL[fTi8g^D~WVnFY#t2'$?O3|ݣpŢCŸ|ڧ0 faL!:L[GRAӌ6i=#-שeN+7.Ȍ?' '[Gc|@xa'Z]aq,}CVbM- /PArĮ0i2?ت %S`$XZ@RhzoAI F&폑l|;3] ؒ/t7ړmȵeb>`\_ȯ.б3X_oTEðIv`?\ˠA06}' !9-xQ䇦3~qI,zZ! ?G1x>} %G5~KQg|:͓۠E2,1-X \W1/وY9. {c* Pkp 몰gr?ƕ}RyxJfW\ctsS'vƆkbt?nIQ34-XfF3^.ގ*|oOӟc.c |1>y57{oe^@oHƧ(#Rl^,.':|bL>v1}z)rS}NI6?{ύ пzh> d@qT`+h endstream endobj 903 0 obj << /Length 1646 /Filter /FlateDecode >> stream xXK6 ϯ16l-PXdOXI:VG/%J~$v=$(#)|x~R1Icʂ>qHB΃8b$ea-ϫt[3zUngEFIJS& i L ub(5`:4H&4 6B$Ȓ$⧗kQtO4nJ)amz\+I$bO|pR "|u6 0ۋ_6"D;1;A%i;uߘhٷШojγViGsQYEYpL֛0FUEv5+=f+'8ZJPcEE$.P)s`%]e7]erQ _&\UeV܂XM@ⒷǶtUibmP!5öV]ؒ[;D2K2 WWsk k;qG$ .8%!W>=W_>ֆɐ$r?]Ț aIV%2n>yg>nZS {Tǣ.f'2`}j֑\3D㵯 I=uH]pե;0Ka qgiQF~xSH5&b!kZwNҚ`cVVY^9²]UY$C掹w$xojP] T {};K̼2qr pM@L|<+o}Zb'~젾~mLpU[-TӾbuͥ= ϭ/!ԓWuZw^ܨUg4tc=& cw/%:^j}| 4ZU.oTֹӶ\,d=k9z;o۔P dbҁ@[c]g}ITc;889Iwn -R]x _#-pI9«&/&k ʸ.;Uٱ00\cU = ӹcը*D}9g^huަҳM]Szyj=%T1]! 4Rr =BY‰kb_݂F)_Ǿ [_t?4F'A]U >ј0:#d1 @䄋/\&$L[ X^H*_SpVDܺO0$X6;]!*#HX3:ƥϝK㴍9H4 QXa 6)$8#;O#j_A{ԼpBJ%$ƂFJ0XpIB3?CzB+ߝ Qr؈d tlS${\8Gכbs)E*L\9LUGծWRނgV~X۷ $C7if: &8Dĕ3ջ~}w=3;ޮ/U|2ZMyOHdgx<_Yn endstream endobj 910 0 obj << /Length 1608 /Filter /FlateDecode >> stream xYn6}WQ ЧHZ.P4郬BwɅm~}")Z7 }EѼ ̜9qrՋ۫gH2Td$fqJLTܮzX]>VۦQ ;1q3+'+LVL\ ;)G ;W?FZmSw/ |zg 1'+ oYS=c.clxwۙE$gO.d s QxsPUhg0=bC`N3aWTഴtJFwZ㯈DnE)`] ?i1I3vA3$$H)2ap92`4j`?d>W7"IGmFq?yc8bLs 8 ԭ ŒC,dlx]msMEUr /V c0#z]d%AB+p q뺫^ɳecRte6"92bmᄇ<`V;8_8͎"@g[Ǧ/gʑaL?un*2Gt(*6bg%;rEF֭'H4c+ A0E#V&IDKED)娠#|Y q&_H򣄃9 tXi(L: &FZ#hP)0*ԩwʮ=C姚pCT {J5;>lݞZ{~dXӻ 9@H8̔Y vXC* F LC&!s`,qUzֻZ[ZX3Y.1uzi @F],M\|g#02ptUy`"ߍ|sSߛ_cVM" UZK魾zT.0! %*vأ!IU}@Ukh㍚lOxV3sĸA%gY)G߿נ)(:3fOb\M G:%涩P@h4F͡v&"g5Cʙ1h Za<z4/^$Ks6:/{Go~9Nq p8$ F>"u*ԖFG=h U2ʆY?,!vp>DO1x)Y)Ju PNF@s^?i!7*05qlq-Y.Xy(OLWV/j́,&^ ס=T}VCq}&xW 1׆EF$xM 91$"O`A{g'|t)R (W0 !ħ,nqƓL9#6#j0$MOlh`@3 ա1OJ9Qn7+5)> stream xYKo6W "Si"(ۇ9ZybW2$mZEJv)_q%f8:Oޞ %H'DM8F$HO.[e"[|>V`Ck`-`3" zAThRRE qD:^GӒ`wvom}wgZgђŒ0{ dSҧ! Mv lh/IҔ KB\l` |Ƞ kDڢ*VY23z pElfc$F]Sk뼹U^f{Y9!8¬?$$bu$NvЇ|Qqc=َ!M^8l7h>{Cǁ#f-,ٮ 7Fk hzkZ {ƁW ؼ1F$ Q,~T>' 8N e.I`$k`F#D_>>i[IQ0e^! As@6pYb(ŽF|DQ8{0! A!$dI#n Bژ^E1>% 8|#>u{v|i)wY0J$Ip]1gh9WmuHo!`m<\R*NOUM7QZΪ`l+E}BJ(m_/aᕅZx:fx1b c.%-qM W p _naA-wWZcUk]_:k6Wj Щg{Ǟː`[˂ 8tv뢗,/̘6/Pwt޾s)RK.Cf!ޔvzvyߔq^*{\e2TZW<ӇbV}8l>ߌװ,kRc1Gk{kU28Z>o`rekUIVrOAA_$\'_}8%J[/ɤ kU0BL]+*ׯ~@u{z~/n|d endstream endobj 940 0 obj << /Length 2132 /Filter /FlateDecode >> stream xZYoF~` z>v6B ;84-+ZډV_(J)1 IQWG7Eo/>|"Q(Lۇ*8“nk|ʦDLeMF`dC vaNL(gDLfL -GڏC8z\]$w31G*t7΢/O dF0/ Jj7dro,jtwB8v~hz$SLDZẉ%2k@8bf0,T! )f:LMrk䨗}'οnrc:TuoUyY^V% GT(4&!P 7oʌD9eʊ!`A|aPd 8'LP๘<|G!,al|[o8 'BZg gp#|gVOډl Ih(M!PqPK ɕGo?ofJkWD.͝B1c= `0 p3GJʳJb[U}oA@;{l^l^_^` @1D{qS8k,;dèHzq'FRw;ti/{l^^mȼ8kĨ> 4(ېqq6[zF"w>^as;Q²=x.&)pk'M8g([O6F`Aթl6pvK{_öfHHGQ.k_] &\"&y7onǧ]!llsu-q3>Bv3bpT5 B$^MG븻ڥŽ3|}Awb8Rs*óKoY<N`bܱY>c(VpDzNž=h6S:e|^N}@=9tusUyqGǕr84 IF;lg]ɑE#PdEϘ#1"p}_-T_[np`Ex5 F6= F>z>8VCWJYpQ i3`ydW{8Uy0_Y!.hUV~t__ֻy@B@S/@r]%N %Y2\' !v;xpQ ")(p(~rVQ6y]B?9;y/k2g TueS ) endstream endobj 949 0 obj << /Length 383 /Filter /FlateDecode >> stream xڭMO@I첻1`Mz {h(&;,`laH=Nwy {H'!BdPI hd *Y ϊy!WBK. CTdYhSm91>=!װ2F!)%*#![x`φ@EM^_ZĨJMh:H8f*HS5oẙB!MR2Gdźr[։j􄿝/sOmuj,K.9.[01\`AEhkWת8 1鷐~1)˼HSo⮑lvr46LUaM,徹W|~"}}-<^!&tcs\PPJ$q3[mol endstream endobj 954 0 obj << /Length 1647 /Filter /FlateDecode >> stream xXY6~_G֢Yh"ݠ@zqYm^vYWTe_8!l(EZ3LMhyi<|۞ʍ C~}US ǀϾ7oK/틗ZA2E:kf@bTE52·+Ӭ5S iع& ;:]QZ(s\uyvaqք<| S0UٛAV$NN:x M vш|l<@(C]upf0fH 95G H8b\6#_iyF'oc) ?+wC8 `{,r-2Cr>rԈYb h5: "j %=dPnN DpQ(h$4U7lrK oCf؇  =ijWg~x!,nƯNXZQ~poU!l.96~I?R{ڲ>ƒPzVo&SR=pS'Y8Z]}4.9K,+"VOAva$E`@GE`W0<JK1׫ /63E1Z NebFΘkompH AڊUƴtaO 5)SFXfi{.{./-m&Iu2**Zr4Ոѥ]]E`o<T۲H Asu6H?55kkXM]k {'p]mqjtl!gt:0=S.`UхqaKvUm syPEloDC)v{ ݭsaRRJ{`YŖ}zӆSGu~=(oqS\ucmbezϼb( "a(0aat1~Isڇk$j3gZY,{dR1`[ nf5 +YSQ׻@iTo  0oTp:T%V`.C lT}n|ܳӛ,ɘP@ > stream xYIFׯ |REX\I\&]a۞˗i@#"@~ $j<޽޽È] .t!9!t/tCnfPc~9<`:e-3l5I=ߪn $ip4$dsc);3ZrEe^nGH‰KJqChd;NB_ Ə?!ޭtsm1xbx=V>dL Ą,39QbJ]^< )NM1JP*"2SS+E,C |4]B\\ICB4}W'9gOrr'9w9_ iQH@$ Q%VM[Sᐄ^RuѵJŶ*;e_|mZ!kv6K?YKp^^/}1:~l %!5i3J#1[6t|7Kntx{xWCq{cf\RCPJ uւ2'&VYmj2R]6l@0N8Pz:oo&Ɨ Mߙ$RCDZ1cWsjdQL"9bN:vfxuH|xc}ԘK66=4>:dcu~ uTl*I7YY)\FL7Eo[G4[&pΒmux}XU%T.49 }^0"8ynO& OI{} J @;O=6h>qۻx4a4Xz[Ǫ}t8.` Pę& >~o9Ѻl`xjh=EETA*vǽjձ}x\V]cl;Dq$"rR̬1GJИE!rz=Oo @-zߟjhSYҺ-rYP n~]aC qqbxZ14Ƅ2Y!Ɣ0wy'Etmu|>MY{j#n/`;u(,gs'W/63&gKr4tiRloS\DbήM̸z瓧OX}aU7gY-ex#>jXI`>5kpʅbvřE=IUKC[]8Ң,G$|@_a,+hd g7yO#„mÙT"јa  {Z$b^bf% /kT3o$|g' x2`o4mH;yM@L,e4R("B؉_. ~;͆!/#"<#<_tsT%{9.я w`1JF?o6j`CQEbDڱC?ٜ1 (aa9'k+W=hЀ8g %$:!!(..Sp]YaeA~9-6)z7CY._|x/%u ܬ Dыa( ,R3֣yyݴ6'@gq^Eke>^2\L:7gxd.Ash*^Gg<^@v-w?ݷcl37 > stream xYkG ~߿bۗٙ43ȏ:-$yh:G11b!l#L ;Iu-k)VBG TG f[$8 !2)Tzϡ| :z;*rVh'H)[8)=F B0;6D6Kzg,PalJ)dV4E \!R)3M XM-54hPZ8Jϸ͵&0S((͐ؓG Tla7cQY=݂qb&0xtHt -taԚJ @b(A@ E nS< g)I2.dRK`6x1 9b )[ d`,֮X-lxPXED.B;H/6p"I- #UxT,  IDj"râ$'͌7݌E59eVLȦ Tȩ`"/.a|wj}ŧa|t<84I0]?"`MB-V9w0 e_NV7-S><{6os{4rL5 Hr>ޜWComy|k#x'@^FօTbE\؜%" pab9d (х:HxˆjkAQ]?u~[39 ՜w0p愬ֻ;xIC-]rynTѫ0_|^);N\ KX]Ѭ%חuY_esXFTbAcW"[[4@FՕѺT(ăLi]|@, r.%xRaY":k"1W\yNL蹤9\fI Lt6\\oʆԟ:\JV6C,=Vz)7zZ 7;ɏ8ys"o3^)de@}LFLpAuGCk[u/7aQaTwGf63V WO` 'aooDhEĊ.-fuas/Mx%nR՞`r\G+_JvǛbgC-\`wb&CD.,YV6V\X-Jh<3mb|~(% o$=Q# v'氻4 {g5"Nn#n{ wW޸f~|/@=)ڷKiAf%R  >~@؄˭`b T,Q E~r 6UY h! jǗSS4Q=WmnL ǚHdYOO endstream endobj 981 0 obj << /Length 495 /Filter /FlateDecode >> stream xڍSM0WHĝG_R%.%38Xмfތ~h n @?2Q(`Eܠb{L7*ETJ? 7`"LJ MAfQ%C"ZљȅY&5/sA G6Ħ{7j/=\.}uh 337FO XiOcq,RLbO!M9S¨쾻4X$%=9Nx54DU?"&͘.Fr7C7E_S]S$ዣ=lڮ ](Ĭʥ9(ZG|%?4R#c} dm˒Q8lKwԝmSM4\Bڔ ϖ~.8ޕvK.$qK;ڇ&ڸڴVKNλֆڟuW9.)$,~aR_wWy( endstream endobj 989 0 obj << /Length 1133 /Filter /FlateDecode >> stream xڭVKs6WHD0@|4viV4DN)Ê}v-Ji{X.~xpxp~nQiqǑ :`2ʂm|בδei4+x0!8_z-+@<بB6f4dfj@˼-i.?zU@{W7gZ2xX ~c D V`l*hZ!M3niELE߭mǢ ڰĢa~dpnl* U=Jz|%^Py-0^FEis)ɴߟEF7zʋ5}m׊4 JHoCe^߭!u3W9@@-+yہ˩Xq=7"9Wӛ|u@UuC⺥b _b7"n2Kmg)-MĢX`N·xRi04- @CM`auckQDƼi_˔37T`q-ɎZfl{/Nʈ%Dž&#T#%& pfyt1B@#4NrOiD;O+ư~r~>_Y(E 6xg}] DƁiՇ<($=9#j3MpC#1)5=VooA(:q,Ar @ F"!$!E5/ 3T2 \lulnup_=cV e℆LѿgN}g߀Q"JPDx endstream endobj 1021 0 obj << /Length 2038 /Filter /FlateDecode >> stream xYr8+bOvlt2e{fI/(YMjnŃHCza@܃O<~݇+&#$/&2FIc{t.jJTvÕvǓw_i۠8FxtlVu,ߛ G-{eERe5owz[β. ,)\G?]:%"ZNEPŨS&ɗe5l*Ǵ\\.LA aHp>q]Os3<Õg DOO\ 3ٶ=Fq”8ZVf]#Kᣰh}¤ ,_N Ye/ =siG8=%D Jx FvmZ䘄 'r2c1ĀxOH)E-Y?c$%#4WSWiif/(|3p.>'u )z<ͽU$[u;1ruBT!/yn&lv@>iDG&0F\Dܓ!+fSM5\^>_ѷS!A:1Gt2?4n;vO1Gr]dU a=Y'WLjjs^uu$ W:IoREYM7YtM~Rn*]i$M]e7'g2-*i9@J9?7MYY #xڢJY.͋%_i5+mEZoz dDYf)K,eY cIOXz5:2UR3 9Pr'rQl[ hu-8v"(=+ݩµ0Oقdy*nnn **Jة$bVDG}lwNZeFD;6y#lGv8yt~0$)M˘-*c,ޖd]lx?'u=ǚƭk//f|qȏojE: Am r%@_64s=oʕGؿ].kjww;4Bu!vq*Qce*;-8Dɟg*eSu`šWeK7eMc#0 uc !몜v%4~3ŢV.ʶXtGDy`S"5ZM ? Z L䨍y+$a(c}=VFK,VZm͎o5TnɛiBظmOvmH}&ϼ`PlR&g| +[X˷<`wSMJJ&#\sUc+W`ߩ:^RYw'!x6S p{#vy7u!(p[AM++X]4AV4@JrWJ*˿;-ུS1l>Of3j͞Yza ~zrcT%az?:ڂ~Y8MΔ2r_hϰLlm .5 A/ePh@h7X!4 Yod TE!XP$QfQxk␭Fm85cTPrZgFL,j{`J ` ıN"6¾T8^ꁁ: һqX}-tS>p\:r{Anvqd vA.`Q#Y`=nD]G J1;<߀ȷ-tfkb̌ 0V,?{ HadY *CXz%OysV%c,A(,ՒIz @"^``bO;z Bݑ"@Ï&m741 KC4!4 am/9$,6+ҞAEVLjp?]m endstream endobj 1036 0 obj << /Length 1454 /Filter /FlateDecode >> stream xڽWKo8W(k."[JP$ nH{e&*K,l9rw=|͌șo^8G8<{39DZbM#*9F2!HMfC|[lDDrL>FkXІ! ߴc4 hRץV/=aL8Oh4fWa.a"?bfߵEmj[~'-f~R2S~1-lm_Ma6-Wem:VfFpsa.ۧQ7H|ј0$tjSrѐNFZVos.<]apG=I$|dG8[NJ3]G] c%8fg, h#O^Ԩ$$FpH >u$4+E ,_=Y?&*_`Vළ? f2tikL[?˴[9}\E +vꔿH]zڼ+iHaR[b*с³-7ՈxmQ*>rd0eяՈA8r mEAoA́&p\nHg%vOۍD :@4 xǮFTy:ø/cPA5^;3~MTrv'BKHsbO%$k H̸)gmkF,hT] MKNUrVfXvcH60}3hfTkȩ:P0(tG!UULUCG4gT KUW-zge1s ^od2n(=dя 9Z~ѣU?_3%ܘ'ɡ%RJ4HOFt@vXl~+%#&cvDߝ?f5Tĉ ^e\yYG¬F R¹,qf}.2!T~t2Kx_"֡` rv֨hл5D< y>;χ֠n p'E@%ǭH89o>uHi|`?iJ~~H^&A ~3I7kJ7qrWMbóW>W%J<R)%=S}X%U=~r/p}R endstream endobj 1110 0 obj << /Length 1459 /Filter /FlateDecode >> stream x͚]o8+rJ[7|\E@YQzR04$ !%j52C|Xѻ2+]r du1 &Bǘ̌~4oOx]׵ h2[4غp% !ۗQQ%2Q~bܻ2^o1s$$$5]DYDBH!1v%r%v7:#XXD`g fv֕p?΃5彝uil%ljq  k5#BпaG觟,?ǧu0 e+Α[/C%z%r-v!5].0پ:͍Mj^{t|6{p(^~g8!Xҿ^e/I~s S)]X 7. ;= { S77^:^/?\b$M' )[/7aV%eG8)R/"Dz7eWGZzW9d%T t8nܹĩUOˎYPX0U:҄ݪ !̍B3ff0W ;@Bƫj| V$umɚ- zO7(gd4 b<`]>na 8iZ'?8A;,yk6tvuKy?~3RzWRneAAJwM+7^[/'t@Y`~")PWCv'*߲h6/ګFILW뎢_W{4I>9VXcLL9 T8`5% 2z^U=>ϷB1QK ku 1`ľ>P#|d5VAQ'Cx$֦gdz)sBi!utW}4HiFv2fXB*ga1HF KnM{E_[U[U_p`Ɓ v endstream endobj 978 0 obj << /Type /ObjStm /N 100 /First 934 /Length 2390 /Filter /FlateDecode >> stream xڽZMoW\8O~IX}Hb yQi-[挪_.^3;Vf1pB¸z!gi^إъ6|\W)fz1q,Xje̸ f+ L`v[y$ GR^is7ť;^qspGo >dM8fz!6Kxo'Swb#"`_ ߅e|[Ï?<.-K./߃7uq//߬/^|w{ymZ:XZ)\?.?y:9ܴ ŢjH2 萘86٬r|F\~lk)N݉KR&C(q\~a("]W`$`zs 7~s7޹P8́ss0rȔȔȔȔȔȔȴj&Qsc^{7LX=Ա$fT ?AxG:uAZjd՝OAVQpDYmLFÒ2JsݑrO|# dRd[YAJ;r"{eHއ#Hba@%|QPi'"Ьh.n9 ,I1].Tmգ-DD옧xP* N9bDoI@1$+:%{X qj:I"ABc $ƞY'IsgC }6zvq2/:\q٩- y =AP.LtjhP.dǾ~ _d'><n@萳n@@BΖzCw۫ `JЭ $$`|.ZSڄ)ĂyA7v؞nOȮc7GFzƑ}# JۈmVdG"qۈ,HU67"C~!oDf~uhlFcF2϶"=3e{(_m"mÈ}ܿܒܒ4[sG@iy%q4-ry40Wppc.HS`#ߎ0@zBtJqV0š Y8DDhR`%EXxW{zl~GDDoQ98|BdD#QKGRhMݣ{4y4TTJY'rO={"D'rO#G"D<y$H#g"D<y&L3g""""""""]CqK9~ ujwɠ1ٸe@Z+ EB-4ʨ ݪNxGe~2;V{P@=n P3 J:ʜ3 \c],ѷg"AhBP4BHO b 5 ٓĬ3~\ uR,ϐѨRgJȚvslcc3u;1;z0TXu=~D ƣq81EEKG ?R [H.wx endstream endobj 1198 0 obj << /Length 1782 /Filter /FlateDecode >> stream x͚[s8+xLf6*Bd$tg:m١K ׯcdٝNL+ll4=tE߳5]Zvs!! cmoQ8smA_~ !m -bqљ-o銺W_b%"kb*//!yP~$O~ _ʛ*Vt}f}+ ,ċfJͺr:F\~8v*k vnq* Iνk _9m| [ 8m my13IUO R`l'/CD$Zf7ٮ#{ɸ8:M}#W]Iik<>އM]ĽzGa,C%Yx/LC?'Pv jֆu.6d&IM4N"Qq+nY OxfUQQo4^cq@4RxMCϲBPL4$Dl_YzB88ŒZA,@PǸnMRx0^MI OݎlwUҿzHrp⌝*C֚'K\i=TLQX<2E#Ӧ)J)0qbTN(auKLM ^Wa|YKcu[C};|BOOw[m.X:~)9 86jx' wVsҪv *J^Lm) ,孾)#' q5?밐C"[.)QyM2p.Ww~u7 (gdNzv%.7D"b Y ̉އ(̙HSJm|؏ҫQpnЄ4t=ooT=C8&7N~Įə>Fl:c Q̪'SU яחb^v:Mtʖ[L>+CNkȣ *-]~ WD,GII6Ϫn$ av ӕ58bݼ ZyMhT4p*D3;Zӱ~'f3~ m9ZaI 2mxZK4}7-L%N3(/3a?㚏q^ifG۝ޑC:C,ċIPdZN*7M #\"5)s@VQ7"dH$RVN8 ZOݘ68uӷ,̋D xkYIky>yޱX`L=EU:IP'w~(kJcކY>&Z>;UꍄJ}7mExGPiTtP!tg>==/S Ud9fa12k΢1HJNC5~ɼ-λgҔY/G;ٿU endstream endobj 1112 0 obj << /Type /ObjStm /N 100 /First 1017 /Length 2758 /Filter /FlateDecode >> stream xڽ[KoW1vף`$@IdAQ}SCcd8uud)ʅ$ M:\ O:5Ѹhi|G2>VnCQևjt'.Go$E\[\OՑ-ZҞ[IҺq5KZ|ΓjդfܭH_I[aQ=x0+I&F&Fyyp,=yVRG2_njSNo^9or?>sO9û2?ܜ~ޕ.о`fwŋtzN{N/ӟ~ۥ7ooD6ZS ̴,DHԅjQh_ QmiDD+  bw[m!@j` 4ʎ}/3 I* ƞ42SzYZ]:L_5/p Q`p T>Uzi;JciȋLWI l>aTZ1SIo9cř0ߓY eG` Ptj rAi]6P4 ra\#/> {/U&)[Ȟ MQz)Cr_B1$\RashbDĪEX*M=)K qIMGi$\f˖4 2gТ\i.! e @dɳ(L+6i-b`d`(/"jL0f3:hR8-ODy瀂  Ö82O!0ށ#k1|GN|v/ JCˆy&E&dئ8-)b1TA%Ā+WqVvdoml1C,ON!xl[r|ʐ/dlHdm"t.`PA6E4ڠ$!PX;3N<ՙ D]5 \J#ְ!5w=6 O!e#DŘڍp[DAٖ9L"('a9;@Xx,j,.Xu!9%@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\r 5k @\r -[ @nr ={ @r=G @<yԩnkqPz;/ VY[:5r[t1/.NW3nDҶ{^T roP{F]. o$UgLJ *r;r\]GaU@J6WPҭI%rlh1YveEH$+{gG h9gu^18DAGKr21Wr }X/Ur>pvTuJ]ta͏봘YZ\9+5~ОA%@LsU yLS2. 녷g A*cα\[ "b_*m b{iW"f;EY1y]PL^ pWag^)}`+D\w̜^S9fh2[ |EYrô2Z;,W nsDiO"@vo FlR"zZM%8Mk̚Jh1c\녣8-#J3zYgA3]q/tnuXCP&R.@c]@ (%K2 A%[H0f g(5(uNZAYur  ðYlI+ ^?5zSr t.u{JCV:g}52 )T^tƄ_: endstream endobj 1261 0 obj << /Length 1388 /Filter /FlateDecode >> stream xř[o8)H'v\[Vej3` !ANh~ubŌڇMca-Ɔ |ׄtn 6B@CۘΌ4ކa?L7}e02_n=gWsX%\OHZpe7n6]?6{݌#_A `yPl_ife&R eA/a/ݬUht4=M6jfpQ2\ 10}]7. %`1YS&TyăvWYSA`>r),˂2?8ۗٯ%a~fUN4ެ<= b %A2KW*U D3oK!PYR}Mߒof+r39au*r8pdFUD+tq%#a]KԒgzXK:,$pU{kЖWĉ1Ou?8K4$YM-ɽҞʽewJze oK[6'wcBIxsS30.KGV$Zc›B)25R jÚ ](DߛE+< HzמFx.qIy?CXtJ-Ff!ykyf&SM=Geh?ׄ>'QN{'kkj&o;}1cjp0. $2J&9"8? H ;h"HW8V-2_DauX;֛$+;Muj"L'7Pc$\l 5@%ޢ|)d{Ni.?)ͺQ'gX  :YlyX֮7[da\jDL vn A@CgXq뒆ˉd=%$T 5p$Su:m@Ρ$Z4N[5SHs*LM֭:0AE'x//h.KLȇsu[&%%ԶR[ԀQy=cay^ܫ31 kZn~Q|"bZ\=} *vB47E/4RB:9{.7>\OCw=boF>_ @m l@7ltJZSoXF9Y`m((o7yNuu0 endstream endobj 1200 0 obj << /Type /ObjStm /N 100 /First 1017 /Length 2702 /Filter /FlateDecode >> stream xڽ[K]ϯem$H"P!#祝9G}5}z?DdJ"2N$ I\d/,w_T+6= {Fb:Qn"'*֫pF+T羖'fsu$ZIlTqF#GT'nݩ4`܇+4KBH[K2ۓXIZq$pF'S5Aɤ̧d*~^&é |S+\/n UcT#``?F2Yj\羚\]UO:ԚKSR/n&Npq }gۘjꍝXۻ}=\IX1!./ 3 RBl41\^d8EdUU,c zSu5(]:FeW _:OX)je?*_U3_fQb*]?tWsu~2|˲TszMon.?a~w|wۏo Cyw/psyu!s7-$(O2l6={.Ooy}UDk6`P劻k: V4;22Ũeh\rul0+uJ*'`j&nJ^( Q" d9 ƵvLi)Y{Psi>B?F@ 1(L8,0]8&ɤ{ kFcFm u 4xESgcD_ \;[$U+|jHp֠s]`uz<(Y-1*4ȬQcuG0Ռ$G%h= h@`4xZgj/(Qs $kD[Wѫ׼`<\Lx=:t/օ `%fe(N 9D 0va <Cnjp^dE9Nqw:ol3KAl%}J3! T\ =TAՅ`J &u㤨.6ޛ"PD{;*Z٤HĽ6':[ݫp^$Vܐ:αc⭉-&hsK]t~U1vTA:7"<% _Լ j]?@=yv^!?tyE!  ~{ɋ~O?~ο?4Ɔ&GIcu|O8~wll BcƢŢbl *)(SPLA2e 9(sPA2e9(KP,AYe %(KPAYe 5(kP֠AYe -([PlAق])[ mRe:nA#|> 1qEBK:(:6=裬lx+{{}>BU9죬@]i0Yy&= ^X$A6f{y!9a,a%gZZ e7rZX~ .m[Z njFx1.YEN>q28|m9/Se`#͐tKz w? u ܝX jH&*lOTdYmSs1AYv0KAY]s\7ս/`FB$"Q>W r5i9A\茊3"Z!]ﰣR>*Kri;X hG>r)pד|{  `*@!r^۵Z yy"ieN]5T|4aϋ/ͻB9:!`^FwpsR!0Rj0guÁꠓ@lRFkB腱^XoⵀNrJYь۵NJ uD4'^. tHy [7vGhD)఍4YRn?O6sFQm^fRqFajK=ndע]fL뛭#!Zg?2FЗ1B'v^*+w۪s*=j>$!'af@#k0 !u=gf*{!gt^1&ЫP7z4HF_tl6=6H ] endstream endobj 1265 0 obj << /Length 169 /Filter /FlateDecode >> stream x3532Q0P0SеP01U0TH1*2 (Cds<-=\ %E\N \. ц \.  @D@D`Hȃ@L<` 6SI3a*F#JS1a*IFA aMOF endstream endobj 1270 0 obj << /Length 100 /Filter /FlateDecode >> stream x3337T0P04P02R05T0TH1*21PAsLr.'~9BIQi*S!B4РX.O e\=3 endstream endobj 1274 0 obj << /Length 111 /Filter /FlateDecode >> stream x340Գ0Q0P04W01P0U05PH1*23aRɹ\N\ f\@q.}O_T.}gC.}hCX.OPeĀk\g endstream endobj 1280 0 obj << /Length 211 /Filter /FlateDecode >> stream xڕ1 ` S Yzs[-*T;:9::(GR}?( {I^kJd3JҗmYtCDN9,gLgt> stream xڝ= P_H&Gp/TVb]r#XZYbi4$18 0Gg|'e9r&Xr;fw[9+[)5<_JYh0hAJVzƒ5/M[ߗGr:c#Mm'rIT2f6}]]b}[q7S3 endstream endobj 1282 0 obj << /Length 171 /Filter /FlateDecode >> stream x31ӳP0P0PеP05Q03VH1*26(Bs<M=\ %E\N @BA,N؃$0z8Q'`& >`& `G${ ?Qr `E endstream endobj 1283 0 obj << /Length 171 /Filter /FlateDecode >> stream x}1 @ a!s51VBVbnY-DM(HgjxD1alT+ Q9O=|1!w)ڠ) B T{@6\% .:Z@ Z|ae_U/b endstream endobj 1284 0 obj << /Length 143 /Filter /FlateDecode >> stream x=1 @wn^Xbhi(m,-q#(|cYj9֌YJUT΢yךTN̖Y ƭx܎b*N7qDoRp, endstream endobj 1285 0 obj << /Length 176 /Filter /FlateDecode >> stream x31ӳP0P0QеP05R03VH1*21 (Bds<L̹=\ %E\N @BA,ȀI;0Y&!H? d" H`Dڃzd afaX>1 ;0"<Ȏx<1LZI.WO@.Ls endstream endobj 1286 0 obj << /Length 193 /Filter /FlateDecode >> stream xe̽0[x "~- &2`A+Zl+ z1xK7}`|8CQ'p7.h nv7Z͐1nc!孅+ ݗД>!uBG3%9mM5F4V.կf֚RFiHk7e0)#W^a> endstream endobj 1287 0 obj << /Length 228 /Filter /FlateDecode >> stream xuбn@ P#$/|B=P/L'T CteJ2vhԮOSF6QQ$xlllMN ksZgtM s"GI\>(Z4;)})o)喠FlԂkXٰ,eZFJ微FV'q:7y@Ju,_)n A_b҅ Ut1s=H|/w' endstream endobj 1288 0 obj << /Length 254 /Filter /FlateDecode >> stream xڕбN@42/(V$HaRK r-%aYL,$/0;?S5K>ǒ^hquyxmG-W*.LEwoOTlϹbw%1'\|zp"_^D"j|tF}Z;6ޏf6$Z fnݐ}^-~'?pNNr]nQZ]k* iMDE~0gzgJBd:̬L7Zs! endstream endobj 1289 0 obj << /Length 200 /Filter /FlateDecode >> stream xڅн 0+ ~`A, vtr'utPt5}4б&O Crɗ3)hX֮*ilEAl&Ws:.{dbB3D]$ p %X3'=íU6}C IB%:+Brm^CseB}]P8qOdz endstream endobj 1290 0 obj << /Length 253 /Filter /FlateDecode >> stream xuнJ@ba;/@λ 8BQ7Q̣RZ,;n<9Gu|Z|XroKz)ssO놊+J*4LEsOwT/NXkj6 C"y`ECt>W Y3]>}vrÞ}ԩ:xAjmMcHo/OvjaOMbSjTt0q;٭'Kjiyq:<贡K~) endstream endobj 1291 0 obj << /Length 186 /Filter /FlateDecode >> stream x}= 0-=B LMѩP+AAAh=Gpt{1b$#CHTا39nR؃Vȃ|Ai@.P+ix< S.ST+066kˉ $Di Qx7~qvy7+DDDCt> stream x31ӳP0P0a3S3CB.C I$r9yr+r{E=}JJS. @-\. ?000+ e8Iv!#!d>B2#dn?`c XYc%e0pzrreT endstream endobj 1293 0 obj << /Length 262 /Filter /FlateDecode >> stream xuAJ@xAN`bP+.U҅"8EBxxq7@|/Ģ.oa7Jh+(*[$'eY1Ƨ|qqFwΏ)xN)%WX 0 A@'X!TW@ F-@\GaL@+?QowI Sji*ՕTj`t ^3@ #=ңuf'^^ endstream endobj 1294 0 obj << /Length 232 /Filter /FlateDecode >> stream xuAJP?dM9/T f!ʅ.]( ]+@"9/Ibd>f_dzt2㘏 S^'Di̮dtGZn8I]ȖT~/ϯWgZm+F EQ$>B>$@Q Y¯"8(: ,:QBM?jlӞokb{tM_+u5 endstream endobj 1295 0 obj << /Length 217 /Filter /FlateDecode >> stream xu1N@P#. l5ɺ&RheaB8 G bc WySWk{][!R:]޼~ȡe.{ywxBQ _>*@H9“2؜!b( b> stream x}бn0n|OPBP:!T*CfeJ;fhծGQx (JXbٟtg4{y]SLK^+}&iƵq`Q=P}.rG?_h %htL(>a%ȽwGk]BrQN &Cv&̍A FȗF7"'1u&<کZ܆dT?s1ݭC&Vo} endstream endobj 1297 0 obj << /Length 222 /Filter /FlateDecode >> stream xmϱj03 |/m&CB=کCdА͏GK@כG)ŖRURղᴔv͇ۖ8lW96۷'lviv'Ncm6Qrő9n6S8iɷDEHȷDE5G%j`XI[B 6t OR%L@EaM-?ȀN endstream endobj 1298 0 obj << /Length 203 /Filter /FlateDecode >> stream x]=@5&plQ+D ,ZZh38 G@avCef^2 &:( =< ANWc{\k7?/#&dH2٭IJGV5jLGhdڷaElTIyO*줪TIUo×T_\+45Ԫa?|p endstream endobj 1299 0 obj << /Length 234 /Filter /FlateDecode >> stream xeαJP2Β7MUHOV0C餎by0<}wēƺ|oG'NPSR:;h Cu2 1/iҔ1_Aڶ:V_^Y}zЊ3h:I<Q﷗#W,/3o-[ndͯ^kxngnݔj'RvANx?I sB endstream endobj 1300 0 obj << /Length 219 /Filter /FlateDecode >> stream xMϱJ1"0>Bt7BBGˣ#\yŒYC_„kJςƵƠoA{]VyfIc/ݝ]Íqh H<YNW͌!#|i~8-v:Q,b#X}n}Hْj`O:Aom"jAk1xp3YvG-m endstream endobj 1301 0 obj << /Length 235 /Filter /FlateDecode >> stream xu=N0\X&G\hVE"T+*D49# '@|{SYO7-մ-3.ư١m--Z.sUwE/oXmϩjG;vd)3v(&_*r) ԗ(G^KoNP=:F#Ȑb0caߨ``u`;}!A%gyY$ქ<K~ endstream endobj 1302 0 obj << /Length 210 /Filter /FlateDecode >> stream xڅ1N048feH-AK|%G2amCEg[|w[>]r-;mzM[.NGxSӜpϟ_{ ۇv}a@ZJhD2Ȅ$2c4dvJuNͨ (p7Rij/M)vAm+uӿ@"S endstream endobj 1303 0 obj << /Length 232 /Filter /FlateDecode >> stream xmαN@ `W"yG8DJS:TD$02ʚY%AC:d) MܜSE-ԬiSTk> stream xU1N1Ei|kBR[ AEQA h.SD;&O͟7+nykzeayH={ɏ#~@~  ,FI# $Hy!p9sP SlQ S]BS3O?9Cz 5I[lIݐ\N+*iD=ktSn'-o endstream endobj 1305 0 obj << /Length 190 /Filter /FlateDecode >> stream x};@%$p.$1q ,ZZhQ8%aaD)e3&SKp4C g0GgԂޠ1mx܎ cncv`3TJyĨJ6$(r&;;/Xy9pED]Kv6}zκOY8ˏywf.'+yWana Oq endstream endobj 1306 0 obj << /Length 286 /Filter /FlateDecode >> stream xuJ@g"0y!SZYZZ(]-rvABs.ovI{F%t\tZSSc/ش\-iYqaqKM%ױ 9UXl订 d ybR.aa cX"`?5̆o,, ߫0Ȅg_RPg)$.z4/@ciJKJʓnyA u%>@+ +0@:ɝs<#Nz3b:%^txۺ endstream endobj 1307 0 obj << /Length 207 /Filter /FlateDecode >> stream x}; @49 SZYZZ(f=Z"xSg7 ?2Aɥ ^H[]McajIj*UTNp>"՘VkQrtaQ d,ɹu|--"1^JBR̉*z&v:N{X5gS\Uo.Nb\ endstream endobj 1308 0 obj << /Length 168 /Filter /FlateDecode >> stream xڕʱ 0+[| LBI Njh}x&A Ifz9mPkcaP,IkSע03:;|L EI+Er$ 4./ @'PE \b<<Iya9PpbpO)T< endstream endobj 1309 0 obj << /Length 210 /Filter /FlateDecode >> stream xڕ; @YRxtJ +P,x4#XZH 6.W 34yP#PKkwFzV[s #cQ':t@>!-| 䪧䟘L=̿;w3'EP+l7jӯi=|:s+b-SJ}e GrQ3|d endstream endobj 1310 0 obj << /Length 221 /Filter /FlateDecode >> stream x}Ͻ 0C>B Zt vtr'utPtS,GB1EAA1$$wKҬ`[43\%4}r`^jijD1w5ޤ l. 0Nߚ`gTj*YO8:uȱqJꂽSyXND!uаڻ7ԗ:1D&/e6 xE3~0)<|] endstream endobj 1311 0 obj << /Length 159 /Filter /FlateDecode >> stream x31ӳP0P0b#S3CB.cS I$r9yr+r{E=}JJS ]  b<]``Q"? ?8 8{0u L?` .WO@.R_^ endstream endobj 1312 0 obj << /Length 177 /Filter /FlateDecode >> stream x}ʱ 0J-}{B(u* ftr'utPt+G#t< pwxb1?p dsԍaw\XL@y B-r@) -=/4mVgu𤆚N-.Ѧt+.Jf{m?FN3w!ct1]a`/B' endstream endobj 1313 0 obj << /Length 190 /Filter /FlateDecode >> stream xm1 P ,jEB`A'qRGE>֣<;B|?Ns42!Mgohu۶՞Lj-)tC*.G'}4!r8FJp-27sX;+YJ>!PDhxհ#qʩe#\Y.D*~ps endstream endobj 1314 0 obj << /Length 217 /Filter /FlateDecode >> stream xe=n0 ^ !Ȕt"YkMG4z0R :]ށ"ħ=,\'7O>i:aAOtL}eÞܖ[V($FFUG"@'C;MBMIU (5[resKMSCЩAgC4jFV"j"kJh+bo endstream endobj 1315 0 obj << /Length 247 /Filter /FlateDecode >> stream xuпN0/`<JUeTD$02G#d|P,'?.n\uۚPk^kozETkToj/ ׯԭ 6~9H$؀BzF{baIu=L1;> stream xu=N0\D&G\eHHE"T+*Ak&\GR> stream x}= 0 kI NEzbIJS$.(qfc.1xIjsq$Uj"ۯ1)Fy#ҜN&"Yy 2$P5sΚʮTz)z@=qQg5椳[o }6 dcq endstream endobj 1318 0 obj << /Length 222 /Filter /FlateDecode >> stream xm=N0_4{2lXҲH@j D (GQr.L(4~sr>p>ܟq q<> stream xuϽ@ ^H.1::htG K6idP@ 5E5^0PጙAKaRݮzNi)أ F8/nO+y\җ1DgiP->Ձan,Oz౽R0ʞ^ endstream endobj 1320 0 obj << /Length 187 /Filter /FlateDecode >> stream xe=@!$p? b"VJ--4ں{4 Fiͼ$)%)]"c0;9߰jP(PlL񺢨v+Pt(> stream xM1 @'49(I F0X]09ZRY73las.O>t%ߓ1y8^(NIHdK*]87 vI%w9PpHZ..XM!/3(ѯz?Gh ź1n_*U JU@h ;0" cd:0&I˔dU~ endstream endobj 1322 0 obj << /Length 204 /Filter /FlateDecode >> stream x]=Q+In$~SHB tˆB9;0f0;0A_r)*kUW*PFgD3gpQH)nQ]Z.VS^-:dmV{9muju*<5MfوݼoϽ=f<\?l@/!g"bf#~vOhr endstream endobj 1323 0 obj << /Length 206 /Filter /FlateDecode >> stream xmͱ0# $'" b"NI4)h< бZ{upФMGlL%%c4g WfY endstream endobj 1324 0 obj << /Length 232 /Filter /FlateDecode >> stream xMαn02 ݒG^:DSD$:u@LЪ:DGˣ=D1>$N}q2QDcMMtR1% '3̶{FܽBيԂ4570ze(mi_,h[i[s?v%| ϛ'a73UVWhvV۩~rk endstream endobj 1325 0 obj << /Length 244 /Filter /FlateDecode >> stream x]N0 @qK?j`Jc N'q@p%~J>a)ODiVkh)Y5a}[mpaxfV;x|ŰÖj?D˃yp̓gYYC;@!&_@b˔?ճGefoT8g~цv@Q6tozazkz4Ut_)ΔQMEјw>7x@ endstream endobj 1333 0 obj << /Length 105 /Filter /FlateDecode >> stream x31ӳP0P04Q04W01R02VH1*22(Aes<̹≠=}JJS ]  b<]?$`)( endstream endobj 1339 0 obj << /Length 197 /Filter /FlateDecode >> stream xڍ `4w/Pj)MPԚ>#46_Gth =(TWC# |=yrϭ3;/ft싳^l,N+=u-',]ƠBR"/ w]OJ Hѐ4MJ0?_9.6վэ-iN͋eVL endstream endobj 1340 0 obj << /Length 196 /Filter /FlateDecode >> stream xڍ= @ GbVbh%GH"/Vef Ʃj?8$C(gbg(X]r;fwPL@ | ~nF <z/@:Mrp\3]8[FihHOҙAHVxuO endstream endobj 1341 0 obj << /Length 110 /Filter /FlateDecode >> stream x31ӳP0P0TеP01Q03VH1*22(Bs<L=\ %E\N @BA,BQ? C GG\\\0oy endstream endobj 1342 0 obj << /Length 112 /Filter /FlateDecode >> stream x31ӳP0P0VеP0P03VH1*22 (Bds<L=\ %E\N @BA,B@ C \\\HB endstream endobj 1343 0 obj << /Length 157 /Filter /FlateDecode >> stream x31ӳP0P0UеP01R03VH1*26 (Bds<͸=\ %E\N @BA,B?Q"A=h`;$@F0A8&TT#`xxrK/ endstream endobj 1344 0 obj << /Length 106 /Filter /FlateDecode >> stream x31ӳP0P0UеT01R5RH1*26 (C$s<͸=̹=}JJS ]  b<]L!W51 endstream endobj 1345 0 obj << /Length 110 /Filter /FlateDecode >> stream x31ӳP0P0 )\\@> IrW04 s{*r;8+E]zb<]@ 5 \\\deX. endstream endobj 1346 0 obj << /Length 142 /Filter /FlateDecode >> stream x31ӳP0P04S54V06R04TH1*24 (s< M=\ %E\N \. ц \. ?aC??@P`4,r endstream endobj 1347 0 obj << /Length 96 /Filter /FlateDecode >> stream x31ӳP0P0@P!Ő H(`\.'O.pU()*Mw pV]zb<]\= endstream endobj 1348 0 obj << /Length 162 /Filter /FlateDecode >> stream x31ӳP0P0UеP01R03VH1*26 (Bds<͸=\ %E\N @BA,<b@N ?8$D D`#2f2X3Iq,63 *@'W yK/ endstream endobj 1349 0 obj << /Length 111 /Filter /FlateDecode >> stream x31ӳP0P0V04W01Q0PH1*21PA#CLr.'~PKW4K)YwQ6T0tQz ?*1pՓ+ JS endstream endobj 1350 0 obj << /Length 102 /Filter /FlateDecode >> stream x31ӳP0PP04W0T02VH1*26PA3Dr.'~BIQi*S!BA,B?ĸ\=E:( endstream endobj 1351 0 obj << /Length 190 /Filter /FlateDecode >> stream x31ӳP0P0bSSsCB.1s<L=\ %E\N \. ц \. P߀ J2~~d|"N`%값 hL F'y,$33oAYՓ+ H06 endstream endobj 1352 0 obj << /Length 198 /Filter /FlateDecode >> stream x}ϱ 0 [|TkI Nj}>JcҘ 4蠄|4;.ˇ)Jq)+di#  3 bnA5o3bDTYk[z^DyÒ1 <§QSHhUsjD0N/QG<T]KDbh@C63K[xGj endstream endobj 1353 0 obj << /Length 230 /Filter /FlateDecode >> stream xڥбJ@/L i +PysQ%o镶={[r\/䶷\C#;"L E(JdG)23!_#2C[{GE{ʐ :Z2 fFb֘9e)QSFO?V2C鎾?9ru endstream endobj 1354 0 obj << /Length 197 /Filter /FlateDecode >> stream x31ӳP0P0bS3CB.C I$r9yr+r{E=}JJS. @-\. 700& @Y4$)&?H L2A :0Y&q RbbH.C _@|A! HC, !݈I endstream endobj 1355 0 obj << /Length 149 /Filter /FlateDecode >> stream x31ӳP0P0bSS3CB.C I$r9yr+r{E=}JJS. @-\. $BփI uD6`D2JOĥj2|$(47Ae\=WD endstream endobj 1356 0 obj << /Length 141 /Filter /FlateDecode >> stream x31ӳP0P0bS3CB.rAɹ\N\ &\@Q.}O_T.}gC.}hCX.O$3``'Lȁ|DAjD  \\\, endstream endobj 1357 0 obj << /Length 230 /Filter /FlateDecode >> stream xڕN0/?BՅv`b@Lб $R_.jKŊ-}oﳻͦTИJr&7R+Ly?ocv~K*^d`dPɑaDZN{8;@Ά:0GdzT 3#'d!Q M4 >15Ȏ×t*ć5 endstream endobj 1358 0 obj << /Length 114 /Filter /FlateDecode >> stream x31ӳP0P0bSS3CB.1s<L=\ %E\N \. ц \. p,~BĄ'W NP endstream endobj 1359 0 obj << /Length 105 /Filter /FlateDecode >> stream x31ӳP0P0bS3CB.c# I$r9yr+q{E=}JJS ]  b<] 0 %\\\6Qg? endstream endobj 1360 0 obj << /Length 231 /Filter /FlateDecode >> stream x]ϱn0` n#' v D$:1NClf1t#4Cd?Ka@?>ό/x7e`+/l2 .|w9be;U:.Jp΂Zг j@AR&B n ~x)[yF^}lEƪ# `"P0~? endstream endobj 1361 0 obj << /Length 126 /Filter /FlateDecode >> stream x31ӳP0P0bS3CB.rAɹ\N\ &\@Q.}O_T.}gC.}hCX.O``'!P:'`b\=jo endstream endobj 1362 0 obj << /Length 201 /Filter /FlateDecode >> stream xڭ1 @4 \kP1),J--!9D,,T]S[̃3nQ*9zK5.sWj9!!qSdaV o,cP$nPPBz@Q(>Zll/5.K=&Mإ(o9)[-_m0v`fs8 endstream endobj 1363 0 obj << /Length 199 /Filter /FlateDecode >> stream xe1 0-wӖZtP*AAAQPPRo7iqpT I( 8{~B&6}\9Ol[L,7@g@GEq;>:@8w^@8@X&as!eV^zH4 6Q25> stream x͐=@XL #V &naRK (҂.C l}/N竌BJh&)^PF ] 厹Fq(Eu1 }C$QtQZۂgmJ9Հe 7Fд?oaF k ,|_F&h endstream endobj 1365 0 obj << /Length 182 /Filter /FlateDecode >> stream xڭϱ 0H^{ӐZZ+AAA(}$]8N KM9&xg,\Od+ f.S0~ ,Ђ)qo19/"jB.P;UuDF 'aybhF4j-iMːO*"`a oƅt endstream endobj 1366 0 obj << /Length 217 /Filter /FlateDecode >> stream x1n@Џ(,M#\j%C* *I"Zo+M|k7c܀?WgiG^,#orI^/*J{rϒ._t<.^7!-i<Pqj,Q?vtDQ[ ba~WJ!IdGSI;% endstream endobj 1367 0 obj << /Length 218 /Filter /FlateDecode >> stream xڝн 1 Y|;? N⤎磝"b/Y>Jڤu)&cjiɈZ=qYh>&xޡG*hɘR. eى/".Ҭt ҪwVhO/o2C xBbMn7ݥ| "Ԁ3ï>$$J endstream endobj 1368 0 obj << /Length 250 /Filter /FlateDecode >> stream xu1N@E'rai=1IL,  DѶ. (ig?lncQiں'Tl=yE&lk\FZ,6KNZa| 9|t5iûH Jbz<rd'0 (9qp&8 %?cFi=H^Q #t)g/pxLkDυ3zA endstream endobj 1369 0 obj << /Length 127 /Filter /FlateDecode >> stream x31ӳP0P0bSS3CB.1s<L=\ %E\N \. ц \. D?`OY$$ ;R?$XՓ+ VX endstream endobj 1370 0 obj << /Length 174 /Filter /FlateDecode >> stream x1 @ ) fa n!he!Vjih-GL2 +7&.&RY S2sjOƠZKFe7?/4#ڂJ"nݯ;QO7ZB؈U$fMYD@ ϝf+;|WW endstream endobj 1371 0 obj << /Length 210 /Filter /FlateDecode >> stream xڍϱj@_pK@{98Sj@-48 -rpnA0f#VX܏]і1ey8%dIL2~Ar<"9hM锳AitJweB# LX6vs`RaF$i4 ;QWytExݭ'{gk~϶,O*$=t %~ endstream endobj 1372 0 obj << /Length 242 /Filter /FlateDecode >> stream xmбn@ P#$/ "L :vhծgO@@$2Dv."PtO h"+4}ih//hMм.tI?hfs ,hRtA אLZ5;: Gk?ޱR/R7ux;xl;3{gߺjmkxN|סuޔΚv:T-tjO;Q d,ŲZ8ݎ[+R endstream endobj 1373 0 obj << /Length 201 /Filter /FlateDecode >> stream xڵͽ 0+ zO`ZE`A'qRGE6Go t(;T%w.z!h>22J;@j)bAjPjJ-SYmEc-wy 7R5-!l·<дM nܝޣtVWYw8C:rb endstream endobj 1374 0 obj << /Length 186 /Filter /FlateDecode >> stream xڭ1 @ aM!L!he!Vji(Xͣ)ykk|x$ 0 S9|W> stream xu=N@ _b%79 H" * D[n&"ymafYy.\O:/wa\gVVOK{Ǵý~~|m]=(k}fϋ kEm&fhF hrá +'2ʉ3q4|PY؁0e齳s5\@e'XreSU4Q~MQd endstream endobj 1376 0 obj << /Length 206 /Filter /FlateDecode >> stream xڥϽ 0+->Z+S*AAAѹ}>b$*.bBz:ԥVDJQܣmT;fiTTf3:; :Yc6\;lhkb⍹/N-Z6*p|ZX?4>usn tn N2\KKv endstream endobj 1377 0 obj << /Length 205 /Filter /FlateDecode >> stream xڍn1 ]1%o )$n@S ZYG!i _ϲ=gzp;:٨T6{hh.DmyءQvF0`80cf̱b9)zA}T$"'S|_Q((M I +TPGey?4dѸYz1_ S endstream endobj 1378 0 obj << /Length 220 /Filter /FlateDecode >> stream xڝ; @ )isJE"b=A aS~] endstream endobj 1379 0 obj << /Length 216 /Filter /FlateDecode >> stream xu1N0E*ir ,-D $(VT@Iv(>–)VAaYO??V=ϝz`U6]oX?ݕvⷺ}qE XXͨ̎p[P0LhB M 4ESDiDf( DETHIc %)>/~Œ\r/_})oG endstream endobj 1380 0 obj << /Length 164 /Filter /FlateDecode >> stream x31ӳP0P0bSsCB.c3 I$r9yr+q{E=}JJS ]  b<]300? C=`cf ?F%5Ƅ@.N%\=CSt endstream endobj 1381 0 obj << /Length 275 /Filter /FlateDecode >> stream xڅ=N@ M_(E"T+*AD \%7!H9Ec{BHLid=RI'tT%=VjIM}h=<|ŕԱh UXiSQy :!1{.g t<A9Nt¿ɽ`n [Y'(3@ ~sPoi5E,b6y0ɬ1$V ٺ[Lz #h&;ij$^MR} ^x?m endstream endobj 1382 0 obj << /Length 165 /Filter /FlateDecode >> stream xɱ @ : Y k 7:9utPt>ZpcҘ(@>?1t>C1I0IF*x܂ڡA ʮv@F G` t>'C/fH= b賚'b6l Q"Di endstream endobj 1383 0 obj << /Length 137 /Filter /FlateDecode >> stream x31ӳP0P0bCSsCB.cc I$r9yr+s{E=}JJS ]  b<](B` D00 aDHpzrrȧYA endstream endobj 1384 0 obj << /Length 168 /Filter /FlateDecode >> stream x1@!&p,`EVJ--4ͣ(-!5W? 9ER?֔$4hqF=`iP(QͤGet>]ń4֚ | f!N^ :^]寸3 lnO(N . k7 endstream endobj 1385 0 obj << /Length 217 /Filter /FlateDecode >> stream xڭνn0pH' Q" vP+ċekdUGk?>48^iƏ%Ii?1B4,Ⱦr'd Wwc'/kL8TEk%t:u=|?Q ;DN d~U7 S[v0ؼ?bjv? k1N\*7V*=4#S endstream endobj 1386 0 obj << /Length 123 /Filter /FlateDecode >> stream x31ӳP0P0b#S3CB.c3 I$r9yr+q{E=}JJS ]  b<]``? ×0? 'W g endstream endobj 1387 0 obj << /Length 161 /Filter /FlateDecode >> stream x31ӳP0C CB.sD"9ɓK?\ĜK(ʥPRTʥ`ȥm`C}?  Yo`*?!*9=g!@d\= endstream endobj 1388 0 obj << /Length 159 /Filter /FlateDecode >> stream x1 @бa1[ZYZZ(ZoG 2΢]> stream xڍ1@E #0eV$&ZY+h+{4(- 㲘ڼOϛ$ͦ񄇚1'O6MvV6&U~{I7 ֤rkT dR" "/x"o"x Aā, Ң~~5oU9qNȩ9IR 3,hK` endstream endobj 1390 0 obj << /Length 221 /Filter /FlateDecode >> stream xڭбn0bt @Y"QPNt@hycs U.ɺϿm˧ > stream xڭϱJA?lq0= %*#xE@+ I-SD5_,9 ,9nsckc_ťc?f5ySǣZhZ}dl5.dj0r DW@`D$  F]67@Hmtt9OYw억g߹٫e&ڥOM&7ۊ` endstream endobj 1392 0 obj << /Length 172 /Filter /FlateDecode >> stream xڵ1 A i832VºSZYZZ(ZXYz#llXZO7荆d/9C;GtVibs0W,lQ9O=l1!洖}N)!0Z2-ygg"(.0P5tŷAUɲ+Y0\%-nYW endstream endobj 1393 0 obj << /Length 218 /Filter /FlateDecode >> stream xM1J`b`w.~7hXW0VbZ * vnUra!,ǔK-tgQ ->Gy劲p3%WtpK-Ϗ kxzX 33䎅rCF40@:b #LɂY.dČ 曶AȺ lB{,Zxώ`1K{+orSN~o' endstream endobj 1394 0 obj << /Length 160 /Filter /FlateDecode >> stream x31ӳP0P0R5T01P05PH1*26 \.'O.pcs.}0BIQi*Sm` $?` #$`'0   Sd.WO@.] endstream endobj 1395 0 obj << /Length 159 /Filter /FlateDecode >> stream x31ӳP0P0R5T01U0TH1*21 (@es<L=\ %E\N \. ц \. `,dF }H<00g?`G"?\=kqt endstream endobj 1396 0 obj << /Length 174 /Filter /FlateDecode >> stream x31ӳP0P0bScKCB.1s<L=\ %E\N \. ц \. 7P& eJ``$? @cg@%4*PFF2?F2~~F2?N7 H{ r V endstream endobj 1397 0 obj << /Length 195 /Filter /FlateDecode >> stream xuν @ > stream xmν 0C(vAAAѵͣ7Q|AwݤGr6&آt&=>'|z zzBQvi z0b zoU YUX)է-ؽFF'{DžyVJtlH!r&u]Ŋ;7RCSQ񋦠iwH>ʳh endstream endobj 1399 0 obj << /Length 237 /Filter /FlateDecode >> stream xeαN02D%{pҊ.TD$: &73Ea+RősƂ)eTQS9mr|IJҌ.kk* C秗{˫3Q&l [f۲cvӨh+켍 R PPÛLm55wۃQ?ڋ_"|v։&Ԋ*Z IM ]4O`9kb{0D>7k endstream endobj 1400 0 obj << /Length 171 /Filter /FlateDecode >> stream xڍ1 @ aM@ Fp A+ RK EۉG(2E:/u ͧB"IIR9|c#ʅgݺ+Kٕr%:/%!ԕIDeoKhѰj#0#0?Y` ` `]ГnS^yi endstream endobj 1405 0 obj << /Length 137 /Filter /FlateDecode >> stream x%; 1F;]]hL!he!Vjih7eIY@5`NKnn;[.>Yʬz8nQuĥ>W#D*L"QCĶ5e" ьwO)B endstream endobj 1406 0 obj << /Length 132 /Filter /FlateDecode >> stream x313T0P0S01T0P05TH1*26 (Bes< =\ %E\N @QhX.O 27??~0?P`G( endstream endobj 1407 0 obj << /Length 192 /Filter /FlateDecode >> stream xڅ1PDPl Ċ1D+ cmq@IA;WL0 v xlagnEt4'g'Ty!n{> stream xڅO; Pl {I*L!he!Vj)h-G,-$q̃T;LNuihuɗV'/2O4Ĭxq7 $$M | ,G\W{F9^ـ"J[|rY"ֱ4nT?pGrjݬc_e*[M* endstream endobj 1409 0 obj << /Length 167 /Filter /FlateDecode >> stream x313T0P0U0Q0T01SH1*26(%s<=\ %E\N \. ц \. L@$AD=$? ?@P&VV̌...SG;&.WO@.n= endstream endobj 1410 0 obj << /Length 162 /Filter /FlateDecode >> stream x] 0->KNZ N⤎>cbMN8>] y GGbO%T2[0YFK&pOdLSAZZFHW 2"L}Tߩoﻭ "Іֺ? endstream endobj 1411 0 obj << /Length 114 /Filter /FlateDecode >> stream x313T0P04W5W01T0PH1*22(Bs<=\ %E\N \. ц \. a`?r 5ez endstream endobj 1412 0 obj << /Length 116 /Filter /FlateDecode >> stream x313T0P0V5W02W0PH1*22 (Bds<=\ %E\N \. ц \. c``pzrrlI endstream endobj 1413 0 obj << /Length 175 /Filter /FlateDecode >> stream xڵ 0DQXK'2҆  * D h%##6HWYM0p sf؜Tz2{XKf1)Kd*rdGR/RA-%a|ݠЂV$QoeUG+O;a endstream endobj 1414 0 obj << /Length 171 /Filter /FlateDecode >> stream xڵ 0EQ  miCp  (0 i~ϧ{~37 <& ~9JϓJu }s7&xܟnKœ(4^Jq^.JNQr?)F#PQ1H)3R;;J~.؆xC?ZOYb endstream endobj 1415 0 obj << /Length 113 /Filter /FlateDecode >> stream x313T0P04F F )\\@ IrW04 s{*r;8+E]zb<] P\=AQ@ endstream endobj 1416 0 obj << /Length 148 /Filter /FlateDecode >> stream x313T0P04U02R06P05TH1*24(YBs< M=\ %E\N \. ц \. ? 0`77g.`r j'. endstream endobj 1417 0 obj << /Length 171 /Filter /FlateDecode >> stream x313T0P0S0W0P01VH1*26(%s< =\ %E\N @QhX.OXǏ?1 ɁԀԂ2} pzrrxS endstream endobj 1418 0 obj << /Length 136 /Filter /FlateDecode >> stream x313T0P04U54R0 R M F0\.'O.pC.}BIQi*S!BA,???PP'W ,5 endstream endobj 1419 0 obj << /Length 99 /Filter /FlateDecode >> stream x313T0P04F )\\@$lIr p{IO_T.}g E!'EA0XAՓ+ ; endstream endobj 1420 0 obj << /Length 157 /Filter /FlateDecode >> stream x313T0P0U5W0T0PH1*26 (Bds<=\ %E\N \. ц \. @#HD؁:Q'@&> f0d82>3 df Dpzrr@: endstream endobj 1421 0 obj << /Length 107 /Filter /FlateDecode >> stream x313T0P04F f )\\@ IrW04 s{*r;8+E]zb<]:\={-= endstream endobj 1422 0 obj << /Length 155 /Filter /FlateDecode >> stream x313T0P04U54R06P06SH1*24 (Xs< M=\ %E\N \. ц \. A# ?0` @.WO@.8 endstream endobj 1423 0 obj << /Length 110 /Filter /FlateDecode >> stream x313T0P0V04S01T06QH1*26 (Z@ds<͹=\ %E\N \. ц \.  \\\A endstream endobj 1424 0 obj << /Length 103 /Filter /FlateDecode >> stream x313T0P0W04S06W02TH1*2 (B$s<,=L=}JJS ]  b<]0 szrr$~ endstream endobj 1425 0 obj << /Length 117 /Filter /FlateDecode >> stream x313T0PT02W06U05RH1*22 ()Lr.'~8PKLz*r;8+r(D*ry(01l;cNJ l r \+ endstream endobj 1426 0 obj << /Length 168 /Filter /FlateDecode >> stream x313T0P0bCSCCB.cs I$r9yr+s{E=}JJS|hCX.Ov;: PNF01`u@Qf f2J~ 񀿁;'W Ǟs endstream endobj 1427 0 obj << /Length 239 /Filter /FlateDecode >> stream xڍ1N0Dg"o|$Q6ZZHPQ *!p!eU8i=opZ-uC玝|H?Я\~4wJ3޻MÍ?ε/2"P<> ufA@5ã`cO4s1d1gʮɧ:eP~Kٜ-˺QvOh9X܅H$% RM Zlmb dr)}A!> stream xm=` .߁1D'㤎]ċ8p n #~$(}L> stream x}0K:#pO`i1NI4 Kd0FMj\ijx@½%\PPGL2P[2;|=7P~K<Ls 9y|9#l K#vӜ_[ZCN _CF,a8[NXTQ endstream endobj 1430 0 obj << /Length 218 /Filter /FlateDecode >> stream xڝ1N@4QY AT (Ar 3AzWJ_kN|y9H/vI'Zun8-)\ؙBwoVWg)6r}Gݚ3J~ ZTMa.)- o̤/`tR27V֯ifhh`+-RN]dvg9 endstream endobj 1431 0 obj << /Length 183 /Filter /FlateDecode >> stream x313T0P0bCSCCB.c I$r9yr+[p{E=}JJS|hCX.OD|?b0 AD}&> f0H0b!On%rv?s?>  `szrrǁG endstream endobj 1432 0 obj << /Length 147 /Filter /FlateDecode >> stream x313T0P0b#SCCB.c HrW0r{*r;8+. ц \.    `|$lthvb)،6 Q .WO@.̌r endstream endobj 1433 0 obj << /Length 145 /Filter /FlateDecode >> stream x313T0P0bCSCCB.c I$r9yr+[p{E=}JJS|hCX.OH" $`@CLmQD !( ,x endstream endobj 1434 0 obj << /Length 227 /Filter /FlateDecode >> stream xڍ=N@\4PY AT(PR$ގk 7eUI"Q|{;5袥aC]8> stream x313T0P0b#SCCB.c HrW0r{*r;8+. ц \. ?c4 N%'W  endstream endobj 1436 0 obj << /Length 108 /Filter /FlateDecode >> stream x313T0P0bc SCCB.crAɹ\N\ \@Q.}O_T.}g E!P E >Փ+ HX~ endstream endobj 1437 0 obj << /Length 156 /Filter /FlateDecode >> stream x313T0P0U5T0҆ )\\&@A "ɥ`l¥U()*Mw pV0wQ6T0tQ``HX`'$@DD?`AH?` @OjhPՓ+ UX endstream endobj 1438 0 obj << /Length 218 /Filter /FlateDecode >> stream xE=n@E.,MvNm M,#EPR%)SB9QPr.]lȢOLt&c&FRf1K~|U.k9s endstream endobj 1439 0 obj << /Length 123 /Filter /FlateDecode >> stream x313T0P0bCSCCB.cs I$r9yr+s{E=}JJS|hCX.OLŘN|? ?*f endstream endobj 1440 0 obj << /Length 177 /Filter /FlateDecode >> stream x313T0P0b#SCCB.c HrW0r{*r;8+. ц \.  B`W${1y 01h͇q|Fa  l?`!'W , endstream endobj 1441 0 obj << /Length 194 /Filter /FlateDecode >> stream xU-@%&c 迨 P$u[GEev K1h8&nL؃-;CFXA_>pi ?!&+R"c(ɉ(N+ƵGSroW\"Ϡ+tIߣmśh5| dXB]/qs| endstream endobj 1442 0 obj << /Length 170 /Filter /FlateDecode >> stream xŐ1 @ERxt)R-n!he!VB9EqW7seϨxAƘxң3U5ݮr 쀾"h `,T'uID x/H 9 Zpqol endstream endobj 1443 0 obj << /Length 174 /Filter /FlateDecode >> stream x313T0P0bSCCB.cs I$r9yr+s{E=}JJS|hCX.O0"370`H؃@`?#^^Q`Cƃ-Y  f $700 F"b\\\wN endstream endobj 1444 0 obj << /Length 197 /Filter /FlateDecode >> stream xڕС0jrf{::"#a e0XvtmCOh)T^ aLiOvG ֤FscT,r0ʖSiNfEN`Y9Q3pqNN3O0n ZJ4&}5ty+A -ؼ+ԀW2>z endstream endobj 1445 0 obj << /Length 236 /Filter /FlateDecode >> stream xu1N@ E"a|$H" * DH$*\!G2HQwmT 娔DJsՠg?x#Um<>r\Iq+wn˜24wC0MLNLtA 9a=tC68yF̛aO2/a<&E>oxv endstream endobj 1446 0 obj << /Length 124 /Filter /FlateDecode >> stream x313T0P0b#SCCB.c HrW0r{*r;8+. ц \. @†H0 z(QՓ+ +T endstream endobj 1447 0 obj << /Length 167 /Filter /FlateDecode >> stream x1@G(LtYY +D ,ZZhq@IaGhf'_Ϭgɂ#}SqblF.b27+e=Z3bÏB&.ْ`9:Rs)U*H]J^w¤%HRQC/~*hGo8 endstream endobj 1448 0 obj << /Length 197 /Filter /FlateDecode >> stream xڍϯ P#)>tœ &5m.b_CYN wzto,NvE69Wh .-rZeD/@sL@56Mo%n} :}v%$@FTiXz[V!zyM-+_X=Ey>J3CN.{K endstream endobj 1449 0 obj << /Length 182 /Filter /FlateDecode >> stream xڥϱ @ Y| j;:9::(}{{3!HŔĔ'tIio _Q[z>^WnEWtL(>a]Q3-c'4a|` BAI=EzNGKC8e  p&ȕ5 l endstream endobj 1450 0 obj << /Length 191 /Filter /FlateDecode >> stream xm= @ x Ղ?` A+ RK E[)S,;h%Xfh< }:ex\T:8^pVQ>EmqF;)C}FE$ sXBט^Hȃ@?|bezYETZ_q-`R!a~K<.Kj/\ endstream endobj 1451 0 obj << /Length 187 /Filter /FlateDecode >> stream xڝ= @g"#Xraˀ!N;GYg!BR@[]/w%ܔ|q&?,Lƹ+x"ҡ@yRx -0遍~*?umֽr!0e] EӐ`%Ж*sz endstream endobj 1452 0 obj << /Length 182 /Filter /FlateDecode >> stream xڍ1 @EIk9 n!he!Vjihh%GL2Φօ}g?ofǜlS>'t#k5?;2{Zd܆L]rBC\"iJzD=[5/jLAOQ~ߏ@B_Zh4J5Ϋ^RMuZ9uEJ endstream endobj 1453 0 obj << /Length 193 /Filter /FlateDecode >> stream xڕα@ .<} L &`qRG;[pqᾤ 5)+H+9s<^&|XLפ*L,r0S⺡MNMC $z11wx!"><Zi&N?>cH RaH'c ˁ:ѴmO, YK endstream endobj 1454 0 obj << /Length 201 /Filter /FlateDecode >> stream xmPE4K BBrmM>}}V́;ܹiԥS=T'u9&a+NFF⻥OK+ VZ[( f#2;܃J>PDCv@Z }•cC 7'* 4u.7mp b2rcZI_ endstream endobj 1455 0 obj << /Length 154 /Filter /FlateDecode >> stream x313T0P0asSCCB.c1s<=\ %E\N @BA,@Az H?*;&p4Aka[~ `1.WO@.^ endstream endobj 1456 0 obj << /Length 253 /Filter /FlateDecode >> stream x}J@#E`}!k.p` A+ RK E#U(y[,gǰzqꜟJz`;볟 Z.(wk~x|ws%{/xv4lnfxYDdItSn\#7@efd=`El6X4jB*`f}E_h0bj1SL̀,x>v*!*:MƢ:?-y%ۧF@-7> endstream endobj 1457 0 obj << /Length 161 /Filter /FlateDecode >> stream x313T0P0bcSCCB.1s<L =\ %E\N @B4Pe,B @d ?  B~oAd $?HzI8'W z endstream endobj 1458 0 obj << /Length 132 /Filter /FlateDecode >> stream x313T0P0bcKS#CB.cC I$r9yr+r{E=}JJS. @-\.  @x@@?C1;}pA|.WO@.O) endstream endobj 1459 0 obj << /Length 169 /Filter /FlateDecode >> stream x͏= @_#d.͟ B Fp !VbnxK q\`eW񊉣~2c!GOj .mO1dXV|-M -X endstream endobj 1460 0 obj << /Length 198 /Filter /FlateDecode >> stream xڝ;@%$p.H)L0VjiVW(x[_~0E_cƃ=2b4gA ΄Sp)-8lsQy endstream endobj 1461 0 obj << /Length 115 /Filter /FlateDecode >> stream x313T0P0b ebUel䃹 \.'O.pc.}(BIQi*Sm`Pz<7,{\W endstream endobj 1462 0 obj << /Length 171 /Filter /FlateDecode >> stream xڽ= @[&G\@7!Q1#X^,7[n8ȃW3r9Al&]'-\,cx܎` s0 n ==Cbq1 SeKvI'mr/)T8R`5zf endstream endobj 1463 0 obj << /Length 155 /Filter /FlateDecode >> stream x313T0P0bcc3CB.1s<L =\ %E\N @QhX.O$$PD2`$ȃ@H&?:7 q.WO@.ll endstream endobj 1464 0 obj << /Length 183 /Filter /FlateDecode >> stream x}=@XLvNBLH0XF[٣Q8ab^2}KJ)*%Kw4 +@@)juE]VQzB[_P :9o.A@9(dq%7@'a/=ߵG.^Tyh p A!\\[>P: endstream endobj 1465 0 obj << /Length 200 /Filter /FlateDecode >> stream xڥ= @g fI"SZYZZ(ښͣ[.(wS|7q4HRYs_8 LWCNv?$#(%p:lHj&5pGٌs V,S*7;(&A]t, -GT@8=F> $_ȥF<5ޯ endstream endobj 1466 0 obj << /Length 211 /Filter /FlateDecode >> stream xڭ= @ 4 وVVb&7J{ Lig Z 6_B޼q;QH1.#ܡ$ )ѯO-3 # ƒcM?n0O$!Wɾb|31P_6rilxz+=Տ>jO=]quBVŴ~[)D\|kse8'vG endstream endobj 1467 0 obj << /Length 158 /Filter /FlateDecode >> stream xڭ1 @ПJuj!Fp A+ RKAEh9JAqc![̃I`4-ØԈmjw쎜{Vky\Y\/|9êe_Hx+5C8#$RC\B"xo<Iw endstream endobj 1468 0 obj << /Length 185 /Filter /FlateDecode >> stream xM1 @4!s7q5@T0XErr,,2ԎgDM&rv=pr^ًYMyaoY!RrGB7 }KD#"eZSW!("PB Ca}96A=> stream x313T0P0bc 3CB.cS I$r9yr+r{E=}JJS ]  b<] @AH2`h AA~[@ Lx:B endstream endobj 1470 0 obj << /Length 148 /Filter /FlateDecode >> stream x313T0P0bcc3CB.1s<L =\ %E\N @QhX.O` $0()D? d=H2cģd> endstream endobj 1471 0 obj << /Length 186 /Filter /FlateDecode >> stream x5= 0W:oN`B`A'qRGE7^̭ ء4ؔ? ,&Q@>0[}pb*Q)QzܟvI>>yG:J^]S |-,ZHZX:^<r[C准qzb&gaQ$L endstream endobj 1472 0 obj << /Length 174 /Filter /FlateDecode >> stream x313T0P0bcc3CB.1s<L =\ %E\N @QhX.O `?aC00~ @2?Dv`N2~+ߎ #ȏߏ`` ?G#g``?A6 H@RՓ+ ɝm endstream endobj 1473 0 obj << /Length 202 /Filter /FlateDecode >> stream xE; PEoH!LUBBBN!۲t @!L@,a̻{ې lfOÄܒZrɌOp>ܘW!kJ/LnRQ;H(+p{h/ O.ok> 44W&F&R$}xY& endstream endobj 1474 0 obj << /Length 237 /Filter /FlateDecode >> stream xEαj@ dz)CB=ҩCɔdnvj:t&=$%p!:d-"zX!ZnhyxDQd}LKႲ)ֳ[{vȭ+OPy5 @U-G[;z[*lB;v\ɼHer;SHR Z88 ~Ka{ endstream endobj 1475 0 obj << /Length 176 /Filter /FlateDecode >> stream x}1 P S2Y<9*BV N⤎G(Ϥc|?!?'S3>gt#͔+^wr~ÏB.9#W!H"Px+"B I / >i`$f_$hj(D{{-ӎ~b endstream endobj 1476 0 obj << /Length 203 /Filter /FlateDecode >> stream xڝ= @_L#8MLRL!he!Vjih'({!q-6߲`}t!'<8 91 ũ piNfqJf)c2ot=̜w{@^m W÷x: dTLdO_'X`*w]!WҢqz9KU" }}d endstream endobj 1477 0 obj << /Length 141 /Filter /FlateDecode >> stream x313T0Pac S#CB.# I$r9yr+Yp{E=}JJS ]  b<] X큸7001;j?0FJ endstream endobj 1478 0 obj << /Length 222 /Filter /FlateDecode >> stream xe1N1E*i| .-V Ab $(UAݣ(>B,?kWEwk.i;O%/$=iI^>$nF6x0ڄʬ ͎X⌾T~fGvlgOȠ<|HTGǂ+ˇD5WTL3*=2,<8h endstream endobj 1479 0 obj << /Length 226 /Filter /FlateDecode >> stream xEнN0 J^ @ZHHCL @>ZlDZTe}9W|Qps}ů}PYkP|N#5[ Sj~??ScNzDDFM&4=:4WL hLVښQ5A1;,wKi sęǐ dw;-y"ͧ\ۼ>[z3Vc4 endstream endobj 1480 0 obj << /Length 181 /Filter /FlateDecode >> stream xڕ=@!$p. b&ZY+h pJLh$%^5Y (xTHN)74 U[QcL uMĄB9ƛG3a(if M( /#`cV2OZ˿Z;5t endstream endobj 1481 0 obj << /Length 207 /Filter /FlateDecode >> stream xڥ= @4{t&)!BBB,xxqFE惝}ov)ZRGk;Sʱڬ)Nюe6aܠOi(Zb>$\Cǹ.5Tº)7 P \)'ߘ'-,e$9ґ i `AY ֚ G9-c endstream endobj 1482 0 obj << /Length 241 /Filter /FlateDecode >> stream xm1N0E"4 @TE"Th+)S ͓=3uE5w|pWs/ 5gFGn{n5j+UknS=6@! `dHp糢0g0p \ύF<'"DMbLz[Zj6]*7DE??(jALP5ˠGԡ(OY*G@BR栛 5pI endstream endobj 1483 0 obj << /Length 183 /Filter /FlateDecode >> stream xڕͽ 0+- h NB`A'qRGE(}zWEq _~3#)';#I~C"cQ8|Q iT5t] '`010%p1 iBt*Rt 2;nB)4_T+~Ѭ.:\M endstream endobj 1484 0 obj << /Length 213 /Filter /FlateDecode >> stream x}O @`qM>!zI 0XɧSW؈p w3s3Y:'sÄ1P{~s8Ӵ$4'tcot=w {* (D`D:y#jAԠBQSQ]9h@9׆mƠ3/"-PIoәn ժ?|R3{6nR}Zn endstream endobj 1485 0 obj << /Length 245 /Filter /FlateDecode >> stream xm1N@ Ema|HBbE"Tj`&GkH 4أnv+4rVISJ{!Orݢ~9^ꖋknR*.PI^((`)3Sژ1+-:%8p'?, \%ᔀ^ÊH"4)MP9%7Hi/! GdL!n&{| JMc_u|_!r endstream endobj 1492 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 1496 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 1500 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 1502 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 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 1506 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 1508 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 1510 0 obj << /Length1 1626 /Length2 16093 /Length3 0 /Length 16946 /Filter /FlateDecode >> stream xڬeTܶ%; wwwwwR8܂;w =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.+տ3kqW%pr2_`@FV.#;߻/俀Xhl 7 뿺:Qw5q0i< 4CX_dd6NKB97iց2#xՇ15}tx-9Q/iѷ鏂ʑεc}vXUՌ`f؝i݋(Q2њ0JΩR~=>PO@'Sz}uk2~qvӜpsԓ[gJ2oVݦ aa_{WL 6(Pu] mxx)1E31*baZ&ʲ3Bޓ#q[3m4軩?қiQ nO,J7{:hfN Idsתꀈ|>B oqӱLB ;d =S>od%o;|us6lpg7Vvu,k? >WC𙦞 +_|HقY^%@ȪSt}8#'69ޖܡm|}k‹S%lF:WEe“M` 7hWrfeg8ǶL"\"w5Vn=Ap}QL:38I" j">2FFۉvs2^m !6R_㘪d,8iü< l2ú&湜uY :M /9z}3ЋKjMߵF ګf(n9 3gv̨.kWmپdcT8Iz0%X % cڄA]8;鉚6fH(#ن@Pq H{Kcy@/81:\|8a;7XW*L]r% ^n_C  +W#bP?Z'to)}D:zOXE>rKVzlD(h%JK඀zu'^Zӓn!*Y?rnLyI.a8br+n )A}/,޼\C*UBAfExWsUOac·`XC^, -wYG!;-`gk^ axb{ v&oأJ>ڀZ<}QlZhg`lF}TC-sF,gbˠ㛢[µQ=K =Oo-Iu9Іv+QB MVӷwP-5ER., VJ(K?nSWi npi2x̿G{'Ev呻t6tW!]!(3ZHSq8`jCM'Vy|EY| ~3hP3=J_hj OG7F^F4#"yэ ma3W2ɀW[_qtS^LGuPWx1 DWܓȷRAk:LGCmRW1iR+GA KRJʅQJL6 }^쌯@KHHFL{u6N> /9BA|ȱqRxFs:D}RZq/_&WLGW78(Egogkp-OFb؞H,6!zf.iM/2)w9< ƒ ߎ> Ѯ*h[\p<8ΟɬFnKP|ͺT] Q<ӕ Ejm[sоF2dP-. ji(9JYL):A! ޳!=Ԃ8l@ߤ0<^{e%16s>.:nS{{qKT33YAPuluEzz=. !o/FWDS*..Lem*FƕqLFzu+6 lC=8+ Mcݺo>x2:mچ/gX{"~Sdm[|I*'hruwܗIGж3| [7QmI{ùg`4OhL8 rj1e,ew^rn ^0Ï@*-4B.%̫쀸W5BU1}e, TǛ"2t)~7 nNE>\:ovߨR$AB.TN _Ie=UpiؾSvB$9Ouf]k'iM͋wRy}A ڛ9׊JMPY8Q@VY/r6&KlOflD zDIJm?Wta C5Q7f%23X.ZvЌxmcX'Yy}0K+w1"h&GtȡDB 틡U$".0wyed"ČRf:iW~iJgQeNDH̬P*|9Ħ\=?t^ȳ] 7;BCDz߸cvpo5ˈO3* Pg;[9ќ_cA+~%_ލi!zݩۣ`alzw-T:~gyIK5〳y1|qLgk3RS~-پ_{JMYmzA¢̋ǟFe+G\BC}w&_)'"6õh5mbG7Q&N]KJv H.ַz rY p cw@hpr>>nNiϒV Eow#4 ;W7xz7v =݈{3H,.L6,Bޅ&f`_;u: LKvyL{(9YP;L<3t$zqhd: ҭy #AϿ|7I&^Q{@MV#UL-t)F$f]ۇUpyM^\7r}$O3~]o.`^|lܳ- +PX`T>7+۬L%-|9`aTTLç4U>O7T 0i JXj3?l栃bkXN:/mp-)Jy*/Qa_YJCjINS^:5RB T=~t}=hQu93 DM|>$M Acj#}= Nuo6raZC vQi .2Ga#_Zh|o5(?"@$xU?|E S6^||? qc0̸K}il3:kS6U..Ӡ`wOLOpB u/^ Bf˚0`T!+Ur+:mTFb{:vW.NWgV>ΏDy ڼm Q-t.D-c/W뜐ĕ"UCU޳hlgb !wD#p~aֲz[*tkFw!U[7%Objw׍UAdKCGv-P%=oUd{QKGѹH}>Y 0UÁ:oQSBk׌XA, W#˾>w,._YN}ҡd&T@jVUt? kUg?ʳ9bFW#y^\- D\o&[fgv婋HTt?`Eirwǻd(Rܚː ZqzoˤNyLO o]H<@ZkazR|GAach+إRzAkfBaZw* ob“sP{M -KiI1@d@3֊*Yz*/z;.Xh:c2H"&?FM,jy9yNZnp? "~Tj ,5o}K'*bÕ%XZpnCDTN?X0 քFzn:?~$ k6:0 woFq' ~|Ġw#qi}ϋ-9b?8,։W/R;i9Ӥ{ma}!F6dym)N*dp–zA+:VտE"g= &z]o_V_Ns{iU /k!:R( ؽَìT(oݯ1NWyWƍNTaWkAq@Ǥ .L7ZةQh 1bi+,5ڴ =jZF _vaJ#  Bb'.ڇ`j;eL_l[2]ޢlq_p_6c;j>2 2g8 t/D[{ͫCZTAtQhG;MW(2Ns(c$C2f~]AI _P|i:A 7 l<ωقH|lٽ%RbCnF^{T}/*#b19kBoHv3@Ճ\5;롛W.B"ӑVͷ1ΎŻ1YYa!cτ Yn' ;DFoJ%f&Fe1~߻̸=QdC!mhJBʨ' 6ɌG}` 4J*H E<u * 45t֜[/Xo 8?[1kdzi\'&j{6MevrYJG%̀BLنN\<[ɯ*k'Y~~L'k0V7 +K:>pfhs|`zM CUԊ˟_V SjͯCQa-KE槙!iPb/ȤA]aDmy2y/PqAsui:8?b9PSu~k2 ]P' ͜W1AϾ;hF-1FQ R@?.lz/*juɀښQ$)x >`{ki\Yף [Υ3pSӪ *7#iፅ^iWw@>֫ JmmYtN 0US+*dݶ0IZzqFc P&Z$:WiF_>)Y0]+".$#dw3GIhK <-~3{iRrwv!uRja}Ao Z5lkְJNxKE˪xJ?]Z;x!RT195tQH܍l$++uN]3*[n߹c&7_w\ ¯ȗĒ76q\lGlS}~mZ?PP).Bza&}̐VhVΰ8C = CdWm7,p`k#:~J8J'Vu}`lJڅj{dJ#! WD+rV km0q֨0V[p#ЯyI]}#{/,(w& U7:4B挛SG;Qd^pd?8yx[|XaɇlD-5PI; }V]y,wv4RB=s֩(Cef>{YIRO_qބP}Jcna5> 8&8rAXbAZOBBS{|_tc_*JuL&Ϲ>BBL?_hgHy&69:|т]ƴ3 $x,# -&ANS=Ƙ,v |d_>*Au7;fxPi#9|=9˼ -'Dҧѥ@|{>&p.,1ɰ }>E!/+14?sK6P{٨0*Eo͏@a}RŖIֱ~YQlL3Z 奄R^G*y6QUPۛ䇎I1Mq8vč;9OO5TtEiisP< !$ձ7p)oZMźݕ }k7 KK Pٍ9Nj9M8w@ npO,҅?"Ę$щy%K|Um[$cC>~w~rc(Lo4?pMc)1RcZVO&Z?| }O_$w-=5Ѝv Yd6.Ud37!Cdݩ*>ŭ#WSDuȨ!Ro0Cnz #c.| W7 72CsCmN4l j`?lbɱs95ibx?q,B@ D4`*Qmw!>ݱNgxa;jiayYO|!i\5 3Eo 剩hD-tB2?uNGu&}3ƴ׏oLk;M0.KfJu5k` h|%--B,\42=Ǭg7Ka /LK~9IVۣXf{O&EK! F-kFˠGe_v,xwr 1v>+RQZP~vU ຌZ9.tŀ{J;ss&[MR@2\;%}5^hCY@r(WF=QGC~Q5䖋GMm]TkTHBP -o"4*7b*[F$ṏ)zC3H@5JU|Kf1[nT@ow׀_gVm2c+ gHFYx}|ɊgnRo+@/#:Frd J,&;-j$G|LOCbK.Rw{K$괦X6'}3ܤV] XmS@(%b÷%Շssdv!Dhe'ߚlzűjnxF믿=Nn\^"81oW7n,*/(erSV{`,yB1#%(0R7(e]&&aVZ,|F\pj>a]/̍9n:OmQe&<7%L#ALj?/{3BbUih0ky֝g(R\nMR.H,djigg3XP'`i~YX|&LVw^f[ !7j:\OculٮO*=?_Mcm:щJ[+Zspp<^oa- [hAF4ZѦM'v{ a3`* ٲ 5iT_D4x,LPS;T ]XhZb1-q rsA[oΥX͔m9efYW݄j,椾d%Ytrax`h%QgO >ZžigIs^@wjnNJ{t+OYUZ Lqσ(9/NTeNyRI ##j*7QDҡl?:a3m#` ӧ/E:83>qdvql;`+I#i^[2\}CI^d>%G\޸#=d! *|uNHNXwnE&j&-;fYKOYT[ &0R,7AB{#YOwƂhI`_ !oۼg 팍d;?C% 'NÑ0~#PJsz#T ɧIrDq;pGh_:^<{i6H?wEm2ZLRsr!ѓ~D?{#Nޤ XrNL-!3T@P9e2ewλiCm/hHdYM+iAPir_ׁ[?J0T_ z8/`A4᷂@;+G$7Nogw\JͲhї5:F+ZM6k6>XovK傽QGeӠU%+iN&*P/Xl %_y1UJ< eb{ʃ#![^Jp!~jXKl>iŚ>l̏E2"X|fGF;-?ӓY Уt}1ʼnHa&p*3 (Iy/Y%%$ S鞿(>1E*5U)lYb63i.$/ͱ;)[vcxYbECpL} 6׋K3Eb8QGZ+bUڻ0d̶4^l(g@ .$M[?~b:1"ᒯAWP BCkgdmf[|!Ѣ%|W$U$0H0Dm:nO}'Sαp.3np>q8 i[Sk8閛?.&T3[{LÿkA 30r #o!fY$\!Z0/Rl|n}l>OLRz[2{XJ|?13k&syBGUa0LW}FLVXqfCYK08W]Eփf?Ne؜f5,',d`1Vt fힸ0&n(XBxZNA}74-G$5܏AGC|"Ƨ IDwɑœ9:3'gn}ozqxW(C;dkjy+wc0~B,[F >hu?(QTyBee'Nfp,>d詸\B,(C[?{JSΎ 7Q#_ 䵚 dn.ߞQF_2ۀR;&+wJZWseaO圠e " zsZZRs(%nm2k8"T>DssJlI!&~ȥ)Y6:% k0 _8iy}{|#5٥X)vkz~J)wPU!`)*jp޿ JTWo,%YC}̪旂v=i@fTRoĝU/+܉WM9ժѩXvo,/(sRW'/-*mRgߍn0_*:]Yo~Wd2dSn2⛪]0-FkW#aLlYF{;۶lOgnãr[| KVH  (4{AfD=; ǀ?y\$8 ·,a07rU##;Ƙ^)cBVO<4dkN Q!_s(ਭZh?KH/o ήGYpŠDAXoKf.[)Qj88^KzcKDhݴmx0(%> GZ9H+%TF)JɸM,lʛ)80٦M˷SJBn."p5,1mbx5?t㔩:c 2}RugjV/n$ONT+|}DcI(v`\'3jRUs Tsy,PeA_v^&e2\jOY`/˚>*i2r$`œU l{%oK0/cs?tiGvOs*|[z d[`"xI?% ^|$!L CŚC齀FSx+)dYF7TW"? MiY3Wc!. Ji+ nx1yٟ O՚qM`CIWWd̘ЬVhb/>>Hd1ID69t,|&xh7EBJ.䇨}^zH 䳊nKt.}HrsnyY./%xϥ{:{0EF.d[gcQ`v79T+De%Lm6ק%3b9`]}AGKH%:n+lGɧs;%Zm7pkf䧳R n1!c{ ݪY9ugJFߊe$Ua*DsR'ce 4 P݃/דh`ztAkV\mb:o祇r"^i| }=$>|=^;;9ˎFPynwT՘mxD Os8m~Ts P֐j9u]Eb_@[z# #cCy jN^O9rx A٣5I2eCЅ l D'~ln[D(ˡ]۰ S $i=FdR!F"(924$ܝHrVxF,;g0$L |ȥz3d\"TXް }X[#+j6]Nؘ4m)ʩZu$u@7,fN^z#*D]qzgc\Go/(q\!(9Po?XxW?s,Ğ8AL"d˯o)j,F.5`N%pD +iÙ豯`BJ2ȑ\JW(jsRTٛOuRQ5mRYPkX>j%q_cDK8ÛL汎2 `0%Fte穂X\d.-=zEKH^H*^6SCȐT^סA(A֡.yK(|Y6Aӻ8gSoԺQ4l @Yy¾vl3n%DWV{A$fm\M=끗xoW@$~$66N?,=EGF ƈTk{v_])75 ;1)9g9:(|2v*NdZޑ3{- {U%gXp endstream endobj 1512 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 1514 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 1516 0 obj << /Length1 1647 /Length2 14120 /Length3 0 /Length 14969 /Filter /FlateDecode >> stream xڭzUp\˒%Rl13l,[?sΝϼ]*WfdD*tBfv.tLy=, c##q23rۉq4Lf&ff@ haTS֠-c"@ ;烛'Q bi0ژD%j 3;3"]m&Y `Wi\B# /`d tv|NFv.gbڙظ?w~bd.&N@gTEQbiWlg' 7ijoWIc4bW,c3)3'4\vʀdfadjcINoowg@g3sz8&Ϙ&.-vp =vSWbnfN_=CA3$2 "ߩ]mll?s9h k98@h9n d#ŧBtLll0Łf@KmW35sڙ}~:12j 4K @fv^çnW -+KLۿ7+~v?"iٛ/*aa{w;2~^G.fVGؿ3rqtko4bv&g/Sg\} LVMx2]js&DuzBJU w~V7Lqz,rx;>N1!-@$o9 d/ALu Ψ~;_ u ʟĭ$.ǯI'Cא_hr`x}"Jv0t7y|vp腞J`Gf/R9O^r4+\jy9tg)Gy!5 B9GfE#_-@ɒq&~caj#F5^AU#1?{eb(;NN"%`tۉdܰ$ M0gB)Ùr9V7uS]s>R LMUƷ"[8e9k3!`I9Ԃ/ߕO!4bcwaQ ^b53wůؿDZccO/ͩȖR˥[F(!K.[`Da4`niyM~}[}-Rmk$%;WCgc7>24"uAV"']]$T[gfD4R$jvXta^C"X4ZʫfT"ς^쩩"2eΨVik<_4 5"xr  \0яy\e _a @v]r)Rãr,Z>UC[ֺi_)`>,ieܐ|33nR,Mh6 JGcf^lbyURhdbi9D XU g+Y4y'ٯ"O; !tł}X]j|-Y%Sh~$\t@DP(ĽF!C$#gc6x䉜߼k+.*Mq ̩ zRidCVjXgI~#.׮dKrHuXH绅˒+Uc2*oZ(Z@ B[f(}sowK!0T ِwkAɂ3ATp#lz!Zg^+콜E hSYu_>;4^Ҷ|0:7~FRSLVʉD`~=o\F,VVc?J47C  K=a€Hsf2 ą } xEԹd3SfHEY6`_JM~ ^Oԭ`3596+\#=60uUt:utӁ(a_){m )I뭓j}8 &[!2VA*V 8_4醐2Tfj\8 U (B4Z ^4V=IΥv]yg&JW\$c/*Nl3E$K+ öU?S?W,?.[iK[Z mX xIV7 FXB1R_=X٩\`dK,7N\Qx2uvNj/J䱜d';=}sK~Oy\&SD@!7<zOMf=~꓉fɠMLHXU2^fuD  y >2jtSOU`g%Ի…Gڸy yJZ"Ab&1#⟜TdQk:֎Z+HVnrU})֌/IY3ba5@G`rRDm Zn!WA8&bay @= Uv=4LoS%r7ċ!I}#@R9,w,(5 _Yؐ= wjbpU"1#ZCucJtimL}D=8 q0{TArsB˦oy=9N 6ѫRĴbT[_18/Ӈc-1ݶR+x =߀!r`왢2< QplZ_&bgnZX=$y YX X3A^mC<^~gr)Cx9)bNRC1~Q]R3iry]"+(D։ܾ6؈eHP 0?pq {&&,C1̐n+l儩kMK+FVۖ{YlHtgm|ϴt@iIM6XmRfw!@֯3ԅ '?-]~ YM_q{GS9ڗW+VN}bx- 1OX!ve~`Jx&mk ,h#J1r5h N]m=-_6a JNA/)U\3w<_'5U|}Mz[ebhoa$o&E(FWjK{WCbÛgF#[lM~dϻlԇ L=iKW YVT E? M$BTׯg:Uɼ4!m?hYb;]a3Z;[pܧ91DVSFlizNHd],2ɴ*څ~Cq-\k 0vI(<T!05n9|x8Jv0>RaF Gu("D:bQ+n8%-+2Q*bH6dj;fLpEօy :t*ʣ4+4i/? =pV{k'Ubܾ(b{;BY>ɍ˹a//%\u8$$w-dP{*V񢴻w76/$&#ئ|:R3/ysYYXةA'w^7Dy %ċobѰNlÖ]F5A~)|F˗bI6g.C,+o0\nzG4tr0K^[IJdTdrdP2Pq@v+L˜ڝhpp ;o[ =ۃsU; ixek&.A2_mI,HTTun0ۀ8L g2Pa^_ǂ!pf~wV.} JP[䠹ĈL SV9\[pL0ѯ(Թ#.π ?LҒWi V'|/0/Phw3L, ӉJ@ PzytZ;͖^?qAbnjTU0%Geuv!vѹ)B?rQ|@-9t3e9^ ,>Q^֢%&Cv{Rߢ;)%${jHY/x$yLo] o@0T;NݧJw&j_%]"Fe)&tnL%=*}ǡb|S>ʼOO J4K'')zժ\ݟœSUL+-.*5m(\fRNѽ:Ec TtmYm|K?@Ap&}ҭÎD/?j/ < 9ї='b`sDVt/ylԺM_Y{:^dlrHk3.ufRA¶P"ZGOr+$MqPX4\sT'`G!ڤq`\7i kY.eɸ~ %.)͠yOw(8b m}HLZA[0;Z^m]d~EͰsSػkp"ٹ A-{vf2cAW} 7C|r/ڗc*CMe'M^;QCMb<=\9CK'yi͔67&oT3ò6Ż,:I Z>Wn*Oc5 x2Jy"BBYG9=߉fDdMX2sȽ>bfMwC}#s'?{0?vcJh~P/-t!u>J#H`'u!IU ëhX_1~X ڝAuV{UƅĀsBjV_k E81`,$x3t|k.+imFFd"g_qO*C[" ;/Jbq,~f0p*e_Z8j,8&=QTbQ[c Px+ hWH3,C4l=D9t53g%nN7g@:$R2Vs.$L/̣H-97n/eBωz?Ni/-HE[qlh:@bKKneS/ zt1xn"2-/E;6J"cl&ԼhPx*,#U>,d컣ƲW ?8 =SPk\y7Q(uX ( B.\3lZِ?7p̄`.0߮#Jptio,@̑9!>"!ZYEۈʳ0ϕ$kđiX(fxw.!9m5#Swi htXQyU`\Gv - fccFfjQgSCgŵN֛ZF ~EABfH a 8̶eT(b< dc P\O؜pRhhxQH  n@&>7 R5x%/w8fIg eUowuW} acyUPJiw{pm)H}Ϝ?r_)@<B,>:sCh e rSWwFt.rrͦSt`oULykj\ h tjdMb/1; É sfW\go_ M{Q 9- 8EtҤ3⋚zdG7ﺋ<.`>쀵 CS">mM:fmB@FaH"Gۏ#ɗ œO!$"~naB?ѻLXIC (^(z%rX;Pz yd`xi~XLY:҆v!k: Q57 PIzRUi-O AUBMÈ\@¦R%E$aOlID$xI†B5Muыe@2̖N_aL?W2ۅnu!H2ASﶅvY#~.McEf'渾{u*ts_3+;(p@^ؘMDt> _+>D_Uzg<_X2^ YH|:~mIujAʢ; KrCOf1xRܟUw(cnKa(^a)\^G#>/&ˀaYqCYc;Zi6wV* -)IE'{#5$f"b0Ԥo]x>xost +b UY`7*sZ|Yi.6eb+sSLWPξ[8wbqaDؑtj%kMItX >I1l+IϊϗQiӠʉg8so2j^R|oEm揪=\ru i6'+ 0D \4+޼Z sۆP -iW ),"sG{S_'n8o Npr|R%9L:v^- %eGFIKl o,%rt5K#C/-:r)ؿ1y2o , ԸMgiMk REB)-䞶Rz20i4cF wQ.ܐ##k$RWC,$l?!wjGY:]ҨҔ DI9bj4jfXyQGaRepj&PzI0|˰c`y嬆@Ԣ 6%Q|fٯHsX>z<_(&3vt1ek> n3H)O^ LnftWLt JÞƓZ~h]CapЎ*1$ϤX Gd]/0P_Xl ʌs}R?b8.8 Rؚ? ?3 _`ǃnNEx~$6F$+ LԱ^(nwA jߎx)Ƒ~0MOZ1$1ړiޚ}&.wU\K!a ƉI,3x^**.'AF2pRm?ŀ#mspj)!N$hAЍCNdJ{3>cSNlV={ N*. %5Vª0KcY'I[ CO, JoٟAdj2(" 1$Y|V<҅J[Yl|Qow2WبlQjkfp[:2,7sHORQ!UdCc46|e86.cʟ$? _nZPJ̌YH'`5<4C,3v=DM^Ig 2oֳ-,E@MNRfgyoN{H׳:YW3]T0V9x+TMd0ߦqGo`NPtV&d҈/UvK)9m89o*kL#&fp|eY1wH4f&s5{6w㲳Gas–{"8NPq-5!f?xdbe+=LЯ9G_x|iBlJQz:) wXZ@F]b_+<%jqQIb8`\iKkXn4E[O lg-cmx Z1^F2XnY3!ꌘkx!t8!M/m{ŋZ?y͙C126~3iz"+#=#U^8OQu= Kq D]ڢc;ND;cjшi 5666m¦$*G׻gm[)պ~ ǚqXxwqeiɂ|%$erTG)$WTi}`O(dv#&*D./4FmTHY|Ⱥ(f3"$hRN!3VR`̴2ѡݕ:w,VE gю(#+ɳ~{ȯtC1aeM@\ /;#)2c'I0RH1VeHS]&ڠkZB "wfzj` 8lf8q8RU<8i cTȸ^- DUVzv sձByOʐC\,2Wr2/P!o$um gdQ WS"ʡGx.H-⦼ \:ț]A&DEYӫB/w$/\:ũ7}+4 :c 8 R(ߖP[8;072, ʻ跋8OQma֓ZTë$}Ns'TS]kntUtOa L.X'exE mcYzh yc.<]݌%mwL&. Œ٨[uxcC"WBx@ 6eD,m]x_)*0\䒪ٽOc=~!,>3O(cZFmJ> $nlɞ77\>u|\_&mھUuo!V(&;Lh< Mr5׳nj9\"rP= (huW'߭O/IR!o/ bwxHCBر p-,Uc{[^v'@m R}B,~wXlx!K?HKI[")҄*'Iڡ ΔKn<1F4 7ڭ킲=Q#S#6%J 3vx86r+HWTX@귛O}Po႟D<qz`'K7%fpWJm\:X ˪߀6u>PqCFܽ4Aꖙ&^-JWcēBpFg"eړMڡI(kPS0ֶ G̘xi$I^up|` 2'dIQnvR@j^|T9f9DFiiț#3ǔL,b|oj$ :Q ?>O&Ə1e-/l̐ldl vc\}qe] 6EOZva%?/Twھ?AyS%Z]sپ4 OOfG2bAsHXs"JΎlay<CK20%NXv.SWϯ|P23w&%B75B=I/q/)7ÞNdk`Ik\p:$_YV }͹ӏ ؁֚&^$vMQf j CKeҔfRIΉ( M 5xa^ȭx-Ca^bDt7iWl-*%y@PFH8#X~MjSK3H:dT@ޓ- J;%w m\ |K{Y/hx둅g+>9<(N\%+L>'gA ZQ'hm/u{']6MsTГrҧAGB)2^0Vh]rػГΝ#³_.*^IwoWT~ʎ $3CdP.Ǥqf@uc7NGz\1C`ZЍWiOpHwads =feХm|~Y]2IqPm@|vQ G jT&[^d.6>t߸څ r%bxv.ܥȞmPVgnjxӰw D`8iZL O#k}򠜟ܶ˭PXLB74~%Ju*e2%⋁ɕOm=a9'LȀ}q==&Rtx+(-xˣ'w ه"B{r}+5m H4֧z;л<`sJE X?oK~9|NjWSies!>f='*69ur^6@Q Xە JvL ; !eZ%;#It?9 3dSc;onchlҖ8xeFL{Ϻigw=VDD}.ͩǏjԕ|3˩mP,[`p]~_#5v>Rgۻu.‡A3݈ JS0S#$GbBR~zQ;@F&SI6$$`0qf_OnnC(rK` Y^pr5Np/x&e^ez:^hArA"bSd46s66߮>eev.g A-j#k>z4ӃZZ85r^ùF-H́뉅B*Q: <B&% Sd|>KJ4>'!lU7d$tSrLkj́ endstream endobj 1518 0 obj << /Length 494 /Filter /FlateDecode >> stream xmMo0 !Rz|UAa۪V&$E 6~=HUAgɯ~uo$ƛLD- t @ZcNt=YNk`T=Ro æeCڕ(>Պ AiZsn[6uc^0Xah\je?0bprOY[AKS|dۙoF)MZ}4W@{YmG;<9`;K (EytbabisbgEjq(po$}Idon-p!J m-O[L endstream endobj 1519 0 obj << /Length 696 /Filter /FlateDecode >> stream xmTMo0Wx$ ! 8l[jWHL7IPV=M̼ su;Uٛ=w]yil;<[[j<=?׾+v`&ߴț<^*;~&Q>MS >_P{=s@dkx;`VY`s4JaQܡn.Uu9\Y6><ٴ.Z.4>Dӗ}~r:-d0VWk,8yLһʮӮђ[*mLr?q 5F8@=@)& 8Rx uD\j2HV0CzL] bctI g$`htы0\F0s jd< I6zg W qȐ+#k .bsrbmXK7ǵH7Gnb>&jؐu1VljOu$՟qWS/%1{\xB!K(hHTЖ枃Jρϯv=k2UKς_:~$/ ~E+7ˢ/ l(/} -+ZXukoԝE?ZKq endstream endobj 1520 0 obj << /Length 739 /Filter /FlateDecode >> stream xmUMo0WxvHUdCmU^!1H#x?gx]OTm$|͜s_Iss :L;<Sz==׾f`*_`ɫڟk3'iѴ}=M;7rfnj-eSӵOLg~8 )ok A8 $`I\3`Af<Z]! xNky"7 _㓧q H`nḱRONH=CpB:# =%888QA~!*zƜАT?!~> tw8y*sύ }nFE>7*QύR>7G];~<6OIyktg>O:yұϓN|I/|yIg>O:y҅ϓ.}2 L> stream xmUMo:W5?$R. d9M eCkmCp;;w~>|3E_?O]5߶w]Occ]=~?}Oyh9%?۹׬B|Ɯ>);vw%g43>\ 6 EJ78 1{~`W(-;]%=xe_,b+-O;q\L}UI--=BKE1p[! Mߊyu>.N5K)Wb٬8i[_uʕMzQ)V(Txޢjy!Z2P="Zd0\ÃGR\).2*Шa!U,H`+j.5Nα@VK-x%3%AYӀzΚ>kP#5m0Woþj.ZT$X/)n)#Wo(oRZ $Kp4Z-b\1ܰJ P"GXQi/8k^Zq:Zs9dB )sL-7xJ`aɽ)f$1 dъcCZC<73JgznHȰYɚTa,_-O87}KԴܗLloK+gJ.GZyVc48Wt]:P~`rZq.n1] S/Pu7Ue:?&?!d&1yHn5)yғBx#1ޞ]Go׏M?X endstream endobj 1522 0 obj << /Length 1022 /Filter /FlateDecode >> stream xmVMo8Wh҃kTHrضh^I IJ!ۇf|tǙqV}xܟ>ڿ7]Ocp{Vc> stream xmUMo0Wx8T·~h[ ۍT~3r#_)9۞c$_{t]P܂~ݣP_(&w(R|vp#P)->g_B?q8SG AC۽[ia߿{2ZE_cf/1/{/4G+)bUUwkuTO4[@ 0@`%! #P .w)úp%KcJe Rͤ(*1:bDDR@ ȓ2UR*N)KIΡԀ0CS,km:5Bͦ&[Y{Ł@꒩)NMvSpJs}irphS ᐙ2L9ΙV}yXi8'z Ԛxq1GyלNZ1fXt:s0>wpVR.խr)>1qҾKvHX1iS5rM yR6FBlH>]6b 5&5&0a'evb_dfQTtQ]zK/WБ^Zz&孯ӷrW.&_rUOz䢓n9)C]!􁠧r7dE?_;~T?m endstream endobj 1524 0 obj << /Length 700 /Filter /FlateDecode >> stream xmTn0CƆ@LE"jD;oƤjgy_xN{qV'wC&]\]]u>t\qxں7ŦmN7isƬ'k~G]?ߓ` 4;RV_n86]{̭֚u[sfߴ L:?v>4|`0nhWu}QE KU=5Yw߇l?N6jwwv Z/բ,ko{&PaffIq XMJ0LfhrdĥP> stream xmVnH+Ap 6 bcW;lJ>S]WX`U쮮~pF7|XaW y;nn?=! oy?Sr4D= / $>i 5|4Pr %O~_RhPqYD$qEI]T'؟oٿN x o~&_4/͏y8$⃇ "Qdϑ/}'}x<B{JM~înz RwɶVaL yz^|5|u?v %@ !P vh#/&%`Pm(0YJp(s9HKV$ @Z1āBHEiᦓ4("ܲVT飇.@E-]ݖpUtI>Ejypxr z m#)u:+@,B:6f{b\qeǂuN\슶̏R(ӞƐd7t-3>Y9ۈOI+ȕ״%>Fl)qԍtSTX8n9Ē+p9\/ڭ p)gdEKhe]NJXX.5 Z2XhX;^1G8%jI,sYg]Ò2⨫ybAOWOKN+QM?.<^+W.<\u>n(~t?\C endstream endobj 1526 0 obj << /Length 997 /Filter /FlateDecode >> stream xmVnH+AMyثL,R~(r]]n7W~c7߽p}z\[{%8OeWϝ\'}oe<]8m|kq7,r[|UJ;P)ڮ:U8 *&~(Ia7~x;~z. OM~?/,x72 .OC+B89 :u m#)u:#@,B: 6f{b\_> stream xmTn0CƆ@LE"j.RC~8iZ)Ayo7?nkNy$냛G׎ծU[7|SlfM[kwʽ5g x=i6;RV׵_n85]֚̽u[OsE͡i P{ LՑ @4=tb/yVvL MnݞArjwf4P׏ީFT]Nrî}sBZ2pmmR?\rs<, X#.KIɌCH'hjmJIQ09da"2rG~\5hגQv]`n @v)(A'b}qHI($ux-JBJ!^I :ggM597F7FN}Y{}&Ff.pdk_ ΜN0VG9ʱwDK4X=CaCɁg2)4X(rb0/s4lƵǮb]ˌ[r> stream xmTn0CƆ@LE"h.RC~8iZ)Ayo7?^$ŝPIs77EW]}==硫nTشxGɛz?{k۝=` 4vN߷u8NM>(s&`ywS0jzQshz+&TuS~Hxqq`P<+ OC톦}SWUn}@`T;P3qtj}w*5UWSܰo\ze \[3. 9ff ؤdF@!i @F\ ` H sn4ȶ` $(Ng 2R0zd9#Cb.k(@.0[Czr aà8SuX$Q:\CAfpGR~m%^!N%$h&՚R #ƿp'XϾ>AI }3Nh25gNE'bkkؿs %|V !3?fc91ӊ9|u 6ZcWCab d1׮eF-9Ag깐3Z=I= 6-7p?)pegT> stream xmTn0CƆ@LE"j.RC~8M])A̼7W?^$PIsWWEW]}~{SCWmݨMi7mv9I+ڴg{ҏÄ~F )P ǦkZn;@1zz5= 7m=x Fgu P}?i]X<;k C톦}UYoO} A`TS7~wpjmS!詺]]ꂅK(ew&97\=̒5⒁yAa>:M1ȈK,x΍t,@F*&" C,zdWXPv-hakH/]d"btv"gg?|2JB^G5kdwt,uVT Jb9;kBX!00a0bw3W M";\88̿9Earʱs ށ?c>+q p~PrL  hi˜c>:q-+01~k2#Ϡ3\OLqRυ>¹M \)s9O \Y!O>\\/Au*[ӺkzT%C0t endstream endobj 1530 0 obj << /Length 700 /Filter /FlateDecode >> stream xmTn0CƆ@LE"j.RC~8M])A̼7W?^$PIsWWEW]}~{SCWmݨMi7mv9I+ڴg{ҏÄ~F )P ǦkZn;@1zz5= 7m=x Fgu P}?i]X<;k C톦}UYoO} A`TS7~wpjmS!詺]]ꂅK(ew&97\=̒5⒁yAa>:M1ȈK,x΍t,@F*&" C,zdWXPv-hakH/]d"btv"gg?|2JB^G5kdwt,uVT Jb9;kBX!00a0bw3W M";\88̿9Earʱs ށ?c>+q p~PrL  hi˜c>:q-+01~k2#Ϡ3\OLqRυ>¹M \)s9O \Y!O>\\/Au*[ӺkzT%C2t endstream endobj 1263 0 obj << /Type /ObjStm /N 100 /First 1002 /Length 5698 /Filter /FlateDecode >> stream x\s7w+%*U>b'zEKTH* I/yec`pFw#!b i4J"cTp8fTl㙵2V2 j*F 9AUXhc1ˆa'x%M$Z8D; xHR3N!G-hg)c3Ǥ6 NS\L ꪽv c%=SķiH᪝L؃H=!4EK LevZTo<\0+A9ɬQـ5y# O<^[@W2-@,/Fbrh,b[{l4Z QV|c801A# 6"~B+TAݴꀲ(TA݌IA6;l&s`YY:_aNHq EeFjsN$\KxNI"{MB070Y[b9u {[ F@|t$"pljKgDo`I,lD ` s$h |́h-OIKA>DKҔk*Ѡ3z ?ۃ fAwtaˏ_d6{D,t'+Nk?1|Жƅa~˺#=>t2_:_FEkFڙ0(azD2C뛏IniWB}E LIɱ#Q~Dvh{v^L\8;MU3`'EgIc7/yLsm"*At|wOƫOHD _Zݽ;6̲C"E/痋2`Ήwbi {zi+bK}Z]T.6yw(!$=x,VSv˾s3#"33㉚*zyP픍Thz㤺7m$jy|ÎeDZbO zɤ?b2k-85P m7(H,5h,bҤ<3<ʮ?Ή[%G4td R#E{ڶ7el-v7%ҍ~qS*0bp(ze _J7zoz&mRj6507"oF 4Wkb%Ήk8Bm؍"kw\r̠(u̒biU.%Qlh1Jyi)D=<':j>%G0}ph|Mã tNbsS*4ҼpK̴]3jqxuʈƫ1o)SLw+SxmL)cOx|TPųR7 qzoPYx8Aeu *[}Cnom 1!(hCE*QgܪDO8WT]}.a>[DYtn}:?>]xO/ާӫf}z;>xOg3 j}=>x Hg $Fh8FuA[#%u>jP(؈ `4b`41)F`lgCMةX)zˑl#;xZm,~3}U#63[7' mv u [?h&wOv})YBp km: tJz2 mvjgvcCDR똮(] -m^,7% #oc=Pno ~aƕ1xnÕ1nkVǻteO -]#0t 8n2]0t jf>.ԦvQpfoŽ~u7aRp)klFJXlkL-}cfj3S+ޘZJ6fV13ҍiLA XkN|ctjbĆѩ1SW-^e]8Y903n+t5}7ae1xԦ=Ц=fptBх6 mG .]h38vptBх mG.]h;8vptBхv G .t5X-t;Ⱥ?}5aa aeK ZhTBZ!6U~PW0Km Ő]ڰ&: L-+P\E$fm`R( pmp&Apy\L-/phEj~x3,= 5:oVE&ݥ.HC& ]V"֋nXVqԊ_dW\;eInY\QaHLcү_:aQձգatv櫧\=󎫫\x1S縼o[_|u<ť?Y/"ɩ~{swN/ƧK[CE۲s7Ǔ*^|7~2=bT$xqh9~XϦwfg?19@gTZL0=XB;H>xy7o '?B CuHh=ns,D ƶt b1#g+էIaԛT:&9鎧jzq(;}|BwiD録j:E!Lgl<;ռ;.]x69O' .HBt[Nϧg>{śϾyrtyv<~FZѕ-ߌiTN}e|޾']8ҖxCp>f=1ư'RVH=ޮ.}zGϿy:=p<>^nI8l>#,SN 8uIvMtݳn}莻I7κY7ݲ[upKhm'G@C/KDѰѵK㖑S{:H+{|2r>Ng:{)'o|xri9QVnM-80>h8wn5\v/M/~̢|~t>Lw`J.!ҢGGwEhM!ފ2CqK}ףq{DBs;^t//ݛmq8Nh]LcqJt0\t'Z9Jl:"bF B[N~h4b2V̻rvB|1~>w=Ẏ\{׏_ytW$p皙h@'$mLqW7B,d9]^3ڹKS}\ZDɟǴV2t85lIqlrHA)6ק$&ӄsmxokג^]!0ψظkz?ނ-څ'Yb *| O;Vְo⺕}];:;نH9m52o.w ylm.-ۼd<.9@5ݸDbIk<вj}q:iitNZFK67Q+6gzǤT_Uk#;^NGZJ3"K·B^'N$}{8i=NYI&~xVN0aZ$l6@j?rA I?HLay$ik]ɔ =Lv:IlIӺ׹ӆx+i܆[+Kͷܒw[ۀvcשF-]m. B@i*/kBJ+A܌nr)6@VWd~3ou\gm`+7 7t.7>^mوO'KZ痰Q,A.ߐ~u7uyI,0W,e4o9I5Y|2,$WX/rLUtY${k7gUj8X[* r `v8һwNs;wOT{5Q=댴=''$iJc@lz}eolQ7ʥ4Vj@R endstream endobj 1535 0 obj << /Type /ObjStm /N 100 /First 953 /Length 5329 /Filter /FlateDecode >> stream x}[ێ$q}߯G-~ySACKj /,re}NtwTUN^E]smp1⧹X.ڤM?.w.tm.!wZ%lߕ$ҕds+)]Gk.iGy"\컀<ֹO\?:xx.t:|:cReR/A c X9KPx<>G~S'hXNSQsmd\ Z;;q9f~XP`\iGpǚW@IC1A!y:_ <zQ srp0Hs4PFf|0053`3QF-1D9vt9OxY+\zqSmԘ gI4dGTڎv̈́T0>:zWCH6#DM668!U3*NgsybD|M'iyx"a:?Ry](~^#Bw--~DHޏt#og\e+iwf$ ׆Vamyg[ bV1d++VhLRjԮvj+3JvLjvR=ʻs9gvb5n꯷pyo;o!g+?%Nw]'L]ƺ)*+gD4{s8yV\W[#']QMB6RgqpW~0Onw'Z]TqXU쟨IԭU|\hQ9SՒ|Nt׽|8Cm?ZUG<=Է[Q:s' R AQBvr{EiwjmQڙUe|Mʹ2O t}9`^(|ojK1;&;B^i:F&Rd[ Ij5U6JT%mzB\:NNE"]cvμ GN#b(Ewh^Mm#YB 0r=L{2"AFU\-ɏKK9֞d ;oq@2 <{Lem yk+<>Mh̝6hcWCI! X6U wv% {-f\%QeQZۢߚFjcLaG훮%R xPzEy"[!Z5AbȂ]d&Cbۘ>R#%RZ+lI›uhnYVڷJF V]&yk{jwLRMuDBJoӦġi( {)8n)6!$MotWZZaam(\LQ:r4 FmgZ(ކɄ؍~YOkGIg(LW[踅|Cf/umG#-$54s2 0 c\[d3Z|QDó׻GKT V~:!jBCkI ,jԁ49˕%7y JQF**j'K5Qzg叠BѰO4^tƾtسԶTX)M%d#evB^ a)ͪ. јz"F q*5[A6YTZYRYK2T` ,u ˼Sˬ~B^ >TdLSn Qn,%%[:q֪jVda]%+i[Biáo.^d@h'9?_r8M5}oudVWW NOh 0}cKjJ{ЋofvGBU /nU%j_H]f%Jp&Vh?0x!rJ :["T8$Sh*5fuk%ՆQ)VwRkk1Y#@^ gi~6)g1neBB[Fׇ9 C #6.0xP-rn=0l^ ~D^ AYɣFAE<[XnKwnsǠ3'I4fmTUagvs3+N>76đ-k=FQ`ĝd` ph_[ʶ3bDq*j~@;ޮ` bLPn3RJ 6+U{ᤗ {-8LK`7JTX{M ^HV y#F3{o#2J*J\mH^=!Ōδyۿ%ZjZJI6ȩՙ?_}~(\| /_^>[9|]KpZ86!ܖ9}#_>@'7vk?opYB{0Ų7?s-^oO_aGpA_Rb?7W C"HW5Bs[ ڿӯyJx8vϮOH/^/y@nuIX+:_ˣ^ ލ)۽u%0ؕo7SOlI*ׅ&_oue^X=YOq5B)vD,iL ~f3_4%0zj 2,ss7Rav$琐ݎP^ _RU,7fUŹ y ˿?xAcTI~%3sH9ϒx7͊*%wkҲ;asfv,WlSWxxj]bJBʘ_4gllqg/sH8.Vwc==+ -"oOfkcoAXTSy3_|2ӢbxLq.*1uH'0aq52qc9vhvO?ii!2J}5Mi~t4EmR|MyB<:gDZj$ƫsK֪H'3Y|w{p VwOb^+vw ͮ,I; uz{.ZKkyqmK|8>pb==Ns^F%ZK1]zoSrnv%k_|l endstream endobj 1646 0 obj << /Producer (pdfTeX-1.40.25) /Author(\376\377\000B\000e\000t\000t\000i\000n\000a\000\040\000E\000i\000c\000k\000\040\000;\000\040\000W\000e\000r\000n\000e\000r\000\040\000N\000i\000c\000k\000e\000l\000\040\000;\000\040\000M\000a\000x\000\040\000H\000o\000r\000n)/Title(\376\377\000P\000o\000l\000y\000c\000y\000c\000l\000i\000c)/Subject()/Creator(\376\377\000L\000a\000T\000e\000X\000\040\000w\000i\000t\000h\000\040\000h\000y\000p\000e\000r\000r\000e\000f\000\040\000p\000a\000c\000k\000a\000g\000e\000\040\000/\000\040\000G\000A\000P\000D\000o\000c)/Keywords() /CreationDate (D:19700101000000Z) /ModDate (D:19700101000000Z) /Trapped /False /PTEX.Fullbanner (This is pdfTeX, Version 3.141592653-2.6-1.40.25 (TeX Live 2023/Debian) kpathsea version 6.3.5) >> endobj 1600 0 obj << /Type /ObjStm /N 46 /First 453 /Length 1864 /Filter /FlateDecode >> stream xڕMo7<ƇՐ,>C|x.8AE|PA >,+AK"Pv"Y=r:ȡԬH$ _kwT8$?%p0EU6aQ} BJfXI@on# $%b&d#pRB!دAIjPq܇R*SeZ|@ ^->BrBz_C:F% AmcM6Ys(u"@MOc))PA@g@ @TZۈͻϧsΧO_nmMZJOR*+A' JfÓ/&ŦQ5#p5MLWTFML)b^#5DkSk%W"| ng;`X_9vs@ 'rjiO]d_j#Ւjk-=\pY&~H )~DmUTU'D I%.5NE%ɕz1ĕZ*$WҌ\W u oZ+)`AWR®5NWpVt 1S;WSz̮8_FT9bɣ:UzWRu%O ]7)`um7P⭩.ޚB'@O*+JJ-vLsz LxP%Lfoisr'w!\z:Մ&)p4JtgpP?ĺƅƧQJkI5v5&ɕS|YCZCZj<Шa{q%TóJ>hZf5vWV9:^ʡ̮ԒI[JUFзPO k˓t3U/e$9F&Z +z!Xvs_^H\\I)]I)+a$JKd Xī crZ9Vg;(SouIJrJ?.ԎeTJ%ܵ5FTSTJUߊTӎRJ\, DzGb!"ԬyqJʮViv_=HyNl $q%'WW Nd¶ZpuՃNI%-6)ٕD+Rٕ4c=ֵ PbSFѕL]IDŕLy7a~o6.o>K@rj?-#3[v-nAATda;0?`n]W5զ ,Mq#miƢW9rZ[χ=s5jeZtU6qj9.Av%o6-khhhhGn5]zmk#Û,tFFF(y1rf:3zLFHFHFH[aLǸ#Xv>>e222262E޷}_~^?N666 54k?y[,nŲ)s_Wkw dx0^('),>UrT oOIF<$V|簋T*U2Aqt r|xNaeʆ_H+̆ +V6lXoi`D`D`D` ,L&Wuc~Ѳx&˱Xɳ$=?tnw_]_OavYbܴӧ/RƣV&?~|z/ݷ_c> 6?|=}Cg' endstream endobj 1647 0 obj << /Type /XRef /Index [0 1648] /Size 1648 /W [1 3 1] /Root 1645 0 R /Info 1646 0 R /ID [<2D51A1312DDB82FB18BA8B2474CB7335> <2D51A1312DDB82FB18BA8B2474CB7335>] /Length 3999 /Filter /FlateDecode >> stream x%{xus4I4ii$m64Ii6I4I3MRuQT ^QYuEW"eQ,QoxCA~_ye3sfߜ<{-y)x^Ec)/:jRԦ2 sԶ1ICXAVf Zjfa%qjU̮&U2[k-Sl)I-ٵPNmM̮Ԛmd6P;NmMGtml1UQlBY=ۨ(2fjt!KMe HM ʩf ms7VGm%{jڊVhvZ!5Q6vB5v>jJ->Om5O? PO`K iRj3uS[KMc{mFSG59#0Jmef8cԖ jSf-0;橝evNJ-^C SkvS0'>j*_]L \eW#=Jt/+X0Q>txb]6?Fx_ogZ+#&G$">\D#"݈PG=48?"N3*T8"NΈ,t8".'S%#D8%$Q85fE+ӭo.iڀG@. > Q/&{~yF@N!Cwd4 $1:/"\Q6ܧng5[IIt>^ S@&iBD#Mq] f l!4iD0ɈE?⬈HsNGsk3/ZSJA2l)Z<`>q-wЛ 7NR zXz( [x'7u-aLn.~n[[ 5 ja/0?%vA4nKqxߧ+hvNn= }؅8#px?]p(1Q]v.ԣ;vL4Iv7Gި˞9 8 sM Y64{Qax l1HHhBB$$%!) KH^BGPG ;x7 %HI@JD$"ԛ8SD 7-NWJ,BF6BbPA anƻCX"$!EH,BM6EHƒ@"Ba'!iOHcƻ%") u'`HDH"BPABCHy4ڼ3ƻ\Aw&?`1E@@":2f9rh|@}@749yncUf@ MA7lMBr: 9A@Դb" 1 aDǝk9nLS.F@β@L E@,bᴫStܤ+ % l@@pdåٖ1c>}+p434Ds\#yqG@q 3Zk!%8R<8<8Ȇ# ;h6f]qqypđGJ)qđ#ZxG]1s骈jሊ#*80q8G"\1˷bDő5ldsIr.AJ)q_7l̥_# nHH#%nҘˏw]%%NtInΘ+N)qđ t}x.w٘BcT˃|(PŰҘ_J`#6z2(u6&cn;Kljc9P; <я4@#]e$ <* V8|F]Apx#G\~ s<=dwӕ}>?T~F`4zSLn&ACɚG,)pΒv5.s%XS CLcd3II;{otą@Uhw,s".?9ALbě[@/ &>@L@bgD &1iOLiDl"ӭnt+[1݊$eQ1\+"<Џ~/]B _7~3 ("( VC 09~rXx7&Pհj`B0w@=d o - m ]?k^ǪۤMC?s9}}Cp */@(Ow8tO g/_ |~*)Пg4矁Q>a [vxK-4xK-4xK-4xK-4xK-4xK-4xK-4xK-4xK-4xK-4xK-tj{MxAwuIޤSDK,DK,Dv ba龥[o龥[o龥[o龥[o龥[o龥[o龥[o龥[o龥[og{DBt?6٤*7xTVT:EdIAA}+5=Z# aHCB [TK:+M BG&t? {R^aZ@$ ]&:!H8r0#:a[Dp^a%& '%ȧfaan0?"|D8{pxoh' iO-Ҟ'&Ƌs=! iOHXRgRojUӛ+Y- }oV煷?.\tp1)!wpɭ;.+w?(2Ὕ.? \6/|?(\Q vC N0|Je֕W p3GWDžOoyp§m§ g,pYt*IH}qHF$\#mL{{^X#|Rz" H_w}J~N.2 `s tDx?_^#|>\/ ߪ uÙw?#|.Qc7 OQqN 7KxǤ^yX?פKS-&NNj3K^t0S&4ztˤtۤY֩}&ZzLf:k2>g23;(yp{& {f$̂`A}v gx,04d V@!A1UJ` BrX6Fa TBTVmP uv@=da'&yU.yatlB endstream endobj startxref 329990 %%EOF polycyclic-2.17/doc/chapBib_mj.html0000644000175100001660000003144115054022512016630 0ustar runnerdocker GAP (polycyclic) - References

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.

[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.

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

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

[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).

[Vau90] 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.17/doc/basics.xml0000644000175100001660000004735415054022512015724 0ustar runnerdocker 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.17/doc/lefttoc.css0000644000175100001660000000047415054022512016100 0ustar runnerdocker/* 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.17/doc/title.xml0000644000175100001660000000317415054022512015571 0ustar runnerdocker Polycyclic Computation with polycyclic groups 2.17 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
RPTU Kaiserslautern-Landau
Gottlieb-Daimler-Straße 48
67663 Kaiserslautern
Germany
mhorn@rptu.de https://www.quendi.de/math
28 August 2025 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. 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.17/doc/chap1.html0000644000175100001660000001276615054022512015617 0ustar runnerdocker 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.17/doc/chap3.txt0000644000175100001660000004553215054022512015471 0ustar runnerdocker 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 [LS90] and [Sim94] and combinatorial collection from the left by [Vau90]. In addition, the package provides access to Hall polynomials computed by Deep Thought for the multiplication in a nilpotent group, see [Mer97] and [LS98]. 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.17/doc/chap2.txt0000644000175100001660000001136215054022512015462 0ustar runnerdocker 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]. polycyclic-2.17/doc/chap8.html0000644000175100001660000006770415054022512015630 0ustar runnerdocker 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( C, 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.17/doc/manual.lab0000644000175100001660000004276215054022512015671 0ustar runnerdocker\GAPDocLabFile{polycyclic} \makelabel{polycyclic:Title page}{}{X7D2C85EC87DD46E5} \makelabel{polycyclic:Copyright}{}{X81488B807F2A1CF1} \makelabel{polycyclic:Acknowledgements}{}{X82A988D47DFAFCFA} \makelabel{polycyclic:Table of Contents}{}{X8537FEB07AF2BEC8} \makelabel{polycyclic:Preface}{1}{X874E1D45845007FE} \makelabel{polycyclic:Introduction to polycyclic presentations}{2}{X792561B378D95B23} \makelabel{polycyclic:Collectors}{3}{X792305CC81E8606A} \makelabel{polycyclic:Constructing a Collector}{3.1}{X800FD91386C08CD8} \makelabel{polycyclic:Accessing Parts of a Collector}{3.2}{X818484817C3BAAE6} \makelabel{polycyclic:Special Features}{3.3}{X79AEB3477800DC16} \makelabel{polycyclic:Pcp-groups - polycyclically presented groups}{4}{X7E2AF25881CF7307} \makelabel{polycyclic:Pcp-elements -- elements of a pc-presented group}{4.1}{X7882F0F57ABEB680} \makelabel{polycyclic:Methods for pcp-elements}{4.2}{X790471D07A953E12} \makelabel{polycyclic:Pcp-groups - groups of pcp-elements}{4.3}{X7A4EF7C68151905A} \makelabel{polycyclic:Basic methods and functions for pcp-groups}{5}{X7B9B85AE7C9B13EE} \makelabel{polycyclic:Elementary methods for pcp-groups}{5.1}{X821360107E355B88} \makelabel{polycyclic:Elementary properties of pcp-groups}{5.2}{X80E88168866D54F3} \makelabel{polycyclic:Subgroups of pcp-groups}{5.3}{X85A7E26C7E14AFBA} \makelabel{polycyclic:Polycyclic presentation sequences for subfactors}{5.4}{X803D62BC86EF07D0} \makelabel{polycyclic:Factor groups of pcp-groups}{5.5}{X845D29B478CA7656} \makelabel{polycyclic:Homomorphisms for pcp-groups}{5.6}{X82E643F178E765EA} \makelabel{polycyclic:Changing the defining pc-presentation}{5.7}{X7C873F807D4F3A3C} \makelabel{polycyclic:Printing a pc-presentation}{5.8}{X85E681027AF19B1E} \makelabel{polycyclic:Converting to and from a presentation}{5.9}{X826ACBBB7A977206} \makelabel{polycyclic:Libraries and examples of pcp-groups}{6}{X78CEF1F27ED8D7BB} \makelabel{polycyclic:Libraries of various types of polycyclic groups}{6.1}{X84A48FAB83934263} \makelabel{polycyclic:Some assorted example groups}{6.2}{X806FBA4A7CB8FB71} \makelabel{polycyclic:Higher level methods for pcp-groups}{7}{X85BB6FE078679DAF} \makelabel{polycyclic:Subgroup series in pcp-groups}{7.1}{X8266A0A2821D98A1} \makelabel{polycyclic:Orbit stabilizer methods for pcp-groups}{7.2}{X7CE2DA437FD2B383} \makelabel{polycyclic:Centralizers, Normalizers and Intersections}{7.3}{X80E3B42E792532B3} \makelabel{polycyclic:Finite subgroups}{7.4}{X7CF015E87A2B2388} \makelabel{polycyclic:Subgroups of finite index and maximal subgroups}{7.5}{X7D9F737F80F6E396} \makelabel{polycyclic:Further attributes for pcp-groups based on the Fitting subgroup}{7.6}{X785E0E877AB1D549} \makelabel{polycyclic:Functions for nilpotent groups}{7.7}{X878DBDC77CCA4F7E} \makelabel{polycyclic:Random methods for pcp-groups}{7.8}{X8640F9D47A1F7434} \makelabel{polycyclic:Non-abelian tensor product and Schur extensions}{7.9}{X824142B784453DB9} \makelabel{polycyclic:Schur covers}{7.10}{X7D3023697BA5CE5A} \makelabel{polycyclic:Cohomology for pcp-groups}{8}{X796AB9787E2A752C} \makelabel{polycyclic:Cohomology records}{8.1}{X875758FA7C6F5CE1} \makelabel{polycyclic:Cohomology groups}{8.2}{X874759D582393441} \makelabel{polycyclic:Extended 1-cohomology}{8.3}{X79610E9178BD0C54} \makelabel{polycyclic:Extensions and Complements}{8.4}{X853E51787A24AE00} \makelabel{polycyclic:Constructing pcp groups as extensions}{8.5}{X823771527DBD857D} \makelabel{polycyclic:Matrix Representations}{9}{X858D1BB07A8FBF87} \makelabel{polycyclic:Unitriangular matrix groups}{9.1}{X7D0ED06C7E6A457D} \makelabel{polycyclic:Upper unitriangular matrix groups}{9.2}{X79A8A51B84E4BF8C} \makelabel{polycyclic:Obsolete Functions and Name Changes}{A}{X874ECE907CAF380D} \makelabel{polycyclic:Bibliography}{Bib}{X7A6F98FD85F02BFE} \makelabel{polycyclic:References}{Bib}{X7A6F98FD85F02BFE} \makelabel{polycyclic:Index}{Ind}{X83A0356F839C696F} \makelabel{polycyclic:License}{}{X81488B807F2A1CF1} \makelabel{polycyclic:FromTheLeftCollector}{3.1.1}{X8382A4E78706DE65} \makelabel{polycyclic:SetRelativeOrder}{3.1.2}{X79A308B28183493B} \makelabel{polycyclic:SetRelativeOrderNC}{3.1.2}{X79A308B28183493B} \makelabel{polycyclic:SetPower}{3.1.3}{X7BC319BA8698420C} \makelabel{polycyclic:SetPowerNC}{3.1.3}{X7BC319BA8698420C} \makelabel{polycyclic:SetConjugate}{3.1.4}{X86A08D887E049347} \makelabel{polycyclic:SetConjugateNC}{3.1.4}{X86A08D887E049347} \makelabel{polycyclic:SetCommutator}{3.1.5}{X7B25997C7DF92B6D} \makelabel{polycyclic:UpdatePolycyclicCollector}{3.1.6}{X7E9903F57BC5CC24} \makelabel{polycyclic:IsConfluent}{3.1.7}{X8006790B86328CE8} \makelabel{polycyclic:RelativeOrders}{3.2.1}{X7DD0DF677AC1CF10} \makelabel{polycyclic:GetPower}{3.2.2}{X844C0A478735EF4B} \makelabel{polycyclic:GetPowerNC}{3.2.2}{X844C0A478735EF4B} \makelabel{polycyclic:GetConjugate}{3.2.3}{X865160E07FA93E00} \makelabel{polycyclic:GetConjugateNC}{3.2.3}{X865160E07FA93E00} \makelabel{polycyclic:NumberOfGenerators}{3.2.4}{X7D6A26A4871FF51A} \makelabel{polycyclic:ObjByExponents}{3.2.5}{X873ECF388503E5DE} \makelabel{polycyclic:ExponentsByObj}{3.2.6}{X85BCB97B8021EAD6} \makelabel{polycyclic:IsWeightedCollector}{3.3.1}{X82EE2ACD7B8C178B} \makelabel{polycyclic:AddHallPolynomials}{3.3.2}{X7A1D7ED68334282C} \makelabel{polycyclic:String}{3.3.3}{X81FB5BE27903EC32} \makelabel{polycyclic:FTLCollectorPrintTo}{3.3.4}{X7ED466B6807D16FE} \makelabel{polycyclic:FTLCollectorAppendTo}{3.3.5}{X789D9EB37ECFA9D7} \makelabel{polycyclic:UseLibraryCollector}{3.3.6}{X808A26FB873A354F} \makelabel{polycyclic:USELIBRARYCOLLECTOR}{3.3.7}{X844E195C7D55F8BD} \makelabel{polycyclic:DEBUGCOMBINATORIALCOLLECTOR}{3.3.8}{X7945C6B97BECCDA8} \makelabel{polycyclic:USECOMBINATORIALCOLLECTOR}{3.3.9}{X7BDFB55D7CB33543} \makelabel{polycyclic:PcpElementByExponentsNC}{4.1.1}{X786DB93F7862D903} \makelabel{polycyclic:PcpElementByExponents}{4.1.1}{X786DB93F7862D903} \makelabel{polycyclic:PcpElementByGenExpListNC}{4.1.2}{X7BBB358C7AA64135} \makelabel{polycyclic:PcpElementByGenExpList}{4.1.2}{X7BBB358C7AA64135} \makelabel{polycyclic:IsPcpElement}{4.1.3}{X86083E297D68733B} \makelabel{polycyclic:IsPcpElementCollection}{4.1.4}{X8695069A7D5073B7} \makelabel{polycyclic:IsPcpElementRep}{4.1.5}{X7F2C83AD862910B9} \makelabel{polycyclic:IsPcpGroup}{4.1.6}{X8470284A78A6C41B} \makelabel{polycyclic:Collector}{4.2.1}{X7E2D258B7DCE8AC9} \makelabel{polycyclic:Exponents}{4.2.2}{X85C672E78630C507} \makelabel{polycyclic:GenExpList}{4.2.3}{X8571F6FB7E74346C} \makelabel{polycyclic:NameTag}{4.2.4}{X82252C5E7B011559} \makelabel{polycyclic:Depth}{4.2.5}{X840D32D9837E99F5} \makelabel{polycyclic:LeadingExponent}{4.2.6}{X874F1EC178721833} \makelabel{polycyclic:RelativeOrder}{4.2.7}{X8008AB61823A76B7} \makelabel{polycyclic:RelativeIndex}{4.2.8}{X875D04288577015B} \makelabel{polycyclic:FactorOrder}{4.2.9}{X87E070747955F2C1} \makelabel{polycyclic:NormingExponent}{4.2.10}{X79A247797F0A8583} \makelabel{polycyclic:NormedPcpElement}{4.2.11}{X798BB22B80833441} \makelabel{polycyclic:PcpGroupByCollector}{4.3.1}{X7C8FBCAB7F63FACB} \makelabel{polycyclic:PcpGroupByCollectorNC}{4.3.1}{X7C8FBCAB7F63FACB} \makelabel{polycyclic:Group}{4.3.2}{X7D7B075385435151} \makelabel{polycyclic:Subgroup}{4.3.3}{X7C82AA387A42DCA0} \makelabel{polycyclic:Size}{5.1.2}{X858ADA3B7A684421} \makelabel{polycyclic:Random}{5.1.3}{X79730D657AB219DB} \makelabel{polycyclic:Index}{5.1.4}{X83A0356F839C696F} \makelabel{polycyclic:Elements}{5.1.6}{X79B130FC7906FB4C} \makelabel{polycyclic:ClosureGroup}{5.1.7}{X7D13FC1F8576FFD8} \makelabel{polycyclic:NormalClosure}{5.1.8}{X7BDEA0A98720D1BB} \makelabel{polycyclic:HirschLength}{5.1.9}{X839B42AE7A1DD544} \makelabel{polycyclic:CommutatorSubgroup}{5.1.10}{X7A9A3D5578CE33A0} \makelabel{polycyclic:PRump}{5.1.11}{X796DA805853FAC90} \makelabel{polycyclic:SmallGeneratingSet}{5.1.12}{X814DBABC878D5232} \makelabel{polycyclic:IsSubgroup}{5.2.1}{X7839D8927E778334} \makelabel{polycyclic:IsNormal}{5.2.2}{X838186F9836F678C} \makelabel{polycyclic:IsNilpotentGroup}{5.2.3}{X87D062608719F2CD} \makelabel{polycyclic:IsAbelian}{5.2.4}{X7C12AA7479A6C103} \makelabel{polycyclic:IsElementaryAbelian}{5.2.5}{X813C952F80E775D4} \makelabel{polycyclic:IsFreeAbelian}{5.2.6}{X84FFC668832F9ED6} \makelabel{polycyclic:Igs for a subgroup}{5.3.1}{X815F756286701BE0} \makelabel{polycyclic:Igs}{5.3.1}{X815F756286701BE0} \makelabel{polycyclic:IgsParallel}{5.3.1}{X815F756286701BE0} \makelabel{polycyclic:Ngs for a subgroup}{5.3.2}{X7F4D95C47F9652BA} \makelabel{polycyclic:Ngs}{5.3.2}{X7F4D95C47F9652BA} \makelabel{polycyclic:Cgs for a subgroup}{5.3.3}{X8077293A787D4571} \makelabel{polycyclic:Cgs}{5.3.3}{X8077293A787D4571} \makelabel{polycyclic:CgsParallel}{5.3.3}{X8077293A787D4571} \makelabel{polycyclic:SubgroupByIgs}{5.3.4}{X83B92A2679EAB1EB} \makelabel{polycyclic:SubgroupByIgs with extra generators}{5.3.4}{X83B92A2679EAB1EB} \makelabel{polycyclic:AddToIgs}{5.3.5}{X78107DE78728B26B} \makelabel{polycyclic:AddToIgsParallel}{5.3.5}{X78107DE78728B26B} \makelabel{polycyclic:AddIgsToIgs}{5.3.5}{X78107DE78728B26B} \makelabel{polycyclic:Pcp}{5.4.1}{X7DD931697DD93169} \makelabel{polycyclic:Pcp for a factor}{5.4.1}{X7DD931697DD93169} \makelabel{polycyclic:GeneratorsOfPcp}{5.4.2}{X821FF77086E38B3A} \makelabel{polycyclic:Length}{5.4.4}{X780769238600AFD1} \makelabel{polycyclic:RelativeOrdersOfPcp}{5.4.5}{X7ABCA7F2790E1673} \makelabel{polycyclic:DenominatorOfPcp}{5.4.6}{X7D16C299825887AA} \makelabel{polycyclic:NumeratorOfPcp}{5.4.7}{X803AED1A84FCBEE8} \makelabel{polycyclic:GroupOfPcp}{5.4.8}{X80BCCF0B81344933} \makelabel{polycyclic:OneOfPcp}{5.4.9}{X87F0BA5F7BA0F4B4} \makelabel{polycyclic:ExponentsByPcp}{5.4.10}{X7A8C8BBC81581E09} \makelabel{polycyclic:PcpGroupByPcp}{5.4.11}{X87D75F7F86FEF203} \makelabel{polycyclic:NaturalHomomorphismByNormalSubgroup}{5.5.1}{X80FC390C7F38A13F} \makelabel{polycyclic:FactorGroup}{5.5.2}{X7F51DF007F51DF00} \makelabel{polycyclic:GroupHomomorphismByImages}{5.6.1}{X7F348F497C813BE0} \makelabel{polycyclic:Kernel}{5.6.2}{X7DCD99628504B810} \makelabel{polycyclic:Image for a homomorphism}{5.6.3}{X847322667E6166C8} \makelabel{polycyclic:Image for a homomorphism and a subgroup}{5.6.3}{X847322667E6166C8} \makelabel{polycyclic:Image for a homomorphism and an element}{5.6.3}{X847322667E6166C8} \makelabel{polycyclic:PreImage}{5.6.4}{X836FAEAC78B55BF4} \makelabel{polycyclic:PreImagesRepresentative}{5.6.5}{X7AE24A1586B7DE79} \makelabel{polycyclic:IsInjective}{5.6.6}{X7F065FD7822C0A12} \makelabel{polycyclic:RefinedPcpGroup}{5.7.1}{X80E9B60E853B2E05} \makelabel{polycyclic:PcpGroupBySeries}{5.7.2}{X7F88F5548329E279} \makelabel{polycyclic:PrintPcpPresentation for a group}{5.8.1}{X79D247127FD57FC8} \makelabel{polycyclic:PrintPcpPresentation for a pcp}{5.8.1}{X79D247127FD57FC8} \makelabel{polycyclic:IsomorphismPcpGroup}{5.9.1}{X8771540F7A235763} \makelabel{polycyclic:IsomorphismPcpGroupFromFpGroupWithPcPres}{5.9.2}{X7F5EBF1C831B4BA9} \makelabel{polycyclic:IsomorphismPcGroup}{5.9.3}{X873CEB137BA1CD6E} \makelabel{polycyclic:IsomorphismFpGroup}{5.9.4}{X7F28268F850F454E} \makelabel{polycyclic:AbelianPcpGroup}{6.1.1}{X7AEDE1BA82014B86} \makelabel{polycyclic:AbelianPcpGroup rels only}{6.1.1}{X7AEDE1BA82014B86} \makelabel{polycyclic:DihedralPcpGroup}{6.1.2}{X7ACF57737D0F12DB} \makelabel{polycyclic:UnitriangularPcpGroup}{6.1.3}{X864CEDAB7911CC79} \makelabel{polycyclic:SubgroupUnitriangularPcpGroup}{6.1.4}{X812E35B17AADBCD5} \makelabel{polycyclic:InfiniteMetacyclicPcpGroup}{6.1.5}{X7A80F7F27FDA6810} \makelabel{polycyclic:HeisenbergPcpGroup}{6.1.6}{X81BEC875827D1CC2} \makelabel{polycyclic:MaximalOrderByUnitsPcpGroup}{6.1.7}{X87F9B9C9786430D7} \makelabel{polycyclic:BurdeGrunewaldPcpGroup}{6.1.8}{X852283A77A2C93DD} \makelabel{polycyclic:ExampleOfMetabelianPcpGroup}{6.2.1}{X86293081865CDFC3} \makelabel{polycyclic:ExamplesOfSomePcpGroups}{6.2.2}{X83A74A6E7E232FD6} \makelabel{polycyclic:PcpSeries}{7.1.1}{X8037DAD77A19D9B2} \makelabel{polycyclic:EfaSeries}{7.1.2}{X86C633357ACD342C} \makelabel{polycyclic:SemiSimpleEfaSeries}{7.1.3}{X80ED4F8380DC477E} \makelabel{polycyclic:DerivedSeriesOfGroup}{7.1.4}{X7A879948834BD889} \makelabel{polycyclic:RefinedDerivedSeries}{7.1.5}{X866D4C5C79F26611} \makelabel{polycyclic:RefinedDerivedSeriesDown}{7.1.6}{X86F7DE927DE3B5CD} \makelabel{polycyclic:LowerCentralSeriesOfGroup}{7.1.7}{X879D55A67DB42676} \makelabel{polycyclic:UpperCentralSeriesOfGroup}{7.1.8}{X8428592E8773CD7B} \makelabel{polycyclic:TorsionByPolyEFSeries}{7.1.9}{X83CA5DE785AE3F2C} \makelabel{polycyclic:PcpsBySeries}{7.1.10}{X7E39431286969377} \makelabel{polycyclic:PcpsOfEfaSeries}{7.1.11}{X79789A1C82139854} \makelabel{polycyclic:PcpOrbitStabilizer}{7.2.1}{X83E17DB483B33AB5} \makelabel{polycyclic:PcpOrbitsStabilizers}{7.2.1}{X83E17DB483B33AB5} \makelabel{polycyclic:StabilizerIntegralAction}{7.2.2}{X80694BA480F69A0E} \makelabel{polycyclic:OrbitIntegralAction}{7.2.2}{X80694BA480F69A0E} \makelabel{polycyclic:NormalizerIntegralAction}{7.2.3}{X875BE4077B32A411} \makelabel{polycyclic:ConjugacyIntegralAction}{7.2.3}{X875BE4077B32A411} \makelabel{polycyclic:Centralizer for an element}{7.3.1}{X808EE8AD7EE3ECE1} \makelabel{polycyclic:IsConjugate for elements}{7.3.1}{X808EE8AD7EE3ECE1} \makelabel{polycyclic:Centralizer for a subgroup}{7.3.2}{X849B5C527BAFAAA4} \makelabel{polycyclic:Normalizer}{7.3.2}{X849B5C527BAFAAA4} \makelabel{polycyclic:IsConjugate for subgroups}{7.3.2}{X849B5C527BAFAAA4} \makelabel{polycyclic:Intersection}{7.3.3}{X851069107CACF98E} \makelabel{polycyclic:TorsionSubgroup}{7.4.1}{X8036FA507A170DC4} \makelabel{polycyclic:NormalTorsionSubgroup}{7.4.2}{X8082CD337972DC63} \makelabel{polycyclic:IsTorsionFree}{7.4.3}{X86D92DA17DCE22DD} \makelabel{polycyclic:FiniteSubgroupClasses}{7.4.4}{X819058217B4F3DC0} \makelabel{polycyclic:FiniteSubgroupClassesBySeries}{7.4.5}{X7E7C32EA81A297B6} \makelabel{polycyclic:MaximalSubgroupClassesByIndex}{7.5.1}{X87D62D497A8715FB} \makelabel{polycyclic:LowIndexSubgroupClasses}{7.5.2}{X7800133F81BC7674} \makelabel{polycyclic:LowIndexNormalSubgroups}{7.5.3}{X7F7067C77F2DC32C} \makelabel{polycyclic:NilpotentByAbelianNormalSubgroup}{7.5.4}{X85A5BC447D83175F} \makelabel{polycyclic:FittingSubgroup}{7.6.1}{X780552B57C30DD8F} \makelabel{polycyclic:IsNilpotentByFinite}{7.6.2}{X86BD63DC844731DF} \makelabel{polycyclic:Centre}{7.6.3}{X847ABE6F781C7FE8} \makelabel{polycyclic:FCCentre}{7.6.4}{X861C36368435EB09} \makelabel{polycyclic:PolyZNormalSubgroup}{7.6.5}{X7E75E2BC806746AC} \makelabel{polycyclic:NilpotentByAbelianByFiniteSeries}{7.6.6}{X86800BF783E30D4A} \makelabel{polycyclic:MinimalGeneratingSet}{7.7.1}{X81D15723804771E2} \makelabel{polycyclic:RandomCentralizerPcpGroup for an element}{7.8.1}{X80AEE73E7D639699} \makelabel{polycyclic:RandomCentralizerPcpGroup for a subgroup}{7.8.1}{X80AEE73E7D639699} \makelabel{polycyclic:RandomNormalizerPcpGroup}{7.8.1}{X80AEE73E7D639699} \makelabel{polycyclic:SchurExtension}{7.9.1}{X79EF28D9845878C9} \makelabel{polycyclic:SchurExtensionEpimorphism}{7.9.2}{X84B60EC978A9A05E} \makelabel{polycyclic:SchurCover}{7.9.3}{X7DD1E37987612042} \makelabel{polycyclic:AbelianInvariantsMultiplier}{7.9.4}{X792BC39D7CEB1D27} \makelabel{polycyclic:NonAbelianExteriorSquareEpimorphism}{7.9.5}{X822ED5978647C93B} \makelabel{polycyclic:NonAbelianExteriorSquare}{7.9.6}{X8739CD4686301A0E} \makelabel{polycyclic:NonAbelianTensorSquareEpimorphism}{7.9.7}{X86553D7B7DABF38F} \makelabel{polycyclic:NonAbelianTensorSquare}{7.9.8}{X7C0DF7C97F78C666} \makelabel{polycyclic:NonAbelianExteriorSquarePlusEmbedding}{7.9.9}{X7AE75EC1860FFE7A} \makelabel{polycyclic:NonAbelianTensorSquarePlusEpimorphism}{7.9.10}{X7D96C84E87925B0F} \makelabel{polycyclic:NonAbelianTensorSquarePlus}{7.9.11}{X8746533787C4E8BC} \makelabel{polycyclic:WhiteheadQuadraticFunctor}{7.9.12}{X78F9184078B2761A} \makelabel{polycyclic:SchurCovers}{7.10.1}{X7D90B44E7B96AFF1} \makelabel{polycyclic:CRRecordByMats}{8.1.1}{X7C97442C7B78806C} \makelabel{polycyclic:CRRecordBySubgroup}{8.1.2}{X8646DFA1804D2A11} \makelabel{polycyclic:CRRecordByPcp}{8.1.2}{X8646DFA1804D2A11} \makelabel{polycyclic:OneCoboundariesCR}{8.2.1}{X85EF170387D39D4A} \makelabel{polycyclic:OneCocyclesCR}{8.2.1}{X85EF170387D39D4A} \makelabel{polycyclic:TwoCoboundariesCR}{8.2.1}{X85EF170387D39D4A} \makelabel{polycyclic:TwoCocyclesCR}{8.2.1}{X85EF170387D39D4A} \makelabel{polycyclic:OneCohomologyCR}{8.2.1}{X85EF170387D39D4A} \makelabel{polycyclic:TwoCohomologyCR}{8.2.1}{X85EF170387D39D4A} \makelabel{polycyclic:TwoCohomologyModCR}{8.2.2}{X79B48D697A8A84C8} \makelabel{polycyclic:OneCoboundariesEX}{8.3.1}{X7E87E3EA81C84621} \makelabel{polycyclic:OneCocyclesEX}{8.3.2}{X8111D2087C16CC0C} \makelabel{polycyclic:OneCohomologyEX}{8.3.3}{X84718DDE792FB212} \makelabel{polycyclic: ComplementCR}{8.4.1}{X7DA9162085058006} \makelabel{polycyclic: ComplementsCR}{8.4.2}{X7F8984D386A813D6} \makelabel{polycyclic: ComplementClassesCR}{8.4.3}{X7FAB3EB0803197FA} \makelabel{polycyclic: ComplementClassesEfaPcps}{8.4.4}{X8759DC59799DD508} \makelabel{polycyclic: ComplementClasses}{8.4.5}{X7B0EC76D81A056AB} \makelabel{polycyclic:ExtensionCR}{8.4.6}{X85F3B55C78CF840B} \makelabel{polycyclic:ExtensionsCR}{8.4.7}{X81DC85907E0948FD} \makelabel{polycyclic:ExtensionClassesCR}{8.4.8}{X7AE16E3687E14B24} \makelabel{polycyclic:SplitExtensionPcpGroup}{8.4.9}{X7986997B78AD3292} \makelabel{polycyclic:UnitriangularMatrixRepresentation}{9.1.1}{X7E6F320F865E309C} \makelabel{polycyclic:IsMatrixRepresentation}{9.1.2}{X7F5E7F5F7DDB2E2C} \makelabel{polycyclic:IsomorphismUpperUnitriMatGroupPcpGroup}{9.2.1}{X8434972E7DDB68C1} \makelabel{polycyclic:SiftUpperUnitriMatGroup}{9.2.2}{X843C9D427FFA2487} \makelabel{polycyclic:RanksLevels}{9.2.3}{X7CF8B8F981931846} \makelabel{polycyclic:MakeNewLevel}{9.2.4}{X81F3760186734EA7} \makelabel{polycyclic:SiftUpperUnitriMat}{9.2.5}{X851A216C85B74574} \makelabel{polycyclic:DecomposeUpperUnitriMat}{9.2.6}{X86D711217C639C2C} \makelabel{polycyclic:SchurCovering}{A}{X874ECE907CAF380D} \makelabel{polycyclic:SchurMultPcpGroup}{A}{X874ECE907CAF380D} polycyclic-2.17/doc/chap6.txt0000644000175100001660000001313515054022512015466 0ustar runnerdocker 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 AbelianPcpGroup( 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 or infinity. If n is not provided then the length of rels is used. If rels is omitted then all generators will have infinite order. 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.17/doc/chap0_mj.html0000644000175100001660000011336115054022512016275 0ustar runnerdocker GAP (polycyclic) - Contents
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

Polycyclic

Computation with polycyclic groups

2.17

28 August 2025

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: mhorn@rptu.de
Homepage: https://www.quendi.de/math
Address:
Fachbereich Mathematik
RPTU Kaiserslautern-Landau
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 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.

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.

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.17/doc/chap7.txt0000644000175100001660000012710715054022512015474 0ustar runnerdocker 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 polycyclic 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× 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.17/doc/toggless.css0000644000175100001660000000167215054022512016270 0ustar runnerdocker/* toggless.css Frank Lübeck */ /* Using javascript we change all div.ContSect to div.ContSectOpen or div.ContSectClosed. This way the config for div.ContSect in manual.css is no longer relevant. Here we add the CSS for the new elements. */ /* This layout is based on an idea by Burkhard Höfling. */ div.ContSectClosed { text-align: left; margin-left: 1em; } div.ContSectOpen { text-align: left; margin-left: 1em; } div.ContSectOpen div.ContSSBlock { display: block; text-align: left; margin-left: 1em; } div.ContSectOpen div.ContSSBlock a { display: block; width: 100%; margin-left: 1em; } span.tocline a:hover { display: inline; background: #eeeeee; } span.ContSS a:hover { display: inline; background: #eeeeee; } span.toctoggle { font-size: 80%; display: inline-block; width: 1.2em; } span.toctoggle:hover { background-color: #aaaaaa; } polycyclic-2.17/doc/chap0.html0000644000175100001660000011160415054022512015605 0ustar runnerdocker GAP (polycyclic) - Contents
Goto Chapter: Top 1 2 3 4 5 6 7 8 9 A Bib Ind

Polycyclic

Computation with polycyclic groups

2.17

28 August 2025

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: mhorn@rptu.de
Homepage: https://www.quendi.de/math
Address:
Fachbereich Mathematik
RPTU Kaiserslautern-Landau
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 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.

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.

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.17/doc/chooser.html0000644000175100001660000000745615054022512016265 0ustar runnerdocker 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.17/doc/polycyclicbib.xml.bib0000644000175100001660000002467615054022512020044 0ustar runnerdocker @book{ Rob82, author = {Robinson, D. J.}, title = {A Course in the Theory of Groups}, publisher = {Springer\texttt{\symbol{45}}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\texttt{\symbol{45}}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\texttt{\symbol{45}}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\texttt{\symbol{45}}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\texttt{\symbol{45}}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\texttt{\symbol{45}}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\texttt{\symbol{45}}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\texttt{\symbol{45}}by\texttt{\symbol{45}}finite groups}, journal = {J. Algebra}, volume = {142}, year = {1991}, pages = {118\texttt{\symbol{45}}\texttt{\symbol{45}}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\texttt{\symbol{45}}521\texttt{\symbol{45}}43213\texttt{\symbol{45}}8}, mrclass = {20F05 (20\texttt{\symbol{45}}02 68Q40 68Q42)}, mrnumber = {95f:20053}, mrreviewer = {Friedrich Otto}, printedkey = {Sim94} } @article{ LGS90, author = {Leedham\texttt{\symbol{45}}Green, C. R. and Soicher, L. H.}, title = {Collection from the left and other strategies}, journal = {J. Symbolic Comput.}, volume = {9}, number = {5\texttt{\symbol{45}}6}, year = {1990}, pages = {665\texttt{\symbol{45}}\texttt{\symbol{45}}675}, issn = {0747\texttt{\symbol{45}}7171}, mrclass = {20D10 (68Q25)}, mrnumber = {92b:20021}, mrreviewer = {M. Greendlinger}, printedkey = {LS90} } @article{ MVL90, author = {Vaughan\texttt{\symbol{45}}Lee, M. R.}, title = {Collection from the left}, journal = {J. Symbolic Comput.}, volume = {9}, number = {5\texttt{\symbol{45}}6}, year = {1990}, pages = {725\texttt{\symbol{45}}\texttt{\symbol{45}}733}, issn = {0747\texttt{\symbol{45}}7171}, mrclass = {20F12 (20\texttt{\symbol{45}}04 20D15 20F18)}, mrnumber = {92c:20065}, mrreviewer = {M. Greendlinger}, printedkey = {Vau90} } @article{ B-K00, author = {Beuerle, J. R. and Kappe, L.\texttt{\symbol{45}}C.}, title = {Infinite metacyclic groups and their non\texttt{\symbol{45}}abelian tensor squares}, journal = {Proc. Edinburgh Math. Soc. (2)}, volume = {43}, number = {3}, year = {2000}, pages = {651\texttt{\symbol{45}}\texttt{\symbol{45}}662}, issn = {0013\texttt{\symbol{45}}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\texttt{\symbol{45}}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\texttt{\symbol{45}}\texttt{\symbol{45}}24 (electronic)}, issn = {1461\texttt{\symbol{45}}1570}, mrclass = {20\texttt{\symbol{45}}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\texttt{\symbol{45}}by\texttt{\symbol{45}}finite group and its applications}, journal = {J. Algebra}, volume = {242}, year = {2001}, pages = {176\texttt{\symbol{45}}\texttt{\symbol{45}}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\texttt{\symbol{45}}stabilizer problems and computing normalizers for polycyclic groups}, journal = {J. Symbolic Comput.}, volume = {34}, year = {2002}, pages = {1\texttt{\symbol{45}}\texttt{\symbol{45}}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\texttt{\symbol{45}}\texttt{\symbol{45}}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\texttt{\symbol{45}}\texttt{\symbol{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\texttt{\symbol{45}}\texttt{\symbol{45}}379}, printedkey = {Ost99} } @article{ dGN02, author = {de Graaf, W. A. and Nickel, W.}, title = {Constructing faithful representations of finitely\texttt{\symbol{45}}generated torsion\texttt{\symbol{45}}free nilpotent groups}, journal = {J. Symbolic Comput.}, volume = {33}, number = {1}, year = {2002}, pages = {31\texttt{\symbol{45}}\texttt{\symbol{45}}41}, issn = {0747\texttt{\symbol{45}}7171}, mrclass = {20C15 (20F18)}, mrnumber = {MR1876310}, printedkey = {GN02} } @article{ EickNickel07, author = {Eick, B. and Nickel, W.}, title = {Computing the Schur multiplicator and the non\texttt{\symbol{45}}abelian tensor square of a polycyclic group}, journal = {J. Algebra}, volume = {320}, number = {2}, year = {2008}, pages = {927{\textendash}\texttt{\symbol{45}}944}, mrclass = {20J05 (20\texttt{\symbol{45}}04 20E22 20F05)}, mrnumber = {MR2422322}, printedkey = {EN08} } polycyclic-2.17/doc/chapA.html0000644000175100001660000000767715054022512015644 0ustar runnerdocker 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.17/doc/chapA.txt0000644000175100001660000000264115054022512015501 0ustar runnerdocker 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.17/doc/chap9_mj.html0000644000175100001660000002674315054022512016315 0ustar runnerdocker 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 polycyclic 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.17/doc/preface.xml0000644000175100001660000000531115054022512016050 0ustar runnerdocker 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.17/doc/matreps.xml0000644000175100001660000001075515054022512016126 0ustar runnerdocker 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 polycyclic 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.17/doc/nocolorprompt.css0000644000175100001660000000031315054022512017345 0ustar runnerdocker /* 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.17/doc/chap9.html0000644000175100001660000002626215054022512015623 0ustar runnerdocker 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 polycyclic 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.17/doc/chap4.html0000644000175100001660000005234215054022512015614 0ustar runnerdocker 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.17/doc/chapA_mj.html0000644000175100001660000001025515054022512016314 0ustar runnerdocker 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.17/doc/chapBib.html0000644000175100001660000003107415054022512016144 0ustar runnerdocker 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.

[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.

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

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

[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).

[Vau90] 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.17/doc/libraries.xml0000644000175100001660000001213415054022512016420 0ustar runnerdocker 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 or infinity. If n is not provided then the length of rels is used. If rels is omitted then all generators will have infinite order. 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.17/doc/chap2.html0000644000175100001660000002025315054022512015606 0ustar runnerdocker 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.17/doc/collect.xml0000644000175100001660000003672215054022512016102 0ustar runnerdocker 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.17/doc/intro.xml0000644000175100001660000001066015054022512015601 0ustar runnerdocker 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.17/doc/manual.js0000644000175100001660000001011315054022512015530 0ustar runnerdocker/* 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:" || window.location.protocol == "https:") { 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.17/doc/toggless.js0000644000175100001660000000420515054022512016107 0ustar runnerdocker/* 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.17/doc/chapInd_mj.html0000644000175100001660000005777415054022512016667 0ustar runnerdocker 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
    rels only 6.1-1
AddHallPolynomials 3.3-2
AddIgsToIgs 5.3-5
AddToIgs 5.3-5
AddToIgsParallel 5.3-5
BurdeGrunewaldPcpGroup 6.1-8
Centralizer, for a subgroup 7.3-2
    for an element 7.3-1
Centre 7.6-3
Cgs 5.3-3
    for a subgroup 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
    for a subgroup 5.3-1
IgsParallel 5.3-1
Image, for a homomorphism 5.6-3
    for a homomorphism and a subgroup 5.6-3
    for a homomorphism and an element 5.6-3
Index 5.1-4
InfiniteMetacyclicPcpGroup 6.1-5
Intersection 7.3-3
IsAbelian 5.2-4
IsConfluent 3.1-7
IsConjugate, for elements 7.3-1
    for subgroups 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
    for a subgroup 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
    for a factor 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, for a group 5.8-1
    for a pcp 5.8-1
PRump 5.1-11
Random 5.1-3
RandomCentralizerPcpGroup, for a subgroup 7.8-1
    for an element 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
    with extra generators 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.17/doc/chapInd.html0000644000175100001660000005624415054022512016170 0ustar runnerdocker 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
    rels only 6.1-1
AddHallPolynomials 3.3-2
AddIgsToIgs 5.3-5
AddToIgs 5.3-5
AddToIgsParallel 5.3-5
BurdeGrunewaldPcpGroup 6.1-8
Centralizer, for a subgroup 7.3-2
    for an element 7.3-1
Centre 7.6-3
Cgs 5.3-3
    for a subgroup 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
    for a subgroup 5.3-1
IgsParallel 5.3-1
Image, for a homomorphism 5.6-3
    for a homomorphism and a subgroup 5.6-3
    for a homomorphism and an element 5.6-3
Index 5.1-4
InfiniteMetacyclicPcpGroup 6.1-5
Intersection 7.3-3
IsAbelian 5.2-4
IsConfluent 3.1-7
IsConjugate, for elements 7.3-1
    for subgroups 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
    for a subgroup 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
    for a factor 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, for a group 5.8-1
    for a pcp 5.8-1
PRump 5.1-11
Random 5.1-3
RandomCentralizerPcpGroup, for a subgroup 7.8-1
    for an element 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
    with extra generators 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.17/doc/chap6.html0000644000175100001660000003207015054022512015612 0ustar runnerdocker 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 )
‣ AbelianPcpGroup( 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 or infinity. If n is not provided then the length of rels is used. If rels is omitted then all generators will have infinite order.

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.17/doc/methods.xml0000644000175100001660000010164115054022512016111 0ustar runnerdocker 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 polycyclic 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\times 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.17/doc/chap3_mj.html0000644000175100001660000010021115054022512016266 0ustar runnerdocker 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 [LS90] and [Sim94] and combinatorial collection from the left by [Vau90]. In addition, the package provides access to Hall polynomials computed by Deep Thought for the multiplication in a nilpotent group, see [Mer97] and [LS98].

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.17/doc/chap8_mj.html0000644000175100001660000007114315054022512016306 0ustar runnerdocker 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( C, 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.17/doc/rainbow.js0000644000175100001660000000533615054022512015727 0ustar runnerdocker 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.17/doc/chap8.txt0000644000175100001660000004040215054022512015465 0ustar runnerdocker 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( C, 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.17/doc/chapInd.txt0000644000175100001660000001715615054022512016042 0ustar runnerdocker 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 rels only 6.1-1 AddHallPolynomials 3.3-2 AddIgsToIgs 5.3-5 AddToIgs 5.3-5 AddToIgsParallel 5.3-5 BurdeGrunewaldPcpGroup 6.1-8 Centralizer, for a subgroup 7.3-2 for an element 7.3-1 Centre 7.6-3 Cgs 5.3-3 for a subgroup 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 for a subgroup 5.3-1 IgsParallel 5.3-1 Image, for a homomorphism 5.6-3 for a homomorphism and a subgroup 5.6-3 for a homomorphism and an element 5.6-3 Index 5.1-4 InfiniteMetacyclicPcpGroup 6.1-5 Intersection 7.3-3 IsAbelian 5.2-4 IsConfluent 3.1-7 IsConjugate, for elements 7.3-1 for subgroups 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 0.0-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 for a subgroup 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 for a factor 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, for a group 5.8-1 for a pcp 5.8-1 PRump 5.1-11 Random 5.1-3 RandomCentralizerPcpGroup, for a subgroup 7.8-1 for an element 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.0 SchurCovers 7.10-1 SchurExtension 7.9-1 SchurExtensionEpimorphism 7.9-2 SchurMultPcpGroup A.0 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 with extra generators 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.17/doc/chap5.txt0000644000175100001660000005777515054022512015507 0ustar runnerdocker 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.17/doc/manual.six0000644000175100001660000010462615054022512015734 0ustar runnerdocker#SIXFORMAT GapDocGAP HELPBOOKINFOSIXTMP := rec( encoding := "UTF-8", bookname := "polycyclic", entries := [ [ "Title page", "0.0", [ 0, 0, 0 ], 1, 1, "title page", "X7D2C85EC87DD46E5" ], [ "Copyright", "0.0-1", [ 0, 0, 1 ], 47, 2, "copyright", "X81488B807F2A1CF1" ], [ "Acknowledgements", "0.0-2", [ 0, 0, 2 ], 58, 2, "acknowledgements", "X82A988D47DFAFCFA" ], [ "Table of Contents", "0.0-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 ], 88, 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", "0.0-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 for a subgroup", "5.3-1", [ 5, 3, 1 ], 149, 23, "igs for a subgroup", "X815F756286701BE0" ], [ "\033[2XIgs\033[102X", "5.3-1", [ 5, 3, 1 ], 149, 23, "igs", "X815F756286701BE0" ], [ "\033[2XIgsParallel\033[102X", "5.3-1", [ 5, 3, 1 ], 149, 23, "igsparallel", "X815F756286701BE0" ], [ "\033[2XNgs\033[102X for a subgroup", "5.3-2", [ 5, 3, 2 ], 161, 23, "ngs for a subgroup", "X7F4D95C47F9652BA" ], [ "\033[2XNgs\033[102X", "5.3-2", [ 5, 3, 2 ], 161, 23, "ngs", "X7F4D95C47F9652BA" ], [ "\033[2XCgs\033[102X for a subgroup", "5.3-3", [ 5, 3, 3 ], 169, 23, "cgs for a subgroup", "X8077293A787D4571" ], [ "\033[2XCgs\033[102X", "5.3-3", [ 5, 3, 3 ], 169, 23, "cgs", "X8077293A787D4571" ], [ "\033[2XCgsParallel\033[102X", "5.3-3", [ 5, 3, 3 ], 169, 23, "cgsparallel", "X8077293A787D4571" ], [ "\033[2XSubgroupByIgs\033[102X", "5.3-4", [ 5, 3, 4 ], 187, 23, "subgroupbyigs", "X83B92A2679EAB1EB" ], [ "\033[2XSubgroupByIgs\033[102X with extra generators", "5.3-4", [ 5, 3, 4 ], 187, 23, "subgroupbyigs with extra generators", "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 for a factor", "5.4-1", [ 5, 4, 1 ], 224, 24, "pcp for a factor", "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 for a homomorphism", "5.6-3", [ 5, 6, 3 ], 405, 28, "image for a homomorphism", "X847322667E6166C8" ], [ "\033[2XImage\033[102X for a homomorphism and a subgroup", "5.6-3", [ 5, 6, 3 ], 405, 28, "image for a homomorphism and a subgroup", "X847322667E6166C8" ], [ "\033[2XImage\033[102X for a homomorphism and an element", "5.6-3", [ 5, 6, 3 ], 405, 28, "image for a homomorphism and an element", "X847322667E6166C8" ], [ "\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 for a group", "5.8-1", [ 5, 8, 1 ], 481, 29, "printpcppresentation for a group", "X79D247127FD57FC8" ], [ "\033[2XPrintPcpPresentation\033[102X for a pcp", "5.8-1", [ 5, 8, 1 ], 481, 29, "printpcppresentation for a pcp", "X79D247127FD57FC8" ], [ "\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[2XAbelianPcpGroup\033[102X rels only", "6.1-1", [ 6, 1, 1 ], 9, 31, "abelianpcpgroup rels only", "X7AEDE1BA82014B86" ], [ "\033[2XDihedralPcpGroup\033[102X", "6.1-2", [ 6, 1, 2 ], 19, 31, "dihedralpcpgroup", "X7ACF57737D0F12DB" ], [ "\033[2XUnitriangularPcpGroup\033[102X", "6.1-3", [ 6, 1, 3 ], 27, 31, "unitriangularpcpgroup", "X864CEDAB7911CC79" ], [ "\033[2XSubgroupUnitriangularPcpGroup\033[102X", "6.1-4", [ 6, 1, 4 ], 36, 31, "subgroupunitriangularpcpgroup", "X812E35B17AADBCD5" ], [ "\033[2XInfiniteMetacyclicPcpGroup\033[102X", "6.1-5", [ 6, 1, 5 ], 44, 32, "infinitemetacyclicpcpgroup", "X7A80F7F27FDA6810" ], [ "\033[2XHeisenbergPcpGroup\033[102X", "6.1-6", [ 6, 1, 6 ], 64, 32, "heisenbergpcpgroup", "X81BEC875827D1CC2" ], [ "\033[2XMaximalOrderByUnitsPcpGroup\033[102X", "6.1-7", [ 6, 1, 7 ], 71, 32, "maximalorderbyunitspcpgroup", "X87F9B9C9786430D7" ], [ "\033[2XBurdeGrunewaldPcpGroup\033[102X", "6.1-8", [ 6, 1, 8 ], 80, 32, "burdegrunewaldpcpgroup", "X852283A77A2C93DD" ], [ "\033[2XExampleOfMetabelianPcpGroup\033[102X", "6.2-1", [ 6, 2, 1 ], 95, 32, "exampleofmetabelianpcpgroup", "X86293081865CDFC3" ], [ "\033[2XExamplesOfSomePcpGroups\033[102X", "6.2-2", [ 6, 2, 2 ], 102, 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 for an element", "7.3-1", [ 7, 3, 1 ], 264, 39, "centralizer for an element", "X808EE8AD7EE3ECE1" ], [ "\033[2XIsConjugate\033[102X for elements", "7.3-1", [ 7, 3, 1 ], 264, 39, "isconjugate for elements", "X808EE8AD7EE3ECE1" ], [ "\033[2XCentralizer\033[102X for a subgroup", "7.3-2", [ 7, 3, 2 ], 278, 39, "centralizer for a subgroup", "X849B5C527BAFAAA4" ], [ "\033[2XNormalizer\033[102X", "7.3-2", [ 7, 3, 2 ], 278, 39, "normalizer", "X849B5C527BAFAAA4" ], [ "\033[2XIsConjugate\033[102X for subgroups", "7.3-2", [ 7, 3, 2 ], 278, 39, "isconjugate for subgroups", "X849B5C527BAFAAA4" ], [ "\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 for an element", "7.8-1", [ 7, 8, 1 ], 542, 44, "randomcentralizerpcpgroup for an element", "X80AEE73E7D639699" ], [ "\033[2XRandomCentralizerPcpGroup\033[102X for a subgroup", "7.8-1", [ 7, 8, 1 ], 542, 44, "randomcentralizerpcpgroup for a subgroup", "X80AEE73E7D639699" ], [ "\033[2XRandomNormalizerPcpGroup\033[102X", "7.8-1", [ 7, 8, 1 ], 542, 44, "randomnormalizerpcpgroup", "X80AEE73E7D639699" ], [ "\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.0", [ "A", 0, 0 ], 1, 60, "schurcovering", "X874ECE907CAF380D" ], [ "\033[10XSchurMultPcpGroup\033[110X", "a.0", [ "A", 0, 0 ], 1, 60, "schurmultpcpgroup", "X874ECE907CAF380D" ] ] ); polycyclic-2.17/doc/polycyclic.tex0000644000175100001660000054675615054022512016644 0ustar runnerdocker% generated by GAPDoc2LaTeX from XML source (Frank Luebeck) \documentclass[a4paper,11pt]{report} \usepackage[top=37mm,bottom=37mm,left=27mm,right=27mm]{geometry} \sloppy \pagestyle{myheadings} \usepackage{amssymb} \usepackage[utf8]{inputenc} \usepackage{makeidx} \makeindex \usepackage{color} \definecolor{FireBrick}{rgb}{0.5812,0.0074,0.0083} \definecolor{RoyalBlue}{rgb}{0.0236,0.0894,0.6179} \definecolor{RoyalGreen}{rgb}{0.0236,0.6179,0.0894} \definecolor{RoyalRed}{rgb}{0.6179,0.0236,0.0894} \definecolor{LightBlue}{rgb}{0.8544,0.9511,1.0000} \definecolor{Black}{rgb}{0.0,0.0,0.0} \definecolor{linkColor}{rgb}{0.0,0.0,0.554} \definecolor{citeColor}{rgb}{0.0,0.0,0.554} \definecolor{fileColor}{rgb}{0.0,0.0,0.554} \definecolor{urlColor}{rgb}{0.0,0.0,0.554} \definecolor{promptColor}{rgb}{0.0,0.0,0.589} \definecolor{brkpromptColor}{rgb}{0.589,0.0,0.0} \definecolor{gapinputColor}{rgb}{0.589,0.0,0.0} \definecolor{gapoutputColor}{rgb}{0.0,0.0,0.0} %% for a long time these were red and blue by default, %% now black, but keep variables to overwrite \definecolor{FuncColor}{rgb}{0.0,0.0,0.0} %% strange name because of pdflatex bug: \definecolor{Chapter }{rgb}{0.0,0.0,0.0} \definecolor{DarkOlive}{rgb}{0.1047,0.2412,0.0064} \usepackage{fancyvrb} \usepackage{mathptmx,helvet} \usepackage[T1]{fontenc} \usepackage{textcomp} \usepackage[ pdftex=true, bookmarks=true, a4paper=true, pdftitle={Written with GAPDoc}, pdfcreator={LaTeX with hyperref package / GAPDoc}, colorlinks=true, backref=page, breaklinks=true, linkcolor=linkColor, citecolor=citeColor, filecolor=fileColor, urlcolor=urlColor, pdfpagemode={UseNone}, ]{hyperref} \newcommand{\maintitlesize}{\fontsize{50}{55}\selectfont} % write page numbers to a .pnr log file for online help \newwrite\pagenrlog \immediate\openout\pagenrlog =\jobname.pnr \immediate\write\pagenrlog{PAGENRS := [} \newcommand{\logpage}[1]{\protect\write\pagenrlog{#1, \thepage,}} %% were never documented, give conflicts with some additional packages \newcommand{\GAP}{\textsf{GAP}} %% nicer description environments, allows long labels \usepackage{enumitem} \setdescription{style=nextline} %% depth of toc \setcounter{tocdepth}{1} %% command for ColorPrompt style examples \newcommand{\gapprompt}[1]{\color{promptColor}{\bfseries #1}} \newcommand{\gapbrkprompt}[1]{\color{brkpromptColor}{\bfseries #1}} \newcommand{\gapinput}[1]{\color{gapinputColor}{#1}} \begin{document} \logpage{[ 0, 0, 0 ]} \begin{titlepage} \mbox{}\vfill \begin{center}{\maintitlesize \textbf{ Polycyclic \mbox{}}}\\ \vfill \hypersetup{pdftitle= Polycyclic } \markright{\scriptsize \mbox{}\hfill Polycyclic \hfill\mbox{}} {\Huge \textbf{ Computation with polycyclic groups \mbox{}}}\\ \vfill {\Huge 2.17 \mbox{}}\\[1cm] { 28 August 2025 \mbox{}}\\[1cm] \mbox{}\\[2cm] {\Large \textbf{ Bettina Eick\\ \mbox{}}}\\ {\Large \textbf{ Werner Nickel\\ \mbox{}}}\\ {\Large \textbf{ Max Horn\\ \mbox{}}}\\ \hypersetup{pdfauthor= Bettina Eick\\ ; Werner Nickel\\ ; Max Horn\\ } \end{center}\vfill \mbox{}\\ {\mbox{}\\ \small \noindent \textbf{ Bettina Eick\\ } Email: \href{mailto://beick@tu-bs.de} {\texttt{beick@tu\texttt{\symbol{45}}bs.de}}\\ Homepage: \href{http://www.iaa.tu-bs.de/beick} {\texttt{http://www.iaa.tu\texttt{\symbol{45}}bs.de/beick}}\\ Address: \begin{minipage}[t]{8cm}\noindent Institut Analysis und Algebra\\ TU Braunschweig\\ Universit{\"a}tsplatz 2\\ D\texttt{\symbol{45}}38106 Braunschweig\\ Germany\\ \end{minipage} }\\ {\mbox{}\\ \small \noindent \textbf{ Werner Nickel\\ }\\ Homepage: \href{http://www.mathematik.tu-darmstadt.de/~nickel/} {\texttt{http://www.mathematik.tu\texttt{\symbol{45}}darmstadt.de/\texttt{\symbol{126}}nickel/}}}\\ {\mbox{}\\ \small \noindent \textbf{ Max Horn\\ } Email: \href{mailto://mhorn@rptu.de} {\texttt{mhorn@rptu.de}}\\ Homepage: \href{https://www.quendi.de/math} {\texttt{https://www.quendi.de/math}}\\ Address: \begin{minipage}[t]{8cm}\noindent Fachbereich Mathematik\\ RPTU Kaiserslautern\texttt{\symbol{45}}Landau\\ Gottlieb\texttt{\symbol{45}}Daimler\texttt{\symbol{45}}Stra{\ss}e 48\\ 67663 Kaiserslautern\\ Germany\\ \end{minipage} }\\ \end{titlepage} \newpage\setcounter{page}{2} {\small \section*{Copyright} \logpage{[ 0, 0, 1 ]} \index{License} {\copyright} 2003\texttt{\symbol{45}}2018 by Bettina Eick, Max Horn and Werner Nickel The \textsf{Polycyclic} package is free software; you can redistribute it and/or modify it under the terms of the \href{http://www.fsf.org/licenses/gpl.html} {GNU General Public License} as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. \mbox{}}\\[1cm] {\small \section*{Acknowledgements} \logpage{[ 0, 0, 2 ]} We appreciate very much all past and future comments, suggestions and contributions to this package and its documentation provided by \textsf{GAP} users and developers. \mbox{}}\\[1cm] \newpage \def\contentsname{Contents\logpage{[ 0, 0, 3 ]}} \tableofcontents \newpage \chapter{\textcolor{Chapter }{Preface}}\label{Preface} \logpage{[ 1, 0, 0 ]} \hyperdef{L}{X874E1D45845007FE}{} { A group $G$ is called \emph{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 \cite{Hir38a}, \cite{Hir38b}, \cite{Hir46}, \cite{Hir52}, \cite{Hir54}, and their central position in infinite group theory has been recognised since. A well\texttt{\symbol{45}}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 \emph{pc\texttt{\symbol{45}}presentation} as defined in the Chapter \hyperref[Introduction to polycyclic presentations]{`Introduction to polycyclic presentations'}. Pc\texttt{\symbol{45}}presentations allow efficient computations with the groups they define. In particular, the word problem is efficiently solvable in a group given by a pc\texttt{\symbol{45}}presentation. Further, subgroups and factor groups of groups given by a pc\texttt{\symbol{45}}presentation can be handled effectively. The \textsf{GAP} 4 package \textsf{Polycyclic} is designed for computations with polycyclic groups which are given by a pc\texttt{\symbol{45}}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 \cite{BCRS91} and \cite{Seg90} the theory of problems which are decidable in polycyclic\texttt{\symbol{45}}by\texttt{\symbol{45}}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\texttt{\symbol{45}}presentations, see for example \cite{Eic00}, and this package is a collection of implementations for these and other methods. We refer to \cite{Rob82}, page 147ff, and \cite{Seg83} for background on polycyclic groups. Further, in \cite{Sims94} a variation of the basic methods for groups with pc\texttt{\symbol{45}}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. } \chapter{\textcolor{Chapter }{Introduction to polycyclic presentations}}\label{Introduction to polycyclic presentations} \logpage{[ 2, 0, 0 ]} \hyperdef{L}{X792561B378D95B23}{} { Let $G$ be a polycyclic group and let $G = C_1 \rhd C_2 \ldots C_n\rhd C_{n+1} = 1$ be a \emph{polycyclic series}, that is, a subnormal series of $G$ with non\texttt{\symbol{45}}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 \emph{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 \mbox{\texttt{\mdseries\slshape uniquely}} as $g_1^{e_1}\cdots g_n^{e_n}$ with $e_i\in {\ensuremath{\mathbb Z}}$ 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 \emph{power\texttt{\symbol{45}}conjugate (pc\texttt{\symbol{45}}) 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\texttt{\symbol{45}}presentation if $G$ is given by a presentation of the form above on generators $g_1,\ldots,g_n$. These generators are the \emph{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 \emph{relative order} of $g_i$. If $G$ is given by a pc\texttt{\symbol{45}}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\texttt{\symbol{45}}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 {\ensuremath{\mathbb Z}}$ 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 \emph{collected form}. In general, an element of the group can be represented by more than one collected word. If the pc\texttt{\symbol{45}}presentation has the property that each element of $G$ has precisely one word in collected form, then the presentation is called \emph{confluent} or \emph{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 \textsf{GAP} package \textsf{Polycyclic} is designed for computations with polycyclic groups which are given by consistent pc\texttt{\symbol{45}}presentations. In particular, all the functions described below assume that we compute with a group defined by a consistent pc\texttt{\symbol{45}}presentation. See Chapter \hyperref[Collectors]{`Collectors'} for a routine that checks the consistency of a pc\texttt{\symbol{45}}presentation. A pc\texttt{\symbol{45}}presentation can be interpreted as a \emph{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 \emph{wreath product ordering}, this monoid presentation is a rewriting system. If the pc\texttt{\symbol{45}}presentation is consistent, the rewriting system is confluent. In this package we do not address this aspect of pc\texttt{\symbol{45}}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\texttt{\symbol{45}}presentations and rewriting systems we recommend the book \cite{Sims94}. } \chapter{\textcolor{Chapter }{Collectors}}\label{Collectors} \logpage{[ 3, 0, 0 ]} \hyperdef{L}{X792305CC81E8606A}{} { Let $G$ be a group defined by a pc\texttt{\symbol{45}}presentation as described in the Chapter \hyperref[Introduction to polycyclic presentations]{`Introduction to polycyclic presentations'}. The process for computing the collected form for an arbitrary word in the generators of $G$ is called \emph{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 \emph{collection from the left} as described by \cite{LGS90} and \cite{Sims94} and \emph{combinatorial collection from the left} by \cite{MVL90}. In addition, the package provides access to Hall polynomials computed by Deep Thought for the multiplication in a nilpotent group, see \cite{WWM97} and \cite{LGS98}. The first step in defining a pc\texttt{\symbol{45}}presented group is setting up a data structure that knows the pc\texttt{\symbol{45}}presentation and has routines that perform the collection algorithm with words in the generators of the presentation. Such a data structure is called \emph{a collector}. To describe the right hand sides of the relations in a pc\texttt{\symbol{45}}presentation we use \emph{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]$. \section{\textcolor{Chapter }{Constructing a Collector}}\label{Constructing a Collector} \logpage{[ 3, 1, 0 ]} \hyperdef{L}{X800FD91386C08CD8}{} { A collector for a group given by a pc\texttt{\symbol{45}}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. \subsection{\textcolor{Chapter }{FromTheLeftCollector}} \logpage{[ 3, 1, 1 ]}\nobreak \hyperdef{L}{X8382A4E78706DE65}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{FromTheLeftCollector({\mdseries\slshape n})\index{FromTheLeftCollector@\texttt{FromTheLeftCollector}} \label{FromTheLeftCollector} }\hfill{\scriptsize (operation)}}\\ returns an empty data structure for a collector with \mbox{\texttt{\mdseries\slshape 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 \mbox{\texttt{\mdseries\slshape n}}. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@ftl := FromTheLeftCollector( 4 );| <> !gapprompt@gap>| !gapinput@PcpGroupByCollector( ftl );| Pcp-group with orders [ 0, 0, 0, 0 ] !gapprompt@gap>| !gapinput@IsAbelian(last);| true \end{Verbatim} If the relative order of a generators has been defined (see \texttt{SetRelativeOrder} (\ref{SetRelativeOrder})), 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. } \subsection{\textcolor{Chapter }{SetRelativeOrder}} \logpage{[ 3, 1, 2 ]}\nobreak \hyperdef{L}{X79A308B28183493B}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SetRelativeOrder({\mdseries\slshape coll, i, ro})\index{SetRelativeOrder@\texttt{SetRelativeOrder}} \label{SetRelativeOrder} }\hfill{\scriptsize (operation)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SetRelativeOrderNC({\mdseries\slshape coll, i, ro})\index{SetRelativeOrderNC@\texttt{SetRelativeOrderNC}} \label{SetRelativeOrderNC} }\hfill{\scriptsize (operation)}}\\ set the relative order in collector \mbox{\texttt{\mdseries\slshape coll}} for generator \mbox{\texttt{\mdseries\slshape i}} to \mbox{\texttt{\mdseries\slshape ro}}. The parameter \mbox{\texttt{\mdseries\slshape coll}} is a collector as returned by the function \texttt{FromTheLeftCollector} (\ref{FromTheLeftCollector}), \mbox{\texttt{\mdseries\slshape i}} is a generator number and \mbox{\texttt{\mdseries\slshape ro}} is a non\texttt{\symbol{45}}negative integer. The generator number \mbox{\texttt{\mdseries\slshape i}} is an integer in the range $1,\ldots,n$ where $n$ is the number of generators of the collector. If \mbox{\texttt{\mdseries\slshape ro}} is $0,$ then the generator with number \mbox{\texttt{\mdseries\slshape 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 \mbox{\texttt{\mdseries\slshape ro}} is the relative order of a generator with number \mbox{\texttt{\mdseries\slshape i}} and no power relation is set for that generator, then \mbox{\texttt{\mdseries\slshape ro}} is the order of that generator. The NC version of the function bypasses checks on the range of \mbox{\texttt{\mdseries\slshape i}}. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@ftl := FromTheLeftCollector( 4 );| <> !gapprompt@gap>| !gapinput@for i in [1..4] do SetRelativeOrder( ftl, i, 3 ); od;| !gapprompt@gap>| !gapinput@G := PcpGroupByCollector( ftl );| Pcp-group with orders [ 3, 3, 3, 3 ] !gapprompt@gap>| !gapinput@IsElementaryAbelian( G );| true \end{Verbatim} } \subsection{\textcolor{Chapter }{SetPower}} \logpage{[ 3, 1, 3 ]}\nobreak \hyperdef{L}{X7BC319BA8698420C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SetPower({\mdseries\slshape coll, i, rhs})\index{SetPower@\texttt{SetPower}} \label{SetPower} }\hfill{\scriptsize (operation)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SetPowerNC({\mdseries\slshape coll, i, rhs})\index{SetPowerNC@\texttt{SetPowerNC}} \label{SetPowerNC} }\hfill{\scriptsize (operation)}}\\ set the right hand side of the power relation for generator \mbox{\texttt{\mdseries\slshape i}} in collector \mbox{\texttt{\mdseries\slshape coll}} to (a copy of) \mbox{\texttt{\mdseries\slshape 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 \mbox{\texttt{\mdseries\slshape coll}} is a collector, \mbox{\texttt{\mdseries\slshape i}} is a generator number and \mbox{\texttt{\mdseries\slshape rhs}} is a generators exponent list or an element from a free group. The no\texttt{\symbol{45}}check (NC) version of the function bypasses checks on the range of \mbox{\texttt{\mdseries\slshape i}} and stores \mbox{\texttt{\mdseries\slshape rhs}} (instead of a copy) in the collector. } \subsection{\textcolor{Chapter }{SetConjugate}} \logpage{[ 3, 1, 4 ]}\nobreak \hyperdef{L}{X86A08D887E049347}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SetConjugate({\mdseries\slshape coll, j, i, rhs})\index{SetConjugate@\texttt{SetConjugate}} \label{SetConjugate} }\hfill{\scriptsize (operation)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SetConjugateNC({\mdseries\slshape coll, j, i, rhs})\index{SetConjugateNC@\texttt{SetConjugateNC}} \label{SetConjugateNC} }\hfill{\scriptsize (operation)}}\\ set the right hand side of the conjugate relation for the generators \mbox{\texttt{\mdseries\slshape j}} and \mbox{\texttt{\mdseries\slshape i}} with \mbox{\texttt{\mdseries\slshape j}} larger than \mbox{\texttt{\mdseries\slshape i}}. The parameter \mbox{\texttt{\mdseries\slshape coll}} is a collector, \mbox{\texttt{\mdseries\slshape j}} and \mbox{\texttt{\mdseries\slshape i}} are generator numbers and \mbox{\texttt{\mdseries\slshape 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 \mbox{\texttt{\mdseries\slshape i}} can be negative in order to define conjugation by the inverse of a generator. The no\texttt{\symbol{45}}check (NC) version of the function bypasses checks on the range of \mbox{\texttt{\mdseries\slshape i}} and \mbox{\texttt{\mdseries\slshape j}} and stores \mbox{\texttt{\mdseries\slshape rhs}} (instead of a copy) in the collector. } \subsection{\textcolor{Chapter }{SetCommutator}} \logpage{[ 3, 1, 5 ]}\nobreak \hyperdef{L}{X7B25997C7DF92B6D}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SetCommutator({\mdseries\slshape coll, j, i, rhs})\index{SetCommutator@\texttt{SetCommutator}} \label{SetCommutator} }\hfill{\scriptsize (operation)}}\\ set the right hand side of the conjugate relation for the generators \mbox{\texttt{\mdseries\slshape j}} and \mbox{\texttt{\mdseries\slshape i}} with \mbox{\texttt{\mdseries\slshape j}} larger than \mbox{\texttt{\mdseries\slshape i}} by specifying the commutator of \mbox{\texttt{\mdseries\slshape j}} and \mbox{\texttt{\mdseries\slshape i}}. The parameter \mbox{\texttt{\mdseries\slshape coll}} is a collector, \mbox{\texttt{\mdseries\slshape j}} and \mbox{\texttt{\mdseries\slshape i}} are generator numbers and \mbox{\texttt{\mdseries\slshape rhs}} is a generator exponent list or an element from a free group. The generator number \mbox{\texttt{\mdseries\slshape 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. } \subsection{\textcolor{Chapter }{UpdatePolycyclicCollector}} \logpage{[ 3, 1, 6 ]}\nobreak \hyperdef{L}{X7E9903F57BC5CC24}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{UpdatePolycyclicCollector({\mdseries\slshape coll})\index{UpdatePolycyclicCollector@\texttt{UpdatePolycyclicCollector}} \label{UpdatePolycyclicCollector} }\hfill{\scriptsize (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\texttt{\symbol{45}}trivial conjugate relation of a generator, the corresponding conjugate relation of the inverse generator is calculated. Note that \texttt{UpdatePolycyclicCollector} is automatically called by the function \texttt{PcpGroupByCollector} (see \texttt{PcpGroupByCollector} (\ref{PcpGroupByCollector})). } \subsection{\textcolor{Chapter }{IsConfluent}} \logpage{[ 3, 1, 7 ]}\nobreak \hyperdef{L}{X8006790B86328CE8}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsConfluent({\mdseries\slshape coll})\index{IsConfluent@\texttt{IsConfluent}} \label{IsConfluent} }\hfill{\scriptsize (property)}}\\ tests if the collector \mbox{\texttt{\mdseries\slshape coll}} is confluent. The function returns true or false accordingly. Compare Chapter \ref{Introduction to polycyclic presentations} for a definition of confluence. Note that confluence is automatically checked by the function \texttt{PcpGroupByCollector} (see \texttt{PcpGroupByCollector} (\ref{PcpGroupByCollector})). 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.\] \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@ftl := FromTheLeftCollector( 3 );| <> !gapprompt@gap>| !gapinput@SetRelativeOrder( ftl, 1, 3 );| !gapprompt@gap>| !gapinput@SetConjugate( ftl, 2, 1, [3,1] );| !gapprompt@gap>| !gapinput@SetConjugate( ftl, 3, 1, [2,-1,3,-1] );| !gapprompt@gap>| !gapinput@UpdatePolycyclicCollector( ftl );| !gapprompt@gap>| !gapinput@IsConfluent( ftl );| true \end{Verbatim} 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 \texttt{UpdatePolycyclicCollector}. It is also possible to specify the conjugation by inverse generators. Note that you need to run \texttt{UpdatePolycyclicCollector} after one of the set functions has been used. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@SetConjugate( ftl, 2, -1, [2,-1,3,-1] );| !gapprompt@gap>| !gapinput@SetConjugate( ftl, 3, -1, [2,1] );| !gapprompt@gap>| !gapinput@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 !gapbrkprompt@brk>| !gapinput@| !gapprompt@gap>| !gapinput@UpdatePolycyclicCollector( ftl );| !gapprompt@gap>| !gapinput@IsConfluent( ftl );| true \end{Verbatim} } } \section{\textcolor{Chapter }{Accessing Parts of a Collector}}\label{Accessing Parts of a Collector} \logpage{[ 3, 2, 0 ]} \hyperdef{L}{X818484817C3BAAE6}{} { \subsection{\textcolor{Chapter }{RelativeOrders}} \logpage{[ 3, 2, 1 ]}\nobreak \hyperdef{L}{X7DD0DF677AC1CF10}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RelativeOrders({\mdseries\slshape coll})\index{RelativeOrders@\texttt{RelativeOrders}} \label{RelativeOrders} }\hfill{\scriptsize (attribute)}}\\ returns (a copy of) the list of relative order stored in the collector \mbox{\texttt{\mdseries\slshape coll}}. } \subsection{\textcolor{Chapter }{GetPower}} \logpage{[ 3, 2, 2 ]}\nobreak \hyperdef{L}{X844C0A478735EF4B}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{GetPower({\mdseries\slshape coll, i})\index{GetPower@\texttt{GetPower}} \label{GetPower} }\hfill{\scriptsize (operation)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{GetPowerNC({\mdseries\slshape coll, i})\index{GetPowerNC@\texttt{GetPowerNC}} \label{GetPowerNC} }\hfill{\scriptsize (operation)}}\\ returns a copy of the generator exponent list stored for the right hand side of the power relation of the generator \mbox{\texttt{\mdseries\slshape i}} in the collector \mbox{\texttt{\mdseries\slshape coll}}. The no\texttt{\symbol{45}}check (NC) version of the function bypasses checks on the range of \mbox{\texttt{\mdseries\slshape i}} and does not create a copy before returning the right hand side of the power relation. } \subsection{\textcolor{Chapter }{GetConjugate}} \logpage{[ 3, 2, 3 ]}\nobreak \hyperdef{L}{X865160E07FA93E00}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{GetConjugate({\mdseries\slshape coll, j, i})\index{GetConjugate@\texttt{GetConjugate}} \label{GetConjugate} }\hfill{\scriptsize (operation)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{GetConjugateNC({\mdseries\slshape coll, j, i})\index{GetConjugateNC@\texttt{GetConjugateNC}} \label{GetConjugateNC} }\hfill{\scriptsize (operation)}}\\ returns a copy of the right hand side of the conjugate relation stored for the generators \mbox{\texttt{\mdseries\slshape j}} and \mbox{\texttt{\mdseries\slshape i}} in the collector \mbox{\texttt{\mdseries\slshape coll}} as generator exponent list. The generator \mbox{\texttt{\mdseries\slshape j}} must be larger than \mbox{\texttt{\mdseries\slshape i}}. The no\texttt{\symbol{45}}check (NC) version of the function bypasses checks on the range of \mbox{\texttt{\mdseries\slshape i}} and \mbox{\texttt{\mdseries\slshape j}} and does not create a copy before returning the right hand side of the power relation. } \subsection{\textcolor{Chapter }{NumberOfGenerators}} \logpage{[ 3, 2, 4 ]}\nobreak \hyperdef{L}{X7D6A26A4871FF51A}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NumberOfGenerators({\mdseries\slshape coll})\index{NumberOfGenerators@\texttt{NumberOfGenerators}} \label{NumberOfGenerators} }\hfill{\scriptsize (operation)}}\\ returns the number of generators of the collector \mbox{\texttt{\mdseries\slshape coll}}. } \subsection{\textcolor{Chapter }{ObjByExponents}} \logpage{[ 3, 2, 5 ]}\nobreak \hyperdef{L}{X873ECF388503E5DE}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ObjByExponents({\mdseries\slshape coll, expvec})\index{ObjByExponents@\texttt{ObjByExponents}} \label{ObjByExponents} }\hfill{\scriptsize (operation)}}\\ returns a generator exponent list for the exponent vector \mbox{\texttt{\mdseries\slshape expvec}}. This is the inverse operation to \texttt{ExponentsByObj}. See \texttt{ExponentsByObj} (\ref{ExponentsByObj}) for an example. } \subsection{\textcolor{Chapter }{ExponentsByObj}} \logpage{[ 3, 2, 6 ]}\nobreak \hyperdef{L}{X85BCB97B8021EAD6}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ExponentsByObj({\mdseries\slshape coll, genexp})\index{ExponentsByObj@\texttt{ExponentsByObj}} \label{ExponentsByObj} }\hfill{\scriptsize (operation)}}\\ returns an exponent vector for the generator exponent list \mbox{\texttt{\mdseries\slshape genexp}}. This is the inverse operation to \texttt{ObjByExponents}. The function assumes that the generators in \mbox{\texttt{\mdseries\slshape genexp}} are given in the right order and that the exponents are in the right range. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := UnitriangularPcpGroup( 4, 0 );| Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] !gapprompt@gap>| !gapinput@coll := Collector ( G );| <> !gapprompt@gap>| !gapinput@ObjByExponents( coll, [6,-5,4,3,-2,1] );| [ 1, 6, 2, -5, 3, 4, 4, 3, 5, -2, 6, 1 ] !gapprompt@gap>| !gapinput@ExponentsByObj( coll, last );| [ 6, -5, 4, 3, -2, 1 ] \end{Verbatim} } } \section{\textcolor{Chapter }{Special Features}}\label{Special Features} \logpage{[ 3, 3, 0 ]} \hyperdef{L}{X79AEB3477800DC16}{} { In this section we descibe collectors for nilpotent groups which make use of the special structure of the given pc\texttt{\symbol{45}}presentation. \subsection{\textcolor{Chapter }{IsWeightedCollector}} \logpage{[ 3, 3, 1 ]}\nobreak \hyperdef{L}{X82EE2ACD7B8C178B}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsWeightedCollector({\mdseries\slshape coll})\index{IsWeightedCollector@\texttt{IsWeightedCollector}} \label{IsWeightedCollector} }\hfill{\scriptsize (property)}}\\ checks if there is a function $w$ from the generators of the collector \mbox{\texttt{\mdseries\slshape 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. } \subsection{\textcolor{Chapter }{AddHallPolynomials}} \logpage{[ 3, 3, 2 ]}\nobreak \hyperdef{L}{X7A1D7ED68334282C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{AddHallPolynomials({\mdseries\slshape coll})\index{AddHallPolynomials@\texttt{AddHallPolynomials}} \label{AddHallPolynomials} }\hfill{\scriptsize (function)}}\\ is applicable to a collector which passes \texttt{IsWeightedCollector} and computes the Hall multiplication polynomials for the presentation stored in \mbox{\texttt{\mdseries\slshape coll}}. The default strategy for this collector is set to evaluating those polynomial when multiplying two elements. } \subsection{\textcolor{Chapter }{String}} \logpage{[ 3, 3, 3 ]}\nobreak \hyperdef{L}{X81FB5BE27903EC32}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{String({\mdseries\slshape coll})\index{String@\texttt{String}} \label{String} }\hfill{\scriptsize (attribute)}}\\ converts a collector \mbox{\texttt{\mdseries\slshape coll}} into a string. } \subsection{\textcolor{Chapter }{FTLCollectorPrintTo}} \logpage{[ 3, 3, 4 ]}\nobreak \hyperdef{L}{X7ED466B6807D16FE}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{FTLCollectorPrintTo({\mdseries\slshape file, name, coll})\index{FTLCollectorPrintTo@\texttt{FTLCollectorPrintTo}} \label{FTLCollectorPrintTo} }\hfill{\scriptsize (function)}}\\ stores a collector \mbox{\texttt{\mdseries\slshape coll}} in the file \mbox{\texttt{\mdseries\slshape file}} such that the file can be read back using the function 'Read' into \textsf{GAP} and would then be stored in the variable \mbox{\texttt{\mdseries\slshape name}}. } \subsection{\textcolor{Chapter }{FTLCollectorAppendTo}} \logpage{[ 3, 3, 5 ]}\nobreak \hyperdef{L}{X789D9EB37ECFA9D7}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{FTLCollectorAppendTo({\mdseries\slshape file, name, coll})\index{FTLCollectorAppendTo@\texttt{FTLCollectorAppendTo}} \label{FTLCollectorAppendTo} }\hfill{\scriptsize (function)}}\\ appends a collector \mbox{\texttt{\mdseries\slshape coll}} in the file \mbox{\texttt{\mdseries\slshape file}} such that the file can be read back into \textsf{GAP} and would then be stored in the variable \mbox{\texttt{\mdseries\slshape name}}. } \subsection{\textcolor{Chapter }{UseLibraryCollector}} \logpage{[ 3, 3, 6 ]}\nobreak \hyperdef{L}{X808A26FB873A354F}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{UseLibraryCollector\index{UseLibraryCollector@\texttt{UseLibraryCollector}} \label{UseLibraryCollector} }\hfill{\scriptsize (global variable)}}\\ this property can be set to \texttt{true} for a collector to force a simple from\texttt{\symbol{45}}the\texttt{\symbol{45}}left collection strategy implemented in the \textsf{GAP} language to be used. Its main purpose is to help debug the collection routines. } \subsection{\textcolor{Chapter }{USE{\textunderscore}LIBRARY{\textunderscore}COLLECTOR}} \logpage{[ 3, 3, 7 ]}\nobreak \hyperdef{L}{X844E195C7D55F8BD}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{USE{\textunderscore}LIBRARY{\textunderscore}COLLECTOR\index{USE{\textunderscore}LIBRARY{\textunderscore}COLLECTOR@\texttt{USE{\textunderscore}}\-\texttt{L}\-\texttt{I}\-\texttt{B}\-\texttt{R}\-\texttt{A}\-\texttt{R}\-\texttt{Y{\textunderscore}}\-\texttt{C}\-\texttt{O}\-\texttt{L}\-\texttt{L}\-\texttt{E}\-\texttt{CTOR}} \label{USEuScoreLIBRARYuScoreCOLLECTOR} }\hfill{\scriptsize (global variable)}}\\ this global variable can be set to \texttt{true} to force all collectors to use a simple from\texttt{\symbol{45}}the\texttt{\symbol{45}}left collection strategy implemented in the \textsf{GAP} language to be used. Its main purpose is to help debug the collection routines. } \subsection{\textcolor{Chapter }{DEBUG{\textunderscore}COMBINATORIAL{\textunderscore}COLLECTOR}} \logpage{[ 3, 3, 8 ]}\nobreak \hyperdef{L}{X7945C6B97BECCDA8}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{DEBUG{\textunderscore}COMBINATORIAL{\textunderscore}COLLECTOR\index{DEBUG{\textunderscore}COMBINATORIAL{\textunderscore}COLLECTOR@\texttt{DEB}\-\texttt{U}\-\texttt{G{\textunderscore}}\-\texttt{C}\-\texttt{O}\-\texttt{M}\-\texttt{B}\-\texttt{I}\-\texttt{N}\-\texttt{A}\-\texttt{T}\-\texttt{O}\-\texttt{R}\-\texttt{I}\-\texttt{A}\-\texttt{L{\textunderscore}}\-\texttt{C}\-\texttt{O}\-\texttt{L}\-\texttt{L}\-\texttt{E}\-\texttt{CTOR}} \label{DEBUGuScoreCOMBINATORIALuScoreCOLLECTOR} }\hfill{\scriptsize (global variable)}}\\ this global variable can be set to \texttt{true} to force the comparison of results from the combinatorial collector with the result of an identical collection performed by a simple from\texttt{\symbol{45}}the\texttt{\symbol{45}}left collector. Its main purpose is to help debug the collection routines. } \subsection{\textcolor{Chapter }{USE{\textunderscore}COMBINATORIAL{\textunderscore}COLLECTOR}} \logpage{[ 3, 3, 9 ]}\nobreak \hyperdef{L}{X7BDFB55D7CB33543}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{USE{\textunderscore}COMBINATORIAL{\textunderscore}COLLECTOR\index{USE{\textunderscore}COMBINATORIAL{\textunderscore}COLLECTOR@\texttt{USE{\textunderscore}}\-\texttt{C}\-\texttt{O}\-\texttt{M}\-\texttt{B}\-\texttt{I}\-\texttt{N}\-\texttt{A}\-\texttt{T}\-\texttt{O}\-\texttt{R}\-\texttt{I}\-\texttt{A}\-\texttt{L{\textunderscore}}\-\texttt{C}\-\texttt{O}\-\texttt{L}\-\texttt{L}\-\texttt{E}\-\texttt{CTOR}} \label{USEuScoreCOMBINATORIALuScoreCOLLECTOR} }\hfill{\scriptsize (global variable)}}\\ this global variable can be set to \texttt{false} in order to prevent the combinatorial collector to be used. } } } \chapter{\textcolor{Chapter }{Pcp\texttt{\symbol{45}}groups \texttt{\symbol{45}} polycyclically presented groups}}\label{Pcp-groups - polycyclically presented groups} \logpage{[ 4, 0, 0 ]} \hyperdef{L}{X7E2AF25881CF7307}{} { \section{\textcolor{Chapter }{Pcp\texttt{\symbol{45}}elements \texttt{\symbol{45}}\texttt{\symbol{45}} elements of a pc\texttt{\symbol{45}}presented group}}\label{Pcp-elements -- elements of a pc-presented group} \logpage{[ 4, 1, 0 ]} \hyperdef{L}{X7882F0F57ABEB680}{} { A \emph{pcp\texttt{\symbol{45}}element} is an element of a group defined by a consistent pc\texttt{\symbol{45}}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 {\ensuremath{\mathbb Z}}$ and $0 \leq e_i < r_i$ for $i \in I$. The integer vector $[e_1, \ldots, e_n]$ is called the \emph{exponent vector} of $g$. The following functions can be used to define pcp\texttt{\symbol{45}}elements via their exponent vector or via an arbitrary generator exponent word as introduced in Chapter \ref{Collectors}. \subsection{\textcolor{Chapter }{PcpElementByExponentsNC}} \logpage{[ 4, 1, 1 ]}\nobreak \hyperdef{L}{X786DB93F7862D903}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpElementByExponentsNC({\mdseries\slshape coll, exp})\index{PcpElementByExponentsNC@\texttt{PcpElementByExponentsNC}} \label{PcpElementByExponentsNC} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpElementByExponents({\mdseries\slshape coll, exp})\index{PcpElementByExponents@\texttt{PcpElementByExponents}} \label{PcpElementByExponents} }\hfill{\scriptsize (function)}}\\ returns the pcp\texttt{\symbol{45}}element with exponent vector \mbox{\texttt{\mdseries\slshape exp}}. The exponent vector is considered relative to the defining generators of the pc\texttt{\symbol{45}}presentation. } \subsection{\textcolor{Chapter }{PcpElementByGenExpListNC}} \logpage{[ 4, 1, 2 ]}\nobreak \hyperdef{L}{X7BBB358C7AA64135}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpElementByGenExpListNC({\mdseries\slshape coll, word})\index{PcpElementByGenExpListNC@\texttt{PcpElementByGenExpListNC}} \label{PcpElementByGenExpListNC} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpElementByGenExpList({\mdseries\slshape coll, word})\index{PcpElementByGenExpList@\texttt{PcpElementByGenExpList}} \label{PcpElementByGenExpList} }\hfill{\scriptsize (function)}}\\ returns the pcp\texttt{\symbol{45}}element with generators exponent list \mbox{\texttt{\mdseries\slshape word}}. This list \mbox{\texttt{\mdseries\slshape 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\texttt{\symbol{45}}presentation. These functions return pcp\texttt{\symbol{45}}elements in the category \texttt{IsPcpElement}. Presently, the only representation implemented for this category is \texttt{IsPcpElementRep}. (This allows us to be a little sloppy right now. The basic set of operations for \texttt{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.) } \subsection{\textcolor{Chapter }{IsPcpElement}} \logpage{[ 4, 1, 3 ]}\nobreak \hyperdef{L}{X86083E297D68733B}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsPcpElement({\mdseries\slshape obj})\index{IsPcpElement@\texttt{IsPcpElement}} \label{IsPcpElement} }\hfill{\scriptsize (Category)}}\\ returns true if the object \mbox{\texttt{\mdseries\slshape obj}} is a pcp\texttt{\symbol{45}}element. } \subsection{\textcolor{Chapter }{IsPcpElementCollection}} \logpage{[ 4, 1, 4 ]}\nobreak \hyperdef{L}{X8695069A7D5073B7}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsPcpElementCollection({\mdseries\slshape obj})\index{IsPcpElementCollection@\texttt{IsPcpElementCollection}} \label{IsPcpElementCollection} }\hfill{\scriptsize (Category)}}\\ returns true if the object \mbox{\texttt{\mdseries\slshape obj}} is a collection of pcp\texttt{\symbol{45}}elements. } \subsection{\textcolor{Chapter }{IsPcpElementRep}} \logpage{[ 4, 1, 5 ]}\nobreak \hyperdef{L}{X7F2C83AD862910B9}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsPcpElementRep({\mdseries\slshape obj})\index{IsPcpElementRep@\texttt{IsPcpElementRep}} \label{IsPcpElementRep} }\hfill{\scriptsize (Representation)}}\\ returns true if the object \mbox{\texttt{\mdseries\slshape obj}} is represented as a pcp\texttt{\symbol{45}}element. } \subsection{\textcolor{Chapter }{IsPcpGroup}} \logpage{[ 4, 1, 6 ]}\nobreak \hyperdef{L}{X8470284A78A6C41B}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsPcpGroup({\mdseries\slshape obj})\index{IsPcpGroup@\texttt{IsPcpGroup}} \label{IsPcpGroup} }\hfill{\scriptsize (Filter)}}\\ returns true if the object \mbox{\texttt{\mdseries\slshape obj}} is a group and also a pcp\texttt{\symbol{45}}element collection. } } \section{\textcolor{Chapter }{Methods for pcp\texttt{\symbol{45}}elements}}\label{Methods for pcp-elements} \logpage{[ 4, 2, 0 ]} \hyperdef{L}{X790471D07A953E12}{} { Now we can describe attributes and functions for pcp\texttt{\symbol{45}}elements. The four basic attributes of a pcp\texttt{\symbol{45}}element, \texttt{Collector}, \texttt{Exponents}, \texttt{GenExpList} and \texttt{NameTag} are computed at the creation of the pcp\texttt{\symbol{45}}element. All other attributes are determined at runtime. Let \mbox{\texttt{\mdseries\slshape g}} be a pcp\texttt{\symbol{45}}element and $g_1, \ldots, g_n$ a polycyclic generating sequence of the underlying pc\texttt{\symbol{45}}presented group. Let $C_1, \ldots, C_n$ be the polycyclic series defined by $g_1, \ldots, g_n$. The \emph{depth} of a non\texttt{\symbol{45}}trivial element $g$ of a pcp\texttt{\symbol{45}}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 \emph{leading exponent} of $g$. If $g$ has depth $i$, then we call $r_i = [C_i:C_{i+1}]$ the \emph{factor order} of $g$. If $r < \infty$, then the smallest positive integer $l$ with $g^l \in C_{i+1}$ is the called \emph{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 \emph{relative index} of $g$. We have that $r = el$. We call a pcp\texttt{\symbol{45}}element \emph{normed}, if its leading exponent is equal to its relative index. For each pcp\texttt{\symbol{45}}element $g$ there exists an integer $e$ such that $g^e$ is normed. \subsection{\textcolor{Chapter }{Collector}} \logpage{[ 4, 2, 1 ]}\nobreak \hyperdef{L}{X7E2D258B7DCE8AC9}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Collector({\mdseries\slshape g})\index{Collector@\texttt{Collector}} \label{Collector} }\hfill{\scriptsize (operation)}}\\ the collector to which the pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}} belongs. } \subsection{\textcolor{Chapter }{Exponents}} \logpage{[ 4, 2, 2 ]}\nobreak \hyperdef{L}{X85C672E78630C507}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Exponents({\mdseries\slshape g})\index{Exponents@\texttt{Exponents}} \label{Exponents} }\hfill{\scriptsize (operation)}}\\ returns the exponent vector of the pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}} with respect to the defining generating set of the underlying collector. } \subsection{\textcolor{Chapter }{GenExpList}} \logpage{[ 4, 2, 3 ]}\nobreak \hyperdef{L}{X8571F6FB7E74346C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{GenExpList({\mdseries\slshape g})\index{GenExpList@\texttt{GenExpList}} \label{GenExpList} }\hfill{\scriptsize (operation)}}\\ returns the generators exponent list of the pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}} with respect to the defining generating set of the underlying collector. } \subsection{\textcolor{Chapter }{NameTag}} \logpage{[ 4, 2, 4 ]}\nobreak \hyperdef{L}{X82252C5E7B011559}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NameTag({\mdseries\slshape g})\index{NameTag@\texttt{NameTag}} \label{NameTag} }\hfill{\scriptsize (operation)}}\\ the name used for printing the pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}}. Printing is done by using the name tag and appending the generator number of \mbox{\texttt{\mdseries\slshape g}}. } \subsection{\textcolor{Chapter }{Depth}} \logpage{[ 4, 2, 5 ]}\nobreak \hyperdef{L}{X840D32D9837E99F5}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Depth({\mdseries\slshape g})\index{Depth@\texttt{Depth}} \label{Depth} }\hfill{\scriptsize (operation)}}\\ returns the depth of the pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}} relative to the defining generators. } \subsection{\textcolor{Chapter }{LeadingExponent}} \logpage{[ 4, 2, 6 ]}\nobreak \hyperdef{L}{X874F1EC178721833}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{LeadingExponent({\mdseries\slshape g})\index{LeadingExponent@\texttt{LeadingExponent}} \label{LeadingExponent} }\hfill{\scriptsize (operation)}}\\ returns the leading exponent of pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}} relative to the defining generators. If \mbox{\texttt{\mdseries\slshape g}} is the identity element, the functions returns 'fail' } \subsection{\textcolor{Chapter }{RelativeOrder}} \logpage{[ 4, 2, 7 ]}\nobreak \hyperdef{L}{X8008AB61823A76B7}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RelativeOrder({\mdseries\slshape g})\index{RelativeOrder@\texttt{RelativeOrder}} \label{RelativeOrder} }\hfill{\scriptsize (attribute)}}\\ returns the relative order of the pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}} with respect to the defining generators. } \subsection{\textcolor{Chapter }{RelativeIndex}} \logpage{[ 4, 2, 8 ]}\nobreak \hyperdef{L}{X875D04288577015B}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RelativeIndex({\mdseries\slshape g})\index{RelativeIndex@\texttt{RelativeIndex}} \label{RelativeIndex} }\hfill{\scriptsize (attribute)}}\\ returns the relative index of the pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}} with respect to the defining generators. } \subsection{\textcolor{Chapter }{FactorOrder}} \logpage{[ 4, 2, 9 ]}\nobreak \hyperdef{L}{X87E070747955F2C1}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{FactorOrder({\mdseries\slshape g})\index{FactorOrder@\texttt{FactorOrder}} \label{FactorOrder} }\hfill{\scriptsize (attribute)}}\\ returns the factor order of the pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}} with respect to the defining generators. } \subsection{\textcolor{Chapter }{NormingExponent}} \logpage{[ 4, 2, 10 ]}\nobreak \hyperdef{L}{X79A247797F0A8583}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NormingExponent({\mdseries\slshape g})\index{NormingExponent@\texttt{NormingExponent}} \label{NormingExponent} }\hfill{\scriptsize (function)}}\\ returns a positive integer $e$ such that the pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}} raised to the power of $e$ is normed. } \subsection{\textcolor{Chapter }{NormedPcpElement}} \logpage{[ 4, 2, 11 ]}\nobreak \hyperdef{L}{X798BB22B80833441}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NormedPcpElement({\mdseries\slshape g})\index{NormedPcpElement@\texttt{NormedPcpElement}} \label{NormedPcpElement} }\hfill{\scriptsize (function)}}\\ returns the normed element corresponding to the pcp\texttt{\symbol{45}}element \mbox{\texttt{\mdseries\slshape g}}. } } \section{\textcolor{Chapter }{Pcp\texttt{\symbol{45}}groups \texttt{\symbol{45}} groups of pcp\texttt{\symbol{45}}elements}}\label{pcpgroup} \logpage{[ 4, 3, 0 ]} \hyperdef{L}{X7A4EF7C68151905A}{} { A \emph{pcp\texttt{\symbol{45}}group} is a group consisting of pcp\texttt{\symbol{45}}elements such that all pcp\texttt{\symbol{45}}elements in the group share the same collector. Thus the group $G$ defined by a polycyclic presentation and all its subgroups are pcp\texttt{\symbol{45}}groups. \subsection{\textcolor{Chapter }{PcpGroupByCollector}} \logpage{[ 4, 3, 1 ]}\nobreak \hyperdef{L}{X7C8FBCAB7F63FACB}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpGroupByCollector({\mdseries\slshape coll})\index{PcpGroupByCollector@\texttt{PcpGroupByCollector}} \label{PcpGroupByCollector} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpGroupByCollectorNC({\mdseries\slshape coll})\index{PcpGroupByCollectorNC@\texttt{PcpGroupByCollectorNC}} \label{PcpGroupByCollectorNC} }\hfill{\scriptsize (function)}}\\ returns a pcp\texttt{\symbol{45}}group build from the collector \mbox{\texttt{\mdseries\slshape coll}}. The function calls \texttt{UpdatePolycyclicCollector} (\ref{UpdatePolycyclicCollector}) and checks the confluence (see \texttt{IsConfluent} (\ref{IsConfluent})) of the collector. The non\texttt{\symbol{45}}check version bypasses these checks. } \subsection{\textcolor{Chapter }{Group}} \logpage{[ 4, 3, 2 ]}\nobreak \hyperdef{L}{X7D7B075385435151}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Group({\mdseries\slshape gens, id})\index{Group@\texttt{Group}} \label{Group} }\hfill{\scriptsize (function)}}\\ returns the group generated by the pcp\texttt{\symbol{45}}elements \mbox{\texttt{\mdseries\slshape gens}} with identity \mbox{\texttt{\mdseries\slshape id}}. } \subsection{\textcolor{Chapter }{Subgroup}} \logpage{[ 4, 3, 3 ]}\nobreak \hyperdef{L}{X7C82AA387A42DCA0}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Subgroup({\mdseries\slshape G, gens})\index{Subgroup@\texttt{Subgroup}} \label{Subgroup} }\hfill{\scriptsize (function)}}\\ returns a subgroup of the pcp\texttt{\symbol{45}}group \mbox{\texttt{\mdseries\slshape G}} generated by the list \mbox{\texttt{\mdseries\slshape gens}} of pcp\texttt{\symbol{45}}elements from \mbox{\texttt{\mdseries\slshape G}}. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@ ftl := FromTheLeftCollector( 2 );;| !gapprompt@gap>| !gapinput@ SetRelativeOrder( ftl, 1, 2 );| !gapprompt@gap>| !gapinput@ SetConjugate( ftl, 2, 1, [2,-1] );| !gapprompt@gap>| !gapinput@ UpdatePolycyclicCollector( ftl );| !gapprompt@gap>| !gapinput@ G:= PcpGroupByCollectorNC( ftl );| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@Subgroup( G, GeneratorsOfGroup(G){[2]} );| Pcp-group with orders [ 0 ] \end{Verbatim} } } } \chapter{\textcolor{Chapter }{Basic methods and functions for pcp\texttt{\symbol{45}}groups}}\label{Basic methods and functions for pcp-groups} \logpage{[ 5, 0, 0 ]} \hyperdef{L}{X7B9B85AE7C9B13EE}{} { Pcp\texttt{\symbol{45}}groups are groups in the \textsf{GAP} sense and hence all generic \textsf{GAP} methods for groups can be applied for pcp\texttt{\symbol{45}}groups. However, for a number of group theoretic questions \textsf{GAP} does not provide generic methods that can be applied to pcp\texttt{\symbol{45}}groups. For some of these questions there are functions provided in \textsf{Polycyclic}. \section{\textcolor{Chapter }{Elementary methods for pcp\texttt{\symbol{45}}groups}}\label{methods} \logpage{[ 5, 1, 0 ]} \hyperdef{L}{X821360107E355B88}{} { In this chapter we describe some important basic functions which are available for pcp\texttt{\symbol{45}}groups. A number of higher level functions are outlined in later sections and chapters. Let $U, V$ and $N$ be subgroups of a pcp\texttt{\symbol{45}}group. \subsection{\textcolor{Chapter }{\texttt{\symbol{92}}=}} \logpage{[ 5, 1, 1 ]}\nobreak \hyperdef{L}{X806A4814806A4814}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{\texttt{\symbol{92}}=({\mdseries\slshape U, V})\index{\texttt{\symbol{92}}=@\texttt{\texttt{\symbol{92}}=}} \label{bSlash=} }\hfill{\scriptsize (method)}}\\ decides if \mbox{\texttt{\mdseries\slshape U}} and \mbox{\texttt{\mdseries\slshape V}} are equal as sets. } \subsection{\textcolor{Chapter }{Size}} \logpage{[ 5, 1, 2 ]}\nobreak \hyperdef{L}{X858ADA3B7A684421}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Size({\mdseries\slshape U})\index{Size@\texttt{Size}} \label{Size} }\hfill{\scriptsize (method)}}\\ returns the size of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{Random}} \logpage{[ 5, 1, 3 ]}\nobreak \hyperdef{L}{X79730D657AB219DB}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Random({\mdseries\slshape U})\index{Random@\texttt{Random}} \label{Random} }\hfill{\scriptsize (method)}}\\ returns a random element of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{Index}} \logpage{[ 5, 1, 4 ]}\nobreak \hyperdef{L}{X83A0356F839C696F}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Index({\mdseries\slshape U, V})\index{Index@\texttt{Index}} \label{Index} }\hfill{\scriptsize (method)}}\\ returns the index of \mbox{\texttt{\mdseries\slshape V}} in \mbox{\texttt{\mdseries\slshape U}} if \mbox{\texttt{\mdseries\slshape V}} is a subgroup of \mbox{\texttt{\mdseries\slshape U}}. The function does not check if \mbox{\texttt{\mdseries\slshape V}} is a subgroup of \mbox{\texttt{\mdseries\slshape U}} and if it is not, the result is not meaningful. } \subsection{\textcolor{Chapter }{\texttt{\symbol{92}}in}} \logpage{[ 5, 1, 5 ]}\nobreak \hyperdef{L}{X87BDB89B7AAFE8AD}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{\texttt{\symbol{92}}in({\mdseries\slshape g, U})\index{\texttt{\symbol{92}}in@\texttt{\texttt{\symbol{92}}in}} \label{bSlashin} }\hfill{\scriptsize (method)}}\\ checks if \mbox{\texttt{\mdseries\slshape g}} is an element of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{Elements}} \logpage{[ 5, 1, 6 ]}\nobreak \hyperdef{L}{X79B130FC7906FB4C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Elements({\mdseries\slshape U})\index{Elements@\texttt{Elements}} \label{Elements} }\hfill{\scriptsize (method)}}\\ returns a list containing all elements of \mbox{\texttt{\mdseries\slshape U}} if \mbox{\texttt{\mdseries\slshape U}} is finite and it returns the list [fail] otherwise. } \subsection{\textcolor{Chapter }{ClosureGroup}} \logpage{[ 5, 1, 7 ]}\nobreak \hyperdef{L}{X7D13FC1F8576FFD8}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ClosureGroup({\mdseries\slshape U, V})\index{ClosureGroup@\texttt{ClosureGroup}} \label{ClosureGroup} }\hfill{\scriptsize (method)}}\\ returns the group generated by \mbox{\texttt{\mdseries\slshape U}} and \mbox{\texttt{\mdseries\slshape V}}. } \subsection{\textcolor{Chapter }{NormalClosure}} \logpage{[ 5, 1, 8 ]}\nobreak \hyperdef{L}{X7BDEA0A98720D1BB}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NormalClosure({\mdseries\slshape U, V})\index{NormalClosure@\texttt{NormalClosure}} \label{NormalClosure} }\hfill{\scriptsize (method)}}\\ returns the normal closure of \mbox{\texttt{\mdseries\slshape V}} under action of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{HirschLength}} \logpage{[ 5, 1, 9 ]}\nobreak \hyperdef{L}{X839B42AE7A1DD544}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{HirschLength({\mdseries\slshape U})\index{HirschLength@\texttt{HirschLength}} \label{HirschLength} }\hfill{\scriptsize (method)}}\\ returns the Hirsch length of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{CommutatorSubgroup}} \logpage{[ 5, 1, 10 ]}\nobreak \hyperdef{L}{X7A9A3D5578CE33A0}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{CommutatorSubgroup({\mdseries\slshape U, V})\index{CommutatorSubgroup@\texttt{CommutatorSubgroup}} \label{CommutatorSubgroup} }\hfill{\scriptsize (method)}}\\ returns the group generated by all commutators $[u,v]$ with $u$ in \mbox{\texttt{\mdseries\slshape U}} and $v$ in \mbox{\texttt{\mdseries\slshape V}}. } \subsection{\textcolor{Chapter }{PRump}} \logpage{[ 5, 1, 11 ]}\nobreak \hyperdef{L}{X796DA805853FAC90}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PRump({\mdseries\slshape U, p})\index{PRump@\texttt{PRump}} \label{PRump} }\hfill{\scriptsize (method)}}\\ returns the subgroup $U'U^p$ of \mbox{\texttt{\mdseries\slshape U}} where \mbox{\texttt{\mdseries\slshape p}} is a prime number. } \subsection{\textcolor{Chapter }{SmallGeneratingSet}} \logpage{[ 5, 1, 12 ]}\nobreak \hyperdef{L}{X814DBABC878D5232}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SmallGeneratingSet({\mdseries\slshape U})\index{SmallGeneratingSet@\texttt{SmallGeneratingSet}} \label{SmallGeneratingSet} }\hfill{\scriptsize (method)}}\\ returns a small generating set for \mbox{\texttt{\mdseries\slshape U}}. } } \section{\textcolor{Chapter }{Elementary properties of pcp\texttt{\symbol{45}}groups}}\label{Elementary properties of pcp-groups} \logpage{[ 5, 2, 0 ]} \hyperdef{L}{X80E88168866D54F3}{} { \subsection{\textcolor{Chapter }{IsSubgroup}} \logpage{[ 5, 2, 1 ]}\nobreak \hyperdef{L}{X7839D8927E778334}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsSubgroup({\mdseries\slshape U, V})\index{IsSubgroup@\texttt{IsSubgroup}} \label{IsSubgroup} }\hfill{\scriptsize (function)}}\\ tests if \mbox{\texttt{\mdseries\slshape V}} is a subgroup of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{IsNormal}} \logpage{[ 5, 2, 2 ]}\nobreak \hyperdef{L}{X838186F9836F678C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsNormal({\mdseries\slshape U, V})\index{IsNormal@\texttt{IsNormal}} \label{IsNormal} }\hfill{\scriptsize (function)}}\\ tests if \mbox{\texttt{\mdseries\slshape V}} is normal in \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{IsNilpotentGroup}} \logpage{[ 5, 2, 3 ]}\nobreak \hyperdef{L}{X87D062608719F2CD}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsNilpotentGroup({\mdseries\slshape U})\index{IsNilpotentGroup@\texttt{IsNilpotentGroup}} \label{IsNilpotentGroup} }\hfill{\scriptsize (method)}}\\ checks whether \mbox{\texttt{\mdseries\slshape U}} is nilpotent. } \subsection{\textcolor{Chapter }{IsAbelian}} \logpage{[ 5, 2, 4 ]}\nobreak \hyperdef{L}{X7C12AA7479A6C103}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsAbelian({\mdseries\slshape U})\index{IsAbelian@\texttt{IsAbelian}} \label{IsAbelian} }\hfill{\scriptsize (method)}}\\ checks whether \mbox{\texttt{\mdseries\slshape U}} is abelian. } \subsection{\textcolor{Chapter }{IsElementaryAbelian}} \logpage{[ 5, 2, 5 ]}\nobreak \hyperdef{L}{X813C952F80E775D4}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsElementaryAbelian({\mdseries\slshape U})\index{IsElementaryAbelian@\texttt{IsElementaryAbelian}} \label{IsElementaryAbelian} }\hfill{\scriptsize (method)}}\\ checks whether \mbox{\texttt{\mdseries\slshape U}} is elementary abelian. } \subsection{\textcolor{Chapter }{IsFreeAbelian}} \logpage{[ 5, 2, 6 ]}\nobreak \hyperdef{L}{X84FFC668832F9ED6}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsFreeAbelian({\mdseries\slshape U})\index{IsFreeAbelian@\texttt{IsFreeAbelian}} \label{IsFreeAbelian} }\hfill{\scriptsize (property)}}\\ checks whether \mbox{\texttt{\mdseries\slshape U}} is free abelian. } } \section{\textcolor{Chapter }{Subgroups of pcp\texttt{\symbol{45}}groups}}\label{Subgroups of pcp-groups} \logpage{[ 5, 3, 0 ]} \hyperdef{L}{X85A7E26C7E14AFBA}{} { A subgroup of a pcp\texttt{\symbol{45}}group $G$ can be defined by a set of generators as described in Section \ref{pcpgroup}. However, many computations with a subgroup $U$ need an \emph{induced generating sequence} or \emph{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\texttt{\symbol{45}}group $G$ is a list of elements of $G$. An igs is called \emph{normed}, if each element in the list is normed. Moreover, it is \emph{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 \mbox{\texttt{\mdseries\slshape U}} of \mbox{\texttt{\mdseries\slshape G}}. \subsection{\textcolor{Chapter }{Igs (for a subgroup)}} \logpage{[ 5, 3, 1 ]}\nobreak \hyperdef{L}{X815F756286701BE0}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Igs({\mdseries\slshape U})\index{Igs@\texttt{Igs}!for a subgroup} \label{Igs:for a subgroup} }\hfill{\scriptsize (attribute)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Igs({\mdseries\slshape gens})\index{Igs@\texttt{Igs}} \label{Igs} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IgsParallel({\mdseries\slshape gens, gens2})\index{IgsParallel@\texttt{IgsParallel}} \label{IgsParallel} }\hfill{\scriptsize (function)}}\\ returns an induced generating sequence of the subgroup \mbox{\texttt{\mdseries\slshape U}} of a pcp\texttt{\symbol{45}}group. In the second form the subgroup is given via a generating set \mbox{\texttt{\mdseries\slshape gens}}. The third form computes an igs for the subgroup generated by \mbox{\texttt{\mdseries\slshape gens}} carrying \mbox{\texttt{\mdseries\slshape gens2}} through as shadows. This means that each operation that is applied to the first list is also applied to the second list. } \subsection{\textcolor{Chapter }{Ngs (for a subgroup)}} \logpage{[ 5, 3, 2 ]}\nobreak \hyperdef{L}{X7F4D95C47F9652BA}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Ngs({\mdseries\slshape U})\index{Ngs@\texttt{Ngs}!for a subgroup} \label{Ngs:for a subgroup} }\hfill{\scriptsize (attribute)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Ngs({\mdseries\slshape igs})\index{Ngs@\texttt{Ngs}} \label{Ngs} }\hfill{\scriptsize (function)}}\\ returns a normed induced generating sequence of the subgroup \mbox{\texttt{\mdseries\slshape U}} of a pcp\texttt{\symbol{45}}group. The second form takes an igs as input and norms it. } \subsection{\textcolor{Chapter }{Cgs (for a subgroup)}} \logpage{[ 5, 3, 3 ]}\nobreak \hyperdef{L}{X8077293A787D4571}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Cgs({\mdseries\slshape U})\index{Cgs@\texttt{Cgs}!for a subgroup} \label{Cgs:for a subgroup} }\hfill{\scriptsize (attribute)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Cgs({\mdseries\slshape igs})\index{Cgs@\texttt{Cgs}} \label{Cgs} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{CgsParallel({\mdseries\slshape gens, gens2})\index{CgsParallel@\texttt{CgsParallel}} \label{CgsParallel} }\hfill{\scriptsize (function)}}\\ returns a canonical generating sequence of the subgroup \mbox{\texttt{\mdseries\slshape U}} of a pcp\texttt{\symbol{45}}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 \mbox{\texttt{\mdseries\slshape 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\texttt{\symbol{45}}groups \mbox{\texttt{\mdseries\slshape U}} we will first of all determine an \mbox{\texttt{\mdseries\slshape igs}} for \mbox{\texttt{\mdseries\slshape U}}. Hence it might speed up computations, if a known \mbox{\texttt{\mdseries\slshape igs}} for a group \mbox{\texttt{\mdseries\slshape U}} is set \emph{a priori}. The following functions can be used for this purpose. } \subsection{\textcolor{Chapter }{SubgroupByIgs}} \logpage{[ 5, 3, 4 ]}\nobreak \hyperdef{L}{X83B92A2679EAB1EB}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SubgroupByIgs({\mdseries\slshape G, igs})\index{SubgroupByIgs@\texttt{SubgroupByIgs}} \label{SubgroupByIgs} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SubgroupByIgs({\mdseries\slshape G, igs, gens})\index{SubgroupByIgs@\texttt{SubgroupByIgs}!with extra generators} \label{SubgroupByIgs:with extra generators} }\hfill{\scriptsize (function)}}\\ returns the subgroup of the pcp\texttt{\symbol{45}}group \mbox{\texttt{\mdseries\slshape G}} generated by the elements of the induced generating sequence \mbox{\texttt{\mdseries\slshape igs}}. Note that \mbox{\texttt{\mdseries\slshape igs}} must be an induced generating sequence of the subgroup generated by the elements of the \mbox{\texttt{\mdseries\slshape igs}}. In the second form \mbox{\texttt{\mdseries\slshape igs}} is a igs for a subgroup and \mbox{\texttt{\mdseries\slshape gens}} are some generators. The function returns the subgroup generated by \mbox{\texttt{\mdseries\slshape igs}} and \mbox{\texttt{\mdseries\slshape gens}}. } \subsection{\textcolor{Chapter }{AddToIgs}} \logpage{[ 5, 3, 5 ]}\nobreak \hyperdef{L}{X78107DE78728B26B}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{AddToIgs({\mdseries\slshape igs, gens})\index{AddToIgs@\texttt{AddToIgs}} \label{AddToIgs} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{AddToIgsParallel({\mdseries\slshape igs, gens, igs2, gens2})\index{AddToIgsParallel@\texttt{AddToIgsParallel}} \label{AddToIgsParallel} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{AddIgsToIgs({\mdseries\slshape igs, igs2})\index{AddIgsToIgs@\texttt{AddIgsToIgs}} \label{AddIgsToIgs} }\hfill{\scriptsize (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 \mbox{\texttt{\mdseries\slshape gens}} is also applied to the second list and the element \mbox{\texttt{\mdseries\slshape gens2}}. The third version is available for efficiency reasons and assumes that the second list \mbox{\texttt{\mdseries\slshape igs2}} is not only a generating set, but an igs. } } \section{\textcolor{Chapter }{Polycyclic presentation sequences for subfactors}}\label{pcps} \logpage{[ 5, 4, 0 ]} \hyperdef{L}{X803D62BC86EF07D0}{} { A subfactor of a pcp\texttt{\symbol{45}}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\texttt{\symbol{45}}consuming. Hence we introduce \emph{polycyclic presentation sequences} or \emph{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\texttt{\symbol{45}}group $U$ or a subfactor $U / N$ can be created with one of the following functions. \subsection{\textcolor{Chapter }{Pcp}} \logpage{[ 5, 4, 1 ]}\nobreak \hyperdef{L}{X7DD931697DD93169}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Pcp({\mdseries\slshape U[, flag]})\index{Pcp@\texttt{Pcp}} \label{Pcp} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Pcp({\mdseries\slshape U, N[, flag]})\index{Pcp@\texttt{Pcp}!for a factor} \label{Pcp:for a factor} }\hfill{\scriptsize (function)}}\\ returns a polycyclic presentation sequence for the subgroup \mbox{\texttt{\mdseries\slshape U}} or the quotient group \mbox{\texttt{\mdseries\slshape U}} modulo \mbox{\texttt{\mdseries\slshape N}}. If the parameter \mbox{\texttt{\mdseries\slshape flag}} is present and equals the string ``snf'', the function can only be applied to an abelian subgroup \mbox{\texttt{\mdseries\slshape U}} or abelian subfactor \mbox{\texttt{\mdseries\slshape U}}/\mbox{\texttt{\mdseries\slshape 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\texttt{\symbol{45}}group $G$. \subsection{\textcolor{Chapter }{GeneratorsOfPcp}} \logpage{[ 5, 4, 2 ]}\nobreak \hyperdef{L}{X821FF77086E38B3A}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{GeneratorsOfPcp({\mdseries\slshape pcp})\index{GeneratorsOfPcp@\texttt{GeneratorsOfPcp}} \label{GeneratorsOfPcp} }\hfill{\scriptsize (function)}}\\ this returns a list of elements of $U$ corresponding to an igs of $U/N$. } \subsection{\textcolor{Chapter }{\texttt{\symbol{92}}[\texttt{\symbol{92}}]}} \logpage{[ 5, 4, 3 ]}\nobreak \hyperdef{L}{X8297BBCD79642BE6}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{\texttt{\symbol{92}}[\texttt{\symbol{92}}]({\mdseries\slshape pcp, i})\index{\texttt{\symbol{92}}[\texttt{\symbol{92}}]@\texttt{\texttt{\symbol{92}}[\texttt{\symbol{92}}]}} \label{bSlash[bSlash]} }\hfill{\scriptsize (method)}}\\ returns the \mbox{\texttt{\mdseries\slshape i}}\texttt{\symbol{45}}th element of \mbox{\texttt{\mdseries\slshape pcp}}. } \subsection{\textcolor{Chapter }{Length}} \logpage{[ 5, 4, 4 ]}\nobreak \hyperdef{L}{X780769238600AFD1}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Length({\mdseries\slshape pcp})\index{Length@\texttt{Length}} \label{Length} }\hfill{\scriptsize (method)}}\\ returns the number of generators in \mbox{\texttt{\mdseries\slshape pcp}}. } \subsection{\textcolor{Chapter }{RelativeOrdersOfPcp}} \logpage{[ 5, 4, 5 ]}\nobreak \hyperdef{L}{X7ABCA7F2790E1673}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RelativeOrdersOfPcp({\mdseries\slshape pcp})\index{RelativeOrdersOfPcp@\texttt{RelativeOrdersOfPcp}} \label{RelativeOrdersOfPcp} }\hfill{\scriptsize (function)}}\\ the relative orders of the igs in \mbox{\texttt{\mdseries\slshape U/N}}. } \subsection{\textcolor{Chapter }{DenominatorOfPcp}} \logpage{[ 5, 4, 6 ]}\nobreak \hyperdef{L}{X7D16C299825887AA}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{DenominatorOfPcp({\mdseries\slshape pcp})\index{DenominatorOfPcp@\texttt{DenominatorOfPcp}} \label{DenominatorOfPcp} }\hfill{\scriptsize (function)}}\\ returns an igs of \mbox{\texttt{\mdseries\slshape N}}. } \subsection{\textcolor{Chapter }{NumeratorOfPcp}} \logpage{[ 5, 4, 7 ]}\nobreak \hyperdef{L}{X803AED1A84FCBEE8}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NumeratorOfPcp({\mdseries\slshape pcp})\index{NumeratorOfPcp@\texttt{NumeratorOfPcp}} \label{NumeratorOfPcp} }\hfill{\scriptsize (function)}}\\ returns an igs of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{GroupOfPcp}} \logpage{[ 5, 4, 8 ]}\nobreak \hyperdef{L}{X80BCCF0B81344933}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{GroupOfPcp({\mdseries\slshape pcp})\index{GroupOfPcp@\texttt{GroupOfPcp}} \label{GroupOfPcp} }\hfill{\scriptsize (function)}}\\ returns \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{OneOfPcp}} \logpage{[ 5, 4, 9 ]}\nobreak \hyperdef{L}{X87F0BA5F7BA0F4B4}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{OneOfPcp({\mdseries\slshape pcp})\index{OneOfPcp@\texttt{OneOfPcp}} \label{OneOfPcp} }\hfill{\scriptsize (function)}}\\ returns the identity element of \mbox{\texttt{\mdseries\slshape G}}. } The main feature of a pcp are the possibility to compute exponent vectors without having to determine an explicit pcp\texttt{\symbol{45}}group corresponding to the subfactor that is represented by the pcp. Nonetheless, it is possible to determine this subfactor. \subsection{\textcolor{Chapter }{ExponentsByPcp}} \logpage{[ 5, 4, 10 ]}\nobreak \hyperdef{L}{X7A8C8BBC81581E09}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ExponentsByPcp({\mdseries\slshape pcp, g})\index{ExponentsByPcp@\texttt{ExponentsByPcp}} \label{ExponentsByPcp} }\hfill{\scriptsize (function)}}\\ returns the exponent vector of \mbox{\texttt{\mdseries\slshape g}} with respect to the generators of \mbox{\texttt{\mdseries\slshape pcp}}. This is the exponent vector of \mbox{\texttt{\mdseries\slshape g}}$N$ with respect to the igs of \mbox{\texttt{\mdseries\slshape U/N}}. } \subsection{\textcolor{Chapter }{PcpGroupByPcp}} \logpage{[ 5, 4, 11 ]}\nobreak \hyperdef{L}{X87D75F7F86FEF203}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpGroupByPcp({\mdseries\slshape pcp})\index{PcpGroupByPcp@\texttt{PcpGroupByPcp}} \label{PcpGroupByPcp} }\hfill{\scriptsize (function)}}\\ let \mbox{\texttt{\mdseries\slshape pcp}} be a Pcp of a subgroup or a factor group of a pcp\texttt{\symbol{45}}group. This function computes a new pcp\texttt{\symbol{45}}group whose defining generators correspond to the generators in \mbox{\texttt{\mdseries\slshape pcp}}. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@ G := DihedralPcpGroup(0);| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@ pcp := Pcp(G);| Pcp [ g1, g2 ] with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@ pcp[1];| g1 !gapprompt@gap>| !gapinput@ Length(pcp);| 2 !gapprompt@gap>| !gapinput@ RelativeOrdersOfPcp(pcp);| [ 2, 0 ] !gapprompt@gap>| !gapinput@ DenominatorOfPcp(pcp);| [ ] !gapprompt@gap>| !gapinput@ NumeratorOfPcp(pcp);| [ g1, g2 ] !gapprompt@gap>| !gapinput@ GroupOfPcp(pcp);| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@OneOfPcp(pcp);| identity \end{Verbatim} \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := ExamplesOfSomePcpGroups(5);| Pcp-group with orders [ 2, 0, 0, 0 ] !gapprompt@gap>| !gapinput@D := DerivedSubgroup( G );| Pcp-group with orders [ 0, 0, 0 ] !gapprompt@gap>| !gapinput@ GeneratorsOfGroup( G );| [ g1, g2, g3, g4 ] !gapprompt@gap>| !gapinput@ GeneratorsOfGroup( D );| [ g2^-2, g3^-2, g4^2 ] # an ordinary pcp for G / D !gapprompt@gap>| !gapinput@pcp1 := Pcp( G, D );| Pcp [ g1, g2, g3, g4 ] with orders [ 2, 2, 2, 2 ] # a pcp for G/D in independent generators !gapprompt@gap>| !gapinput@ pcp2 := Pcp( G, D, "snf" );| Pcp [ g2, g3, g1 ] with orders [ 2, 2, 4 ] !gapprompt@gap>| !gapinput@ g := Random( G );| g1*g2^-4*g3*g4^2 # compute the exponent vector of g in G/D with respect to pcp1 !gapprompt@gap>| !gapinput@ExponentsByPcp( pcp1, g );| [ 1, 0, 1, 0 ] # compute the exponent vector of g in G/D with respect to pcp2 !gapprompt@gap>| !gapinput@ ExponentsByPcp( pcp2, g );| [ 0, 1, 1 ] \end{Verbatim} } } \section{\textcolor{Chapter }{Factor groups of pcp\texttt{\symbol{45}}groups}}\label{Factor groups of pcp-groups} \logpage{[ 5, 5, 0 ]} \hyperdef{L}{X845D29B478CA7656}{} { Pcp's for subfactors of pcp\texttt{\symbol{45}}groups have already been described above. These are usually used within algorithms to compute with pcp\texttt{\symbol{45}}groups. However, it is also possible to explicitly construct factor groups and their corresponding natural homomorphisms. \subsection{\textcolor{Chapter }{NaturalHomomorphismByNormalSubgroup}} \logpage{[ 5, 5, 1 ]}\nobreak \hyperdef{L}{X80FC390C7F38A13F}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NaturalHomomorphismByNormalSubgroup({\mdseries\slshape G, N})\index{NaturalHomomorphismByNormalSubgroup@\texttt{NaturalHomomorphismByNormalSubgroup}} \label{NaturalHomomorphismByNormalSubgroup} }\hfill{\scriptsize (method)}}\\ returns the natural homomorphism $G \to G/N$. Its image is the factor group $G/N$. } \subsection{\textcolor{Chapter }{\texttt{\symbol{92}}/}} \logpage{[ 5, 5, 2 ]}\nobreak \hyperdef{L}{X7F51DF007F51DF00}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{\texttt{\symbol{92}}/({\mdseries\slshape G, N})\index{\texttt{\symbol{92}}/@\texttt{\texttt{\symbol{92}}/}} \label{bSlash/} }\hfill{\scriptsize (method)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{FactorGroup({\mdseries\slshape G, N})\index{FactorGroup@\texttt{FactorGroup}} \label{FactorGroup} }\hfill{\scriptsize (method)}}\\ returns the desired factor as pcp\texttt{\symbol{45}}group without giving the explicit homomorphism. This function is just a wrapper for \texttt{PcpGroupByPcp( Pcp( G, N ) )}. } } \section{\textcolor{Chapter }{Homomorphisms for pcp\texttt{\symbol{45}}groups}}\label{Homomorphisms for pcp-groups} \logpage{[ 5, 6, 0 ]} \hyperdef{L}{X82E643F178E765EA}{} { \textsf{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: \subsection{\textcolor{Chapter }{GroupHomomorphismByImages}} \logpage{[ 5, 6, 1 ]}\nobreak \hyperdef{L}{X7F348F497C813BE0}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{GroupHomomorphismByImages({\mdseries\slshape G, H, gens, imgs})\index{GroupHomomorphismByImages@\texttt{GroupHomomorphismByImages}} \label{GroupHomomorphismByImages} }\hfill{\scriptsize (function)}}\\ returns the homomorphism from the (pcp\texttt{\symbol{45}}) group \mbox{\texttt{\mdseries\slshape G}} to the pcp\texttt{\symbol{45}}group \mbox{\texttt{\mdseries\slshape H}} mapping the generators of \mbox{\texttt{\mdseries\slshape G}} in the list \mbox{\texttt{\mdseries\slshape gens}} to the corresponding images in the list \mbox{\texttt{\mdseries\slshape imgs}} of elements of \mbox{\texttt{\mdseries\slshape H}}. } \subsection{\textcolor{Chapter }{Kernel}} \logpage{[ 5, 6, 2 ]}\nobreak \hyperdef{L}{X7DCD99628504B810}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Kernel({\mdseries\slshape hom})\index{Kernel@\texttt{Kernel}} \label{Kernel} }\hfill{\scriptsize (function)}}\\ returns the kernel of the homomorphism \mbox{\texttt{\mdseries\slshape hom}} from a pcp\texttt{\symbol{45}}group to a pcp\texttt{\symbol{45}}group. } \subsection{\textcolor{Chapter }{Image (for a homomorphism)}} \logpage{[ 5, 6, 3 ]}\nobreak \hyperdef{L}{X847322667E6166C8}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Image({\mdseries\slshape hom})\index{Image@\texttt{Image}!for a homomorphism} \label{Image:for a homomorphism} }\hfill{\scriptsize (operation)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Image({\mdseries\slshape hom, U})\index{Image@\texttt{Image}!for a homomorphism and a subgroup} \label{Image:for a homomorphism and a subgroup} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Image({\mdseries\slshape hom, g})\index{Image@\texttt{Image}!for a homomorphism and an element} \label{Image:for a homomorphism and an element} }\hfill{\scriptsize (function)}}\\ returns the image of the whole group, of \mbox{\texttt{\mdseries\slshape U}} and of \mbox{\texttt{\mdseries\slshape g}}, respectively, under the homomorphism \mbox{\texttt{\mdseries\slshape hom}}. } \subsection{\textcolor{Chapter }{PreImage}} \logpage{[ 5, 6, 4 ]}\nobreak \hyperdef{L}{X836FAEAC78B55BF4}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PreImage({\mdseries\slshape hom, U})\index{PreImage@\texttt{PreImage}} \label{PreImage} }\hfill{\scriptsize (function)}}\\ returns the complete preimage of the subgroup \mbox{\texttt{\mdseries\slshape U}} under the homomorphism \mbox{\texttt{\mdseries\slshape hom}}. If the domain of \mbox{\texttt{\mdseries\slshape hom}} is not a pcp\texttt{\symbol{45}}group, then this function only works properly if \mbox{\texttt{\mdseries\slshape hom}} is injective. } \subsection{\textcolor{Chapter }{PreImagesRepresentative}} \logpage{[ 5, 6, 5 ]}\nobreak \hyperdef{L}{X7AE24A1586B7DE79}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PreImagesRepresentative({\mdseries\slshape hom, g})\index{PreImagesRepresentative@\texttt{PreImagesRepresentative}} \label{PreImagesRepresentative} }\hfill{\scriptsize (method)}}\\ returns a preimage of the element \mbox{\texttt{\mdseries\slshape g}} under the homomorphism \mbox{\texttt{\mdseries\slshape hom}}. } \subsection{\textcolor{Chapter }{IsInjective}} \logpage{[ 5, 6, 6 ]}\nobreak \hyperdef{L}{X7F065FD7822C0A12}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsInjective({\mdseries\slshape hom})\index{IsInjective@\texttt{IsInjective}} \label{IsInjective} }\hfill{\scriptsize (method)}}\\ checks if the homomorphism \mbox{\texttt{\mdseries\slshape hom}} is injective. } } \section{\textcolor{Chapter }{Changing the defining pc\texttt{\symbol{45}}presentation}}\label{Changing the defining pc-presentation} \logpage{[ 5, 7, 0 ]} \hyperdef{L}{X7C873F807D4F3A3C}{} { \subsection{\textcolor{Chapter }{RefinedPcpGroup}} \logpage{[ 5, 7, 1 ]}\nobreak \hyperdef{L}{X80E9B60E853B2E05}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RefinedPcpGroup({\mdseries\slshape G})\index{RefinedPcpGroup@\texttt{RefinedPcpGroup}} \label{RefinedPcpGroup} }\hfill{\scriptsize (function)}}\\ returns a new pcp\texttt{\symbol{45}}group isomorphic to \mbox{\texttt{\mdseries\slshape 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$. } \subsection{\textcolor{Chapter }{PcpGroupBySeries}} \logpage{[ 5, 7, 2 ]}\nobreak \hyperdef{L}{X7F88F5548329E279}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpGroupBySeries({\mdseries\slshape ser[, flag]})\index{PcpGroupBySeries@\texttt{PcpGroupBySeries}} \label{PcpGroupBySeries} }\hfill{\scriptsize (function)}}\\ returns a new pcp\texttt{\symbol{45}}group isomorphic to the first subgroup $G$ of the given series \mbox{\texttt{\mdseries\slshape 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 \mbox{\texttt{\mdseries\slshape 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. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := DihedralPcpGroup(0);| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@ U := Subgroup( G, [Pcp(G)[2]^1440]);| Pcp-group with orders [ 0 ] !gapprompt@gap>| !gapinput@ F := G/U;| Pcp-group with orders [ 2, 1440 ] !gapprompt@gap>| !gapinput@RefinedPcpGroup(F);| Pcp-group with orders [ 2, 2, 2, 2, 2, 2, 3, 3, 5 ] !gapprompt@gap>| !gapinput@ser := [G, U, TrivialSubgroup(G)];| [ Pcp-group with orders [ 2, 0 ], Pcp-group with orders [ 0 ], Pcp-group with orders [ ] ] !gapprompt@gap>| !gapinput@ PcpGroupBySeries(ser);| Pcp-group with orders [ 2, 1440, 0 ] \end{Verbatim} } } \section{\textcolor{Chapter }{Printing a pc\texttt{\symbol{45}}presentation}}\label{Printing a pc-presentation} \logpage{[ 5, 8, 0 ]} \hyperdef{L}{X85E681027AF19B1E}{} { By default, a pcp\texttt{\symbol{45}}group is printed using its relative orders only. The following methods can be used to view the pcp presentation of the group. \subsection{\textcolor{Chapter }{PrintPcpPresentation (for a group)}} \logpage{[ 5, 8, 1 ]}\nobreak \hyperdef{L}{X79D247127FD57FC8}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PrintPcpPresentation({\mdseries\slshape G[, flag]})\index{PrintPcpPresentation@\texttt{PrintPcpPresentation}!for a group} \label{PrintPcpPresentation:for a group} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PrintPcpPresentation({\mdseries\slshape pcp[, flag]})\index{PrintPcpPresentation@\texttt{PrintPcpPresentation}!for a pcp} \label{PrintPcpPresentation:for a pcp} }\hfill{\scriptsize (function)}}\\ prints the pcp presentation defined by the igs of \mbox{\texttt{\mdseries\slshape G}} or the pcp \mbox{\texttt{\mdseries\slshape 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 \mbox{\texttt{\mdseries\slshape 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. } } \section{\textcolor{Chapter }{Converting to and from a presentation}}\label{Converting to and from a presentation} \logpage{[ 5, 9, 0 ]} \hyperdef{L}{X826ACBBB7A977206}{} { \subsection{\textcolor{Chapter }{IsomorphismPcpGroup}} \logpage{[ 5, 9, 1 ]}\nobreak \hyperdef{L}{X8771540F7A235763}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsomorphismPcpGroup({\mdseries\slshape G})\index{IsomorphismPcpGroup@\texttt{IsomorphismPcpGroup}} \label{IsomorphismPcpGroup} }\hfill{\scriptsize (attribute)}}\\ returns an isomorphism from \mbox{\texttt{\mdseries\slshape G}} onto a pcp\texttt{\symbol{45}}group \mbox{\texttt{\mdseries\slshape H}}. There are various methods installed for this operation and some of these methods are part of the \textsf{Polycyclic} package, while others may be part of other packages. For example, \textsf{Polycyclic} contains methods for this function in the case that \mbox{\texttt{\mdseries\slshape G}} is a finite pc\texttt{\symbol{45}}group or a finite solvable permutation group. Other examples for methods for IsomorphismPcpGroup are the methods for the case that \mbox{\texttt{\mdseries\slshape G}} is a crystallographic group (see \textsf{Cryst}) or the case that \mbox{\texttt{\mdseries\slshape G}} is an almost crystallographic group (see \textsf{AClib}). A method for the case that \mbox{\texttt{\mdseries\slshape G}} is a rational polycyclic matrix group is included in the \textsf{Polenta} package. } \subsection{\textcolor{Chapter }{IsomorphismPcpGroupFromFpGroupWithPcPres}} \logpage{[ 5, 9, 2 ]}\nobreak \hyperdef{L}{X7F5EBF1C831B4BA9}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsomorphismPcpGroupFromFpGroupWithPcPres({\mdseries\slshape G})\index{IsomorphismPcpGroupFromFpGroupWithPcPres@\texttt{Isomorphism}\-\texttt{Pcp}\-\texttt{Group}\-\texttt{From}\-\texttt{Fp}\-\texttt{Group}\-\texttt{With}\-\texttt{Pc}\-\texttt{Pres}} \label{IsomorphismPcpGroupFromFpGroupWithPcPres} }\hfill{\scriptsize (function)}}\\ This function can convert a finitely presented group with a polycyclic presentation into a pcp group. } \subsection{\textcolor{Chapter }{IsomorphismPcGroup}} \logpage{[ 5, 9, 3 ]}\nobreak \hyperdef{L}{X873CEB137BA1CD6E}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsomorphismPcGroup({\mdseries\slshape G})\index{IsomorphismPcGroup@\texttt{IsomorphismPcGroup}} \label{IsomorphismPcGroup} }\hfill{\scriptsize (method)}}\\ pc\texttt{\symbol{45}}groups are a representation for finite polycyclic groups. This function can convert finite pcp\texttt{\symbol{45}}groups to pc\texttt{\symbol{45}}groups. } \subsection{\textcolor{Chapter }{IsomorphismFpGroup}} \logpage{[ 5, 9, 4 ]}\nobreak \hyperdef{L}{X7F28268F850F454E}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsomorphismFpGroup({\mdseries\slshape G})\index{IsomorphismFpGroup@\texttt{IsomorphismFpGroup}} \label{IsomorphismFpGroup} }\hfill{\scriptsize (method)}}\\ This function can convert pcp\texttt{\symbol{45}}groups to a finitely presented group. } } } \chapter{\textcolor{Chapter }{Libraries and examples of pcp\texttt{\symbol{45}}groups}}\label{Libraries and examples of pcp-groups} \logpage{[ 6, 0, 0 ]} \hyperdef{L}{X78CEF1F27ED8D7BB}{} { \section{\textcolor{Chapter }{Libraries of various types of polycyclic groups}}\label{Libraries of various types of polycyclic groups} \logpage{[ 6, 1, 0 ]} \hyperdef{L}{X84A48FAB83934263}{} { There are the following generic pcp\texttt{\symbol{45}}groups available. \subsection{\textcolor{Chapter }{AbelianPcpGroup}} \logpage{[ 6, 1, 1 ]}\nobreak \hyperdef{L}{X7AEDE1BA82014B86}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{AbelianPcpGroup({\mdseries\slshape n[, rels]})\index{AbelianPcpGroup@\texttt{AbelianPcpGroup}} \label{AbelianPcpGroup} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{AbelianPcpGroup({\mdseries\slshape rels})\index{AbelianPcpGroup@\texttt{AbelianPcpGroup}!rels only} \label{AbelianPcpGroup:rels only} }\hfill{\scriptsize (function)}}\\ constructs the abelian group on \mbox{\texttt{\mdseries\slshape n}} generators such that generator $i$ has order $rels[i]$. If this order is infinite, then $rels[i]$ should be either unbound or 0 or infinity. If \mbox{\texttt{\mdseries\slshape n}} is not provided then the length of \mbox{\texttt{\mdseries\slshape rels}} is used. If \mbox{\texttt{\mdseries\slshape rels}} is omitted then all generators will have infinite order. } \subsection{\textcolor{Chapter }{DihedralPcpGroup}} \logpage{[ 6, 1, 2 ]}\nobreak \hyperdef{L}{X7ACF57737D0F12DB}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{DihedralPcpGroup({\mdseries\slshape n})\index{DihedralPcpGroup@\texttt{DihedralPcpGroup}} \label{DihedralPcpGroup} }\hfill{\scriptsize (function)}}\\ constructs the dihedral group of order \mbox{\texttt{\mdseries\slshape n}}. If \mbox{\texttt{\mdseries\slshape n}} is an odd integer, then 'fail' is returned. If \mbox{\texttt{\mdseries\slshape n}} is zero or not an integer, then the infinite dihedral group is returned. } \subsection{\textcolor{Chapter }{UnitriangularPcpGroup}} \logpage{[ 6, 1, 3 ]}\nobreak \hyperdef{L}{X864CEDAB7911CC79}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{UnitriangularPcpGroup({\mdseries\slshape n, c})\index{UnitriangularPcpGroup@\texttt{UnitriangularPcpGroup}} \label{UnitriangularPcpGroup} }\hfill{\scriptsize (function)}}\\ returns a pcp\texttt{\symbol{45}}group isomorphic to the group of upper triangular in $GL(n, R)$ where $R = {\ensuremath{\mathbb Z}}$ if $c = 0$ and $R = \mathbb{F}_p$ if $c = p$. The natural unitriangular matrix representation of the returned pcp\texttt{\symbol{45}}group $G$ can be obtained as $G!.isomorphism$. } \subsection{\textcolor{Chapter }{SubgroupUnitriangularPcpGroup}} \logpage{[ 6, 1, 4 ]}\nobreak \hyperdef{L}{X812E35B17AADBCD5}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SubgroupUnitriangularPcpGroup({\mdseries\slshape mats})\index{SubgroupUnitriangularPcpGroup@\texttt{SubgroupUnitriangularPcpGroup}} \label{SubgroupUnitriangularPcpGroup} }\hfill{\scriptsize (function)}}\\ \mbox{\texttt{\mdseries\slshape mats}} should be a list of upper unitriangular $n \times n$ matrices over ${\ensuremath{\mathbb Z}}$ or over $\mathbb{F}_p$. This function returns the subgroup of the corresponding 'UnitriangularPcpGroup' generated by the matrices in \mbox{\texttt{\mdseries\slshape mats}}. } \subsection{\textcolor{Chapter }{InfiniteMetacyclicPcpGroup}} \logpage{[ 6, 1, 5 ]}\nobreak \hyperdef{L}{X7A80F7F27FDA6810}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{InfiniteMetacyclicPcpGroup({\mdseries\slshape n, m, r})\index{InfiniteMetacyclicPcpGroup@\texttt{InfiniteMetacyclicPcpGroup}} \label{InfiniteMetacyclicPcpGroup} }\hfill{\scriptsize (function)}}\\ Infinite metacyclic groups are classified in \cite{B-K00}. 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\texttt{\symbol{45}}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 \mbox{\texttt{\mdseries\slshape n}}, \mbox{\texttt{\mdseries\slshape m}} and \mbox{\texttt{\mdseries\slshape r}} as a pcp\texttt{\symbol{45}}group with the pc\texttt{\symbol{45}}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$. } \subsection{\textcolor{Chapter }{HeisenbergPcpGroup}} \logpage{[ 6, 1, 6 ]}\nobreak \hyperdef{L}{X81BEC875827D1CC2}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{HeisenbergPcpGroup({\mdseries\slshape n})\index{HeisenbergPcpGroup@\texttt{HeisenbergPcpGroup}} \label{HeisenbergPcpGroup} }\hfill{\scriptsize (function)}}\\ returns the Heisenberg group on $2\mbox{\texttt{\mdseries\slshape n}}+1$ generators as pcp\texttt{\symbol{45}}group. This gives a group of Hirsch length $2\mbox{\texttt{\mdseries\slshape n}}+1$. } \subsection{\textcolor{Chapter }{MaximalOrderByUnitsPcpGroup}} \logpage{[ 6, 1, 7 ]}\nobreak \hyperdef{L}{X87F9B9C9786430D7}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{MaximalOrderByUnitsPcpGroup({\mdseries\slshape f})\index{MaximalOrderByUnitsPcpGroup@\texttt{MaximalOrderByUnitsPcpGroup}} \label{MaximalOrderByUnitsPcpGroup} }\hfill{\scriptsize (function)}}\\ takes as input a normed, irreducible polynomial over the integers. Thus \mbox{\texttt{\mdseries\slshape f}} defines a field extension \mbox{\texttt{\mdseries\slshape F}} over the rationals. This function returns the split extension of the maximal order \mbox{\texttt{\mdseries\slshape O}} of \mbox{\texttt{\mdseries\slshape F}} by the unit group \mbox{\texttt{\mdseries\slshape U}} of \mbox{\texttt{\mdseries\slshape O}}, where \mbox{\texttt{\mdseries\slshape U}} acts by right multiplication on \mbox{\texttt{\mdseries\slshape O}}. } \subsection{\textcolor{Chapter }{BurdeGrunewaldPcpGroup}} \logpage{[ 6, 1, 8 ]}\nobreak \hyperdef{L}{X852283A77A2C93DD}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{BurdeGrunewaldPcpGroup({\mdseries\slshape s, t})\index{BurdeGrunewaldPcpGroup@\texttt{BurdeGrunewaldPcpGroup}} \label{BurdeGrunewaldPcpGroup} }\hfill{\scriptsize (function)}}\\ returns a nilpotent group of Hirsch length 11 which has been constructed by Burde und Grunewald. If \mbox{\texttt{\mdseries\slshape s}} is not 0, then this group has no faithful 12\texttt{\symbol{45}}dimensional linear representation. } } \section{\textcolor{Chapter }{Some assorted example groups}}\label{Some asorted example groups} \logpage{[ 6, 2, 0 ]} \hyperdef{L}{X806FBA4A7CB8FB71}{} { 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. \subsection{\textcolor{Chapter }{ExampleOfMetabelianPcpGroup}} \logpage{[ 6, 2, 1 ]}\nobreak \hyperdef{L}{X86293081865CDFC3}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ExampleOfMetabelianPcpGroup({\mdseries\slshape a, k})\index{ExampleOfMetabelianPcpGroup@\texttt{ExampleOfMetabelianPcpGroup}} \label{ExampleOfMetabelianPcpGroup} }\hfill{\scriptsize (function)}}\\ returns an example of a metabelian group. The input parameters must be two positive integers greater than 1. } \subsection{\textcolor{Chapter }{ExamplesOfSomePcpGroups}} \logpage{[ 6, 2, 2 ]}\nobreak \hyperdef{L}{X83A74A6E7E232FD6}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ExamplesOfSomePcpGroups({\mdseries\slshape n})\index{ExamplesOfSomePcpGroups@\texttt{ExamplesOfSomePcpGroups}} \label{ExamplesOfSomePcpGroups} }\hfill{\scriptsize (function)}}\\ this function takes values \mbox{\texttt{\mdseries\slshape n}} in 1 up to 16 and returns for each input an example of a pcp\texttt{\symbol{45}}group. The groups in this example list have been used as test groups for the functions in this package. } } } \chapter{\textcolor{Chapter }{Higher level methods for pcp\texttt{\symbol{45}}groups}}\label{Higher level methods for pcp-groups} \logpage{[ 7, 0, 0 ]} \hyperdef{L}{X85BB6FE078679DAF}{} { This is a description of some higher level functions of the \textsf{Polycyclic} package of GAP 4. Throughout this chapter we let \mbox{\texttt{\mdseries\slshape G}} be a pc\texttt{\symbol{45}}presented group and we consider algorithms for subgroups \mbox{\texttt{\mdseries\slshape U}} and \mbox{\texttt{\mdseries\slshape V}} of \mbox{\texttt{\mdseries\slshape G}}. For background and a description of the underlying algorithms we refer to \cite{Eic01b}. \section{\textcolor{Chapter }{Subgroup series in pcp\texttt{\symbol{45}}groups}}\label{Subgroup series in pcp-groups} \logpage{[ 7, 1, 0 ]} \hyperdef{L}{X8266A0A2821D98A1}{} { Many algorithm for pcp\texttt{\symbol{45}}groups work by induction using some series through the group. In this section we provide a number of useful series for pcp\texttt{\symbol{45}}groups. An \emph{efa series} is a normal series with elementary or free abelian factors. See \cite{Eic00} for outlines on the algorithms of a number of the available series. \subsection{\textcolor{Chapter }{PcpSeries}} \logpage{[ 7, 1, 1 ]}\nobreak \hyperdef{L}{X8037DAD77A19D9B2}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpSeries({\mdseries\slshape U})\index{PcpSeries@\texttt{PcpSeries}} \label{PcpSeries} }\hfill{\scriptsize (function)}}\\ returns the polycyclic series of \mbox{\texttt{\mdseries\slshape U}} defined by an igs of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{EfaSeries}} \logpage{[ 7, 1, 2 ]}\nobreak \hyperdef{L}{X86C633357ACD342C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{EfaSeries({\mdseries\slshape U})\index{EfaSeries@\texttt{EfaSeries}} \label{EfaSeries} }\hfill{\scriptsize (attribute)}}\\ returns a normal series of \mbox{\texttt{\mdseries\slshape U}} with elementary or free abelian factors. } \subsection{\textcolor{Chapter }{SemiSimpleEfaSeries}} \logpage{[ 7, 1, 3 ]}\nobreak \hyperdef{L}{X80ED4F8380DC477E}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SemiSimpleEfaSeries({\mdseries\slshape U})\index{SemiSimpleEfaSeries@\texttt{SemiSimpleEfaSeries}} \label{SemiSimpleEfaSeries} }\hfill{\scriptsize (attribute)}}\\ returns an efa series of \mbox{\texttt{\mdseries\slshape U}} such that every factor in the series is semisimple as a module for \mbox{\texttt{\mdseries\slshape U}} over a finite field or over the rationals. } \subsection{\textcolor{Chapter }{DerivedSeriesOfGroup}} \logpage{[ 7, 1, 4 ]}\nobreak \hyperdef{L}{X7A879948834BD889}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{DerivedSeriesOfGroup({\mdseries\slshape U})\index{DerivedSeriesOfGroup@\texttt{DerivedSeriesOfGroup}} \label{DerivedSeriesOfGroup} }\hfill{\scriptsize (method)}}\\ the derived series of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{RefinedDerivedSeries}} \logpage{[ 7, 1, 5 ]}\nobreak \hyperdef{L}{X866D4C5C79F26611}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RefinedDerivedSeries({\mdseries\slshape U})\index{RefinedDerivedSeries@\texttt{RefinedDerivedSeries}} \label{RefinedDerivedSeries} }\hfill{\scriptsize (function)}}\\ the derived series of \mbox{\texttt{\mdseries\slshape U}} refined to an efa series such that in each abelian factor of the derived series the free abelian factor is at the top. } \subsection{\textcolor{Chapter }{RefinedDerivedSeriesDown}} \logpage{[ 7, 1, 6 ]}\nobreak \hyperdef{L}{X86F7DE927DE3B5CD}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RefinedDerivedSeriesDown({\mdseries\slshape U})\index{RefinedDerivedSeriesDown@\texttt{RefinedDerivedSeriesDown}} \label{RefinedDerivedSeriesDown} }\hfill{\scriptsize (function)}}\\ the derived series of \mbox{\texttt{\mdseries\slshape U}} refined to an efa series such that in each abelian factor of the derived series the free abelian factor is at the bottom. } \subsection{\textcolor{Chapter }{LowerCentralSeriesOfGroup}} \logpage{[ 7, 1, 7 ]}\nobreak \hyperdef{L}{X879D55A67DB42676}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{LowerCentralSeriesOfGroup({\mdseries\slshape U})\index{LowerCentralSeriesOfGroup@\texttt{LowerCentralSeriesOfGroup}} \label{LowerCentralSeriesOfGroup} }\hfill{\scriptsize (method)}}\\ the lower central series of \mbox{\texttt{\mdseries\slshape U}}. If \mbox{\texttt{\mdseries\slshape U}} does not have a largest nilpotent quotient group, then this function may not terminate. } \subsection{\textcolor{Chapter }{UpperCentralSeriesOfGroup}} \logpage{[ 7, 1, 8 ]}\nobreak \hyperdef{L}{X8428592E8773CD7B}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{UpperCentralSeriesOfGroup({\mdseries\slshape U})\index{UpperCentralSeriesOfGroup@\texttt{UpperCentralSeriesOfGroup}} \label{UpperCentralSeriesOfGroup} }\hfill{\scriptsize (method)}}\\ the upper central series of \mbox{\texttt{\mdseries\slshape U}}. This function always terminates, but it may terminate at a proper subgroup of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{TorsionByPolyEFSeries}} \logpage{[ 7, 1, 9 ]}\nobreak \hyperdef{L}{X83CA5DE785AE3F2C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{TorsionByPolyEFSeries({\mdseries\slshape U})\index{TorsionByPolyEFSeries@\texttt{TorsionByPolyEFSeries}} \label{TorsionByPolyEFSeries} }\hfill{\scriptsize (function)}}\\ returns an efa series of \mbox{\texttt{\mdseries\slshape U}} such that all torsion\texttt{\symbol{45}}free factors are at the top and all finite factors are at the bottom. Such a series might not exist for \mbox{\texttt{\mdseries\slshape U}} and in this case the function returns fail. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := ExamplesOfSomePcpGroups(5);| Pcp-group with orders [ 2, 0, 0, 0 ] !gapprompt@gap>| !gapinput@Igs(G);| [ g1, g2, g3, g4 ] !gapprompt@gap>| !gapinput@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 [ ] ] !gapprompt@gap>| !gapinput@List( PcpSeries(G), Igs );| [ [ g1, g2, g3, g4 ], [ g2, g3, g4 ], [ g3, g4 ], [ g4 ], [ ] ] \end{Verbatim} } Algorithms for pcp\texttt{\symbol{45}}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. \subsection{\textcolor{Chapter }{PcpsBySeries}} \logpage{[ 7, 1, 10 ]}\nobreak \hyperdef{L}{X7E39431286969377}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpsBySeries({\mdseries\slshape ser[, flag]})\index{PcpsBySeries@\texttt{PcpsBySeries}} \label{PcpsBySeries} }\hfill{\scriptsize (function)}}\\ returns a list of pcp's corresponding to the factors of the series. If the parameter \mbox{\texttt{\mdseries\slshape flag}} is present and equals the string ``snf'', then each pcp corresponds to a decomposition of the abelian groups into direct factors. } \subsection{\textcolor{Chapter }{PcpsOfEfaSeries}} \logpage{[ 7, 1, 11 ]}\nobreak \hyperdef{L}{X79789A1C82139854}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpsOfEfaSeries({\mdseries\slshape U})\index{PcpsOfEfaSeries@\texttt{PcpsOfEfaSeries}} \label{PcpsOfEfaSeries} }\hfill{\scriptsize (attribute)}}\\ returns a list of pcps corresponding to an efa series of \mbox{\texttt{\mdseries\slshape U}}. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := ExamplesOfSomePcpGroups(5);| Pcp-group with orders [ 2, 0, 0, 0 ] !gapprompt@gap>| !gapinput@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 ] ] !gapprompt@gap>| !gapinput@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 ] ] !gapprompt@gap>| !gapinput@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 ] ] !gapprompt@gap>| !gapinput@G.1^4 in DerivedSubgroup( G );| true !gapprompt@gap>| !gapinput@G.1^2 = G.4;| true !gapprompt@gap>| !gapinput@ PcpsOfEfaSeries( G );| [ Pcp [ g1 ] with orders [ 2 ], Pcp [ g2 ] with orders [ 0 ], Pcp [ g3 ] with orders [ 0 ], Pcp [ g4 ] with orders [ 0 ] ] \end{Verbatim} } } \section{\textcolor{Chapter }{Orbit stabilizer methods for pcp\texttt{\symbol{45}}groups}}\label{Orbit stabilizer methods for pcp-groups} \logpage{[ 7, 2, 0 ]} \hyperdef{L}{X7CE2DA437FD2B383}{} { Let \mbox{\texttt{\mdseries\slshape U}} be a pcp\texttt{\symbol{45}}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 \mbox{\texttt{\mdseries\slshape 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. \subsection{\textcolor{Chapter }{PcpOrbitStabilizer}} \logpage{[ 7, 2, 1 ]}\nobreak \hyperdef{L}{X83E17DB483B33AB5}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpOrbitStabilizer({\mdseries\slshape point, gens, acts, oper})\index{PcpOrbitStabilizer@\texttt{PcpOrbitStabilizer}} \label{PcpOrbitStabilizer} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PcpOrbitsStabilizers({\mdseries\slshape points, gens, acts, oper})\index{PcpOrbitsStabilizers@\texttt{PcpOrbitsStabilizers}} \label{PcpOrbitsStabilizers} }\hfill{\scriptsize (function)}}\\ The input \mbox{\texttt{\mdseries\slshape gens}} can be an igs or a pcp of a pcp\texttt{\symbol{45}}group \mbox{\texttt{\mdseries\slshape U}}. The elements in the list \mbox{\texttt{\mdseries\slshape gens}} act as the elements in the list \mbox{\texttt{\mdseries\slshape acts}} via the function \mbox{\texttt{\mdseries\slshape oper}} on the given points; that is, \mbox{\texttt{\mdseries\slshape oper( point, acts[i] )}} applies the $i$th generator to a given point. Thus the group defined by \mbox{\texttt{\mdseries\slshape acts}} must be a homomorphic image of the group defined by \mbox{\texttt{\mdseries\slshape 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. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := DihedralPcpGroup( 0 );| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@mats := [ [[-1,0],[0,1]], [[1,1],[0,1]] ];;| !gapprompt@gap>| !gapinput@pcp := Pcp(G);| Pcp [ g1, g2 ] with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@PcpOrbitStabilizer( [0,1], pcp, mats, OnRight );| rec( orbit := [ [ 0, 1 ] ], stab := [ g1, g2 ], word := [ [ [ 1, 1 ] ], [ [ 2, 1 ] ] ] ) \end{Verbatim} 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 \cite{EOs01} and \cite{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. } \subsection{\textcolor{Chapter }{StabilizerIntegralAction}} \logpage{[ 7, 2, 2 ]}\nobreak \hyperdef{L}{X80694BA480F69A0E}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{StabilizerIntegralAction({\mdseries\slshape U, mats, v})\index{StabilizerIntegralAction@\texttt{StabilizerIntegralAction}} \label{StabilizerIntegralAction} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{OrbitIntegralAction({\mdseries\slshape U, mats, v, w})\index{OrbitIntegralAction@\texttt{OrbitIntegralAction}} \label{OrbitIntegralAction} }\hfill{\scriptsize (function)}}\\ The first function computes the stabilizer in \mbox{\texttt{\mdseries\slshape U}} of the vector \mbox{\texttt{\mdseries\slshape v}} where the pcp group \mbox{\texttt{\mdseries\slshape U}} acts via \mbox{\texttt{\mdseries\slshape mats}} on an integral space and \mbox{\texttt{\mdseries\slshape v}} and \mbox{\texttt{\mdseries\slshape w}} are elements in this integral space. The second function checks whether \mbox{\texttt{\mdseries\slshape v}} and \mbox{\texttt{\mdseries\slshape w}} are in the same orbit and the function returns either \mbox{\texttt{\mdseries\slshape false}} or a record containing an element in \mbox{\texttt{\mdseries\slshape U}} mapping \mbox{\texttt{\mdseries\slshape v}} to \mbox{\texttt{\mdseries\slshape w}} and the stabilizer of \mbox{\texttt{\mdseries\slshape v}}. } \subsection{\textcolor{Chapter }{NormalizerIntegralAction}} \logpage{[ 7, 2, 3 ]}\nobreak \hyperdef{L}{X875BE4077B32A411}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NormalizerIntegralAction({\mdseries\slshape U, mats, B})\index{NormalizerIntegralAction@\texttt{NormalizerIntegralAction}} \label{NormalizerIntegralAction} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ConjugacyIntegralAction({\mdseries\slshape U, mats, B, C})\index{ConjugacyIntegralAction@\texttt{ConjugacyIntegralAction}} \label{ConjugacyIntegralAction} }\hfill{\scriptsize (function)}}\\ The first function computes the normalizer in \mbox{\texttt{\mdseries\slshape U}} of the lattice with the basis \mbox{\texttt{\mdseries\slshape B}}, where the pcp group \mbox{\texttt{\mdseries\slshape U}} acts via \mbox{\texttt{\mdseries\slshape mats}} on an integral space and \mbox{\texttt{\mdseries\slshape B}} is a subspace of this integral space. The second functions checks whether the two lattices with the bases \mbox{\texttt{\mdseries\slshape B}} and \mbox{\texttt{\mdseries\slshape C}} are contained in the same orbit under \mbox{\texttt{\mdseries\slshape U}}. The function returns either \mbox{\texttt{\mdseries\slshape false}} or a record with an element in \mbox{\texttt{\mdseries\slshape U}} mapping \mbox{\texttt{\mdseries\slshape B}} to \mbox{\texttt{\mdseries\slshape C}} and the stabilizer of \mbox{\texttt{\mdseries\slshape B}}. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] # get a pcp group and a free abelian normal subgroup !gapprompt@gap>| !gapinput@G := ExamplesOfSomePcpGroups(8);| Pcp-group with orders [ 0, 0, 0, 0, 0 ] !gapprompt@gap>| !gapinput@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 [ ] ] !gapprompt@gap>| !gapinput@N := efa[3];| Pcp-group with orders [ 0, 0, 0 ] !gapprompt@gap>| !gapinput@IsFreeAbelian(N);| true # create conjugation action on N !gapprompt@gap>| !gapinput@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 !gapprompt@gap>| !gapinput@StabilizerIntegralAction(G,mats, [2,3,4]);| Pcp-group with orders [ 0, 0, 0, 0 ] !gapprompt@gap>| !gapinput@Igs(last);| [ g1, g3, g4, g5 ] # check orbits with some other vectors !gapprompt@gap>| !gapinput@OrbitIntegralAction(G,mats, [2,3,4],[3,1,5]);| rec( stab := Pcp-group with orders [ 0, 0, 0, 0 ], prei := g2 ) !gapprompt@gap>| !gapinput@OrbitIntegralAction(G,mats, [2,3,4], [4,6,8]);| false # compute the orbit of a subgroup of Z^3 under the action of G !gapprompt@gap>| !gapinput@NormalizerIntegralAction(G, mats, [[1,0,0],[0,1,0]]);| Pcp-group with orders [ 0, 0, 0, 0, 0 ] !gapprompt@gap>| !gapinput@Igs(last);| [ g1, g2^2, g3, g4, g5 ] \end{Verbatim} } } \section{\textcolor{Chapter }{Centralizers, Normalizers and Intersections}}\label{Centralizers, Normalizers and Intersections} \logpage{[ 7, 3, 0 ]} \hyperdef{L}{X80E3B42E792532B3}{} { In this section we list a number of operations for which there are methods installed to compute the corresponding features in polycyclic groups. \subsection{\textcolor{Chapter }{Centralizer (for an element)}} \logpage{[ 7, 3, 1 ]}\nobreak \hyperdef{L}{X808EE8AD7EE3ECE1}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Centralizer({\mdseries\slshape U, g})\index{Centralizer@\texttt{Centralizer}!for an element} \label{Centralizer:for an element} }\hfill{\scriptsize (method)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsConjugate({\mdseries\slshape U, g, h})\index{IsConjugate@\texttt{IsConjugate}!for elements} \label{IsConjugate:for elements} }\hfill{\scriptsize (method)}}\\ These functions solve the conjugacy problem for elements in pcp\texttt{\symbol{45}}groups and they can be used to compute centralizers. The first method returns a subgroup of the given group \mbox{\texttt{\mdseries\slshape 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 \cite{EOs01}. For nilpotent groups, an algorithm to solve the conjugacy problem for elements is described in \cite{Sims94}. } \subsection{\textcolor{Chapter }{Centralizer (for a subgroup)}} \logpage{[ 7, 3, 2 ]}\nobreak \hyperdef{L}{X849B5C527BAFAAA4}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Centralizer({\mdseries\slshape U, V})\index{Centralizer@\texttt{Centralizer}!for a subgroup} \label{Centralizer:for a subgroup} }\hfill{\scriptsize (method)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Normalizer({\mdseries\slshape U, V})\index{Normalizer@\texttt{Normalizer}} \label{Normalizer} }\hfill{\scriptsize (method)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsConjugate({\mdseries\slshape U, V, W})\index{IsConjugate@\texttt{IsConjugate}!for subgroups} \label{IsConjugate:for subgroups} }\hfill{\scriptsize (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 \mbox{\texttt{\mdseries\slshape 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 \cite{Eic02}. For nilpotent groups, an algorithm to solve the conjugacy problems for subgroups is described in \cite{Lo98}. } \subsection{\textcolor{Chapter }{Intersection}} \logpage{[ 7, 3, 3 ]}\nobreak \hyperdef{L}{X851069107CACF98E}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Intersection({\mdseries\slshape U, N})\index{Intersection@\texttt{Intersection}} \label{Intersection} }\hfill{\scriptsize (function)}}\\ A general method to compute intersections of subgroups of a pcp\texttt{\symbol{45}}group is described in \cite{Eic01b}, but it is not yet implemented here. However, intersections of subgroups $U, N \leq G$ can be computed if $N$ is normalising $U$. See \cite{Sims94} for an outline of the algorithm. } } \section{\textcolor{Chapter }{Finite subgroups}}\label{Finite subgroups} \logpage{[ 7, 4, 0 ]} \hyperdef{L}{X7CF015E87A2B2388}{} { There are various finite subgroups of interest in polycyclic groups. See \cite{Eic00} for a description of the algorithms underlying the functions in this section. \subsection{\textcolor{Chapter }{TorsionSubgroup}} \logpage{[ 7, 4, 1 ]}\nobreak \hyperdef{L}{X8036FA507A170DC4}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{TorsionSubgroup({\mdseries\slshape U})\index{TorsionSubgroup@\texttt{TorsionSubgroup}} \label{TorsionSubgroup} }\hfill{\scriptsize (attribute)}}\\ If the set of elements of finite order forms a subgroup, then we call it the \emph{torsion subgroup}. This function determines the torsion subgroup of \mbox{\texttt{\mdseries\slshape U}}, if it exists, and returns fail otherwise. Note that a torsion subgroup does always exist if \mbox{\texttt{\mdseries\slshape U}} is nilpotent. } \subsection{\textcolor{Chapter }{NormalTorsionSubgroup}} \logpage{[ 7, 4, 2 ]}\nobreak \hyperdef{L}{X8082CD337972DC63}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NormalTorsionSubgroup({\mdseries\slshape U})\index{NormalTorsionSubgroup@\texttt{NormalTorsionSubgroup}} \label{NormalTorsionSubgroup} }\hfill{\scriptsize (attribute)}}\\ Each polycyclic groups has a unique largest finite normal subgroup. This function computes it for \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{IsTorsionFree}} \logpage{[ 7, 4, 3 ]}\nobreak \hyperdef{L}{X86D92DA17DCE22DD}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsTorsionFree({\mdseries\slshape U})\index{IsTorsionFree@\texttt{IsTorsionFree}} \label{IsTorsionFree} }\hfill{\scriptsize (property)}}\\ This function checks if \mbox{\texttt{\mdseries\slshape U}} is torsion free. It returns true or false. } \subsection{\textcolor{Chapter }{FiniteSubgroupClasses}} \logpage{[ 7, 4, 4 ]}\nobreak \hyperdef{L}{X819058217B4F3DC0}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{FiniteSubgroupClasses({\mdseries\slshape U})\index{FiniteSubgroupClasses@\texttt{FiniteSubgroupClasses}} \label{FiniteSubgroupClasses} }\hfill{\scriptsize (attribute)}}\\ There exist only finitely many conjugacy classes of finite subgroups in a polycyclic group \mbox{\texttt{\mdseries\slshape U}} and this function can be used to compute them. The algorithm underlying this function proceeds by working down a normal series of \mbox{\texttt{\mdseries\slshape U}} with elementary or free abelian factors. The following function can be used to give the algorithm a specific series. } \subsection{\textcolor{Chapter }{FiniteSubgroupClassesBySeries}} \logpage{[ 7, 4, 5 ]}\nobreak \hyperdef{L}{X7E7C32EA81A297B6}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{FiniteSubgroupClassesBySeries({\mdseries\slshape U, pcps})\index{FiniteSubgroupClassesBySeries@\texttt{FiniteSubgroupClassesBySeries}} \label{FiniteSubgroupClassesBySeries} }\hfill{\scriptsize (function)}}\\ \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := ExamplesOfSomePcpGroups(15);| Pcp-group with orders [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 4, 0 ] !gapprompt@gap>| !gapinput@TorsionSubgroup(G);| Pcp-group with orders [ 5, 2 ] !gapprompt@gap>| !gapinput@NormalTorsionSubgroup(G);| Pcp-group with orders [ 5, 2 ] !gapprompt@gap>| !gapinput@IsTorsionFree(G);| false !gapprompt@gap>| !gapinput@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 ] !gapprompt@gap>| !gapinput@G := DihedralPcpGroup( 0 );| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@TorsionSubgroup(G);| fail !gapprompt@gap>| !gapinput@NormalTorsionSubgroup(G);| Pcp-group with orders [ ] !gapprompt@gap>| !gapinput@IsTorsionFree(G);| false !gapprompt@gap>| !gapinput@FiniteSubgroupClasses(G);| [ Pcp-group with orders [ 2 ]^G, Pcp-group with orders [ 2 ]^G, Pcp-group with orders [ ]^G ] \end{Verbatim} } } \section{\textcolor{Chapter }{Subgroups of finite index and maximal subgroups}}\label{Subgroups of finite index and maximal subgroups} \logpage{[ 7, 5, 0 ]} \hyperdef{L}{X7D9F737F80F6E396}{} { Here we outline functions to determine various types of subgroups of finite index in polycyclic groups. Again, see \cite{Eic00} for a description of the algorithms underlying the functions in this section. Also, we refer to \cite{Lo99} for an alternative approach. \subsection{\textcolor{Chapter }{MaximalSubgroupClassesByIndex}} \logpage{[ 7, 5, 1 ]}\nobreak \hyperdef{L}{X87D62D497A8715FB}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{MaximalSubgroupClassesByIndex({\mdseries\slshape U, p})\index{MaximalSubgroupClassesByIndex@\texttt{MaximalSubgroupClassesByIndex}} \label{MaximalSubgroupClassesByIndex} }\hfill{\scriptsize (operation)}}\\ Each maximal subgroup of a polycyclic group \mbox{\texttt{\mdseries\slshape U}} has \mbox{\texttt{\mdseries\slshape p}}\texttt{\symbol{45}}power index for some prime \mbox{\texttt{\mdseries\slshape p}}. This function can be used to determine the conjugacy classes of all maximal subgroups of \mbox{\texttt{\mdseries\slshape p}}\texttt{\symbol{45}}power index for a given prime \mbox{\texttt{\mdseries\slshape p}}. } \subsection{\textcolor{Chapter }{LowIndexSubgroupClasses}} \logpage{[ 7, 5, 2 ]}\nobreak \hyperdef{L}{X7800133F81BC7674}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{LowIndexSubgroupClasses({\mdseries\slshape U, n})\index{LowIndexSubgroupClasses@\texttt{LowIndexSubgroupClasses}} \label{LowIndexSubgroupClasses} }\hfill{\scriptsize (operation)}}\\ There are only finitely many subgroups of a given index in a polycyclic group \mbox{\texttt{\mdseries\slshape U}}. This function computes conjugacy classes of all subgroups of index \mbox{\texttt{\mdseries\slshape n}} in \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{LowIndexNormalSubgroups}} \logpage{[ 7, 5, 3 ]}\nobreak \hyperdef{L}{X7F7067C77F2DC32C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{LowIndexNormalSubgroups({\mdseries\slshape U, n})\index{LowIndexNormalSubgroups@\texttt{LowIndexNormalSubgroups}} \label{LowIndexNormalSubgroups} }\hfill{\scriptsize (operation)}}\\ This function computes the normal subgroups of index \mbox{\texttt{\mdseries\slshape n}} in \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{NilpotentByAbelianNormalSubgroup}} \logpage{[ 7, 5, 4 ]}\nobreak \hyperdef{L}{X85A5BC447D83175F}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NilpotentByAbelianNormalSubgroup({\mdseries\slshape U})\index{NilpotentByAbelianNormalSubgroup@\texttt{NilpotentByAbelianNormalSubgroup}} \label{NilpotentByAbelianNormalSubgroup} }\hfill{\scriptsize (function)}}\\ This function returns a normal subgroup \mbox{\texttt{\mdseries\slshape N}} of finite index in \mbox{\texttt{\mdseries\slshape U}} such that \mbox{\texttt{\mdseries\slshape N}} is nilpotent\texttt{\symbol{45}}by\texttt{\symbol{45}}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. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := ExamplesOfSomePcpGroups(2);| Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] !gapprompt@gap>| !gapinput@MaximalSubgroupClassesByIndex( G, 61 );;| !gapprompt@gap>| !gapinput@max := List( last, Representative );;| !gapprompt@gap>| !gapinput@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 ] !gapprompt@gap>| !gapinput@LowIndexSubgroupClasses( G, 61 );;| !gapprompt@gap>| !gapinput@low := List( last, Representative );;| !gapprompt@gap>| !gapinput@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 ] \end{Verbatim} } } \section{\textcolor{Chapter }{Further attributes for pcp\texttt{\symbol{45}}groups based on the Fitting subgroup}}\label{Further attributes for pcp-groups based on the Fitting subgroup} \logpage{[ 7, 6, 0 ]} \hyperdef{L}{X785E0E877AB1D549}{} { In this section we provide a variety of other attributes for pcp\texttt{\symbol{45}}groups. Most of the methods below are based or related to the Fitting subgroup of the given group. We refer to \cite{Eic01} for a description of the underlying methods. \subsection{\textcolor{Chapter }{FittingSubgroup}} \logpage{[ 7, 6, 1 ]}\nobreak \hyperdef{L}{X780552B57C30DD8F}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{FittingSubgroup({\mdseries\slshape U})\index{FittingSubgroup@\texttt{FittingSubgroup}} \label{FittingSubgroup} }\hfill{\scriptsize (attribute)}}\\ returns the Fitting subgroup of \mbox{\texttt{\mdseries\slshape U}}; that is, the largest nilpotent normal subgroup of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{IsNilpotentByFinite}} \logpage{[ 7, 6, 2 ]}\nobreak \hyperdef{L}{X86BD63DC844731DF}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsNilpotentByFinite({\mdseries\slshape U})\index{IsNilpotentByFinite@\texttt{IsNilpotentByFinite}} \label{IsNilpotentByFinite} }\hfill{\scriptsize (property)}}\\ checks whether the Fitting subgroup of \mbox{\texttt{\mdseries\slshape U}} has finite index. } \subsection{\textcolor{Chapter }{Centre}} \logpage{[ 7, 6, 3 ]}\nobreak \hyperdef{L}{X847ABE6F781C7FE8}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{Centre({\mdseries\slshape U})\index{Centre@\texttt{Centre}} \label{Centre} }\hfill{\scriptsize (method)}}\\ returns the centre of \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{FCCentre}} \logpage{[ 7, 6, 4 ]}\nobreak \hyperdef{L}{X861C36368435EB09}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{FCCentre({\mdseries\slshape U})\index{FCCentre@\texttt{FCCentre}} \label{FCCentre} }\hfill{\scriptsize (method)}}\\ returns the FC\texttt{\symbol{45}}centre of \mbox{\texttt{\mdseries\slshape U}}; that is, the subgroup containing all elements having a finite conjugacy class in \mbox{\texttt{\mdseries\slshape U}}. } \subsection{\textcolor{Chapter }{PolyZNormalSubgroup}} \logpage{[ 7, 6, 5 ]}\nobreak \hyperdef{L}{X7E75E2BC806746AC}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{PolyZNormalSubgroup({\mdseries\slshape U})\index{PolyZNormalSubgroup@\texttt{PolyZNormalSubgroup}} \label{PolyZNormalSubgroup} }\hfill{\scriptsize (function)}}\\ returns a normal subgroup \mbox{\texttt{\mdseries\slshape N}} of finite index in \mbox{\texttt{\mdseries\slshape U}}, such that \mbox{\texttt{\mdseries\slshape N}} has a polycyclic series with infinite factors only. } \subsection{\textcolor{Chapter }{NilpotentByAbelianByFiniteSeries}} \logpage{[ 7, 6, 6 ]}\nobreak \hyperdef{L}{X86800BF783E30D4A}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NilpotentByAbelianByFiniteSeries({\mdseries\slshape U})\index{NilpotentByAbelianByFiniteSeries@\texttt{NilpotentByAbelianByFiniteSeries}} \label{NilpotentByAbelianByFiniteSeries} }\hfill{\scriptsize (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. } } \section{\textcolor{Chapter }{Functions for nilpotent groups}}\label{Functions for nilpotent groups} \logpage{[ 7, 7, 0 ]} \hyperdef{L}{X878DBDC77CCA4F7E}{} { 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. \subsection{\textcolor{Chapter }{MinimalGeneratingSet}} \logpage{[ 7, 7, 1 ]}\nobreak \hyperdef{L}{X81D15723804771E2}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{MinimalGeneratingSet({\mdseries\slshape U})\index{MinimalGeneratingSet@\texttt{MinimalGeneratingSet}} \label{MinimalGeneratingSet} }\hfill{\scriptsize (method)}}\\ \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@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 ] !gapprompt@gap>| !gapinput@IsNilpotent(G);| true !gapprompt@gap>| !gapinput@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 ] ] !gapprompt@gap>| !gapinput@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 ] ] !gapprompt@gap>| !gapinput@MinimalGeneratingSet(G);| [ g1, g2 ] \end{Verbatim} } } \section{\textcolor{Chapter }{Random methods for pcp\texttt{\symbol{45}}groups}}\label{Random methods for pcp-groups} \logpage{[ 7, 8, 0 ]} \hyperdef{L}{X8640F9D47A1F7434}{} { 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. \subsection{\textcolor{Chapter }{RandomCentralizerPcpGroup (for an element)}} \logpage{[ 7, 8, 1 ]}\nobreak \hyperdef{L}{X80AEE73E7D639699}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RandomCentralizerPcpGroup({\mdseries\slshape U, g})\index{RandomCentralizerPcpGroup@\texttt{RandomCentralizerPcpGroup}!for an element} \label{RandomCentralizerPcpGroup:for an element} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RandomCentralizerPcpGroup({\mdseries\slshape U, V})\index{RandomCentralizerPcpGroup@\texttt{RandomCentralizerPcpGroup}!for a subgroup} \label{RandomCentralizerPcpGroup:for a subgroup} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RandomNormalizerPcpGroup({\mdseries\slshape U, V})\index{RandomNormalizerPcpGroup@\texttt{RandomNormalizerPcpGroup}} \label{RandomNormalizerPcpGroup} }\hfill{\scriptsize (function)}}\\ \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := DihedralPcpGroup(0);| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@mats := [[[-1, 0],[0,1]], [[1,1],[0,1]]];| [ [ [ -1, 0 ], [ 0, 1 ] ], [ [ 1, 1 ], [ 0, 1 ] ] ] !gapprompt@gap>| !gapinput@pcp := Pcp(G);| Pcp [ g1, g2 ] with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@RandomPcpOrbitStabilizer( [1,0], pcp, mats, OnRight ).stab;| #I Orbit longer than limit: exiting. [ ] !gapprompt@gap>| !gapinput@g := Igs(G)[1];| g1 !gapprompt@gap>| !gapinput@RandomCentralizerPcpGroup( G, g );| #I Stabilizer not increasing: exiting. Pcp-group with orders [ 2 ] !gapprompt@gap>| !gapinput@Igs(last);| [ g1 ] \end{Verbatim} } } \section{\textcolor{Chapter }{Non\texttt{\symbol{45}}abelian tensor product and Schur extensions}}\label{Non-abelian tensor product and Schur extensions} \logpage{[ 7, 9, 0 ]} \hyperdef{L}{X824142B784453DB9}{} { \subsection{\textcolor{Chapter }{SchurExtension}} \logpage{[ 7, 9, 1 ]}\nobreak \hyperdef{L}{X79EF28D9845878C9}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SchurExtension({\mdseries\slshape G})\index{SchurExtension@\texttt{SchurExtension}} \label{SchurExtension} }\hfill{\scriptsize (attribute)}}\\ Let \mbox{\texttt{\mdseries\slshape G}} be a polycyclic group with a polycyclic generating sequence consisting of $n$ elements. This function computes the largest central extension \mbox{\texttt{\mdseries\slshape H}} of \mbox{\texttt{\mdseries\slshape G}} such that \mbox{\texttt{\mdseries\slshape H}} is generated by $n$ elements. If $F/R$ is the underlying polycyclic presentation for \mbox{\texttt{\mdseries\slshape G}}, then \mbox{\texttt{\mdseries\slshape H}} is isomorphic to $F/[R,F]$. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := DihedralPcpGroup( 0 );| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@Centre( G );| Pcp-group with orders [ ] !gapprompt@gap>| !gapinput@H := SchurExtension( G );| Pcp-group with orders [ 2, 0, 0, 0 ] !gapprompt@gap>| !gapinput@Centre( H );| Pcp-group with orders [ 0, 0 ] !gapprompt@gap>| !gapinput@H/Centre(H);| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@Subgroup( H, [H.1,H.2] ) = H;| true \end{Verbatim} } \subsection{\textcolor{Chapter }{SchurExtensionEpimorphism}} \logpage{[ 7, 9, 2 ]}\nobreak \hyperdef{L}{X84B60EC978A9A05E}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SchurExtensionEpimorphism({\mdseries\slshape G})\index{SchurExtensionEpimorphism@\texttt{SchurExtensionEpimorphism}} \label{SchurExtensionEpimorphism} }\hfill{\scriptsize (attribute)}}\\ returns the projection from the Schur extension $G^{*}$ of \mbox{\texttt{\mdseries\slshape G}} onto \mbox{\texttt{\mdseries\slshape G}}. See the function \texttt{SchurExtension}. The kernel of this epimorphism is the direct product of the Schur multiplicator of \mbox{\texttt{\mdseries\slshape G}} and a direct product of $n$ copies of ${\ensuremath{\mathbb Z}}$ where $n$ is the number of generators in the polycyclic presentation for \mbox{\texttt{\mdseries\slshape G}}. The Schur multiplicator is the intersection of the kernel and the derived group of the source. See also the function \texttt{SchurCover}. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@gl23 := Range( IsomorphismPcpGroup( GL(2,3) ) );| Pcp-group with orders [ 2, 3, 2, 2, 2 ] !gapprompt@gap>| !gapinput@SchurExtensionEpimorphism( gl23 );| [ g1, g2, g3, g4, g5, g6, g7, g8, g9, g10 ] -> [ g1, g2, g3, g4, g5, id, id, id, id, id ] !gapprompt@gap>| !gapinput@Kernel( last );| Pcp-group with orders [ 0, 0, 0, 0, 0 ] !gapprompt@gap>| !gapinput@AbelianInvariantsMultiplier( gl23 );| [ ] !gapprompt@gap>| !gapinput@Intersection( Kernel(epi), DerivedSubgroup( Source(epi) ) );| [ ] \end{Verbatim} There is a crossed pairing from \mbox{\texttt{\mdseries\slshape G}} into $(G^{*})'$ which can be defined via this epimorphism: \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := DihedralPcpGroup(0);| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@epi := SchurExtensionEpimorphism( G );| [ g1, g2, g3, g4 ] -> [ g1, g2, id, id ] !gapprompt@gap>| !gapinput@PreImagesRepresentative( epi, G.1 );| g1 !gapprompt@gap>| !gapinput@PreImagesRepresentative( epi, G.2 );| g2 !gapprompt@gap>| !gapinput@Comm( last, last2 );| g2^-2*g4 \end{Verbatim} } \subsection{\textcolor{Chapter }{SchurCover}} \logpage{[ 7, 9, 3 ]}\nobreak \hyperdef{L}{X7DD1E37987612042}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SchurCover({\mdseries\slshape G})\index{SchurCover@\texttt{SchurCover}} \label{SchurCover} }\hfill{\scriptsize (function)}}\\ computes a Schur covering group of the polycyclic group \mbox{\texttt{\mdseries\slshape G}}. A Schur covering is a largest central extension \mbox{\texttt{\mdseries\slshape H}} of \mbox{\texttt{\mdseries\slshape G}} such that the kernel \mbox{\texttt{\mdseries\slshape M}} of the projection of \mbox{\texttt{\mdseries\slshape H}} onto \mbox{\texttt{\mdseries\slshape G}} is contained in the commutator subgroup of \mbox{\texttt{\mdseries\slshape H}}. If \mbox{\texttt{\mdseries\slshape G}} is given by a presentation $F/R$, then \mbox{\texttt{\mdseries\slshape 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 \mbox{\texttt{\mdseries\slshape H}} and $R/C$ is isomorphic to \mbox{\texttt{\mdseries\slshape M}}. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := AbelianPcpGroup( 3 );| Pcp-group with orders [ 0, 0, 0 ] !gapprompt@gap>| !gapinput@ext := SchurCover( G );| Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] !gapprompt@gap>| !gapinput@Centre( ext );| Pcp-group with orders [ 0, 0, 0 ] !gapprompt@gap>| !gapinput@IsSubgroup( DerivedSubgroup( ext ), last );| true \end{Verbatim} } \subsection{\textcolor{Chapter }{AbelianInvariantsMultiplier}} \logpage{[ 7, 9, 4 ]}\nobreak \hyperdef{L}{X792BC39D7CEB1D27}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{AbelianInvariantsMultiplier({\mdseries\slshape G})\index{AbelianInvariantsMultiplier@\texttt{AbelianInvariantsMultiplier}} \label{AbelianInvariantsMultiplier} }\hfill{\scriptsize (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. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := DihedralPcpGroup( 0 );| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@DirectProduct( G, AbelianPcpGroup( 2 ) );| Pcp-group with orders [ 0, 0, 2, 0 ] !gapprompt@gap>| !gapinput@AbelianInvariantsMultiplier( last );| [ 0, 2, 2, 2, 2 ] \end{Verbatim} } \subsection{\textcolor{Chapter }{NonAbelianExteriorSquareEpimorphism}} \logpage{[ 7, 9, 5 ]}\nobreak \hyperdef{L}{X822ED5978647C93B}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NonAbelianExteriorSquareEpimorphism({\mdseries\slshape G})\index{NonAbelianExteriorSquareEpimorphism@\texttt{NonAbelianExteriorSquareEpimorphism}} \label{NonAbelianExteriorSquareEpimorphism} }\hfill{\scriptsize (function)}}\\ returns the epimorphism of the non\texttt{\symbol{45}}abelian exterior square of a polycyclic group \mbox{\texttt{\mdseries\slshape G}} onto the derived group of \mbox{\texttt{\mdseries\slshape G}}. The non\texttt{\symbol{45}}abelian exterior square can be defined as the derived subgroup of a Schur cover of \mbox{\texttt{\mdseries\slshape G}}. The isomorphism type of the non\texttt{\symbol{45}}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 \mbox{\texttt{\mdseries\slshape G}} which is what the function returns. The kernel of the epimorphism is isomorphic to the Schur multiplicator of \mbox{\texttt{\mdseries\slshape G}}. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := ExamplesOfSomePcpGroups( 3 );| Pcp-group with orders [ 0, 0 ] !gapprompt@gap>| !gapinput@G := DirectProduct( G,G );| Pcp-group with orders [ 0, 0, 0, 0 ] !gapprompt@gap>| !gapinput@AbelianInvariantsMultiplier( G );| [ [ 0, 1 ], [ 2, 3 ] ] !gapprompt@gap>| !gapinput@epi := NonAbelianExteriorSquareEpimorphism( G );| [ g2^-2*g5, g4^-2*g10, g6, g7, g8, g9 ] -> [ g2^-2, g4^-2, id, id, id, id ] !gapprompt@gap>| !gapinput@Kernel( epi );| Pcp-group with orders [ 0, 2, 2, 2 ] !gapprompt@gap>| !gapinput@Collected( AbelianInvariants( last ) );| [ [ 0, 1 ], [ 2, 3 ] ] \end{Verbatim} } \subsection{\textcolor{Chapter }{NonAbelianExteriorSquare}} \logpage{[ 7, 9, 6 ]}\nobreak \hyperdef{L}{X8739CD4686301A0E}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NonAbelianExteriorSquare({\mdseries\slshape G})\index{NonAbelianExteriorSquare@\texttt{NonAbelianExteriorSquare}} \label{NonAbelianExteriorSquare} }\hfill{\scriptsize (attribute)}}\\ computes the non\texttt{\symbol{45}}abelian exterior square of a polycyclic group \mbox{\texttt{\mdseries\slshape G}}. See the explanation for \texttt{NonAbelianExteriorSquareEpimorphism}. The natural projection of the non\texttt{\symbol{45}}abelian exterior square onto the derived group of \mbox{\texttt{\mdseries\slshape G}} is stored in the component \texttt{!.epimorphism}. There is a crossed pairing from $G\times G$ into $G\wedge G$. See the function \texttt{SchurExtensionEpimorphism} for details. The crossed pairing is stored in the component \texttt{!.crossedPairing}. This is the crossed pairing $\lambda$ in \cite{EickNickel07}. \begin{Verbatim}[commandchars=@|B,fontsize=\small,frame=single,label=Example] @gapprompt|gap>B @gapinput|G := DihedralPcpGroup(0);B Pcp-group with orders [ 2, 0 ] @gapprompt|gap>B @gapinput|GwG := NonAbelianExteriorSquare( G );B Pcp-group with orders [ 0 ] @gapprompt|gap>B @gapinput|lambda := GwG!.crossedPairing;B function( g, h ) ... end @gapprompt|gap>B @gapinput|lambda( G.1, G.2 );B g2^2*g4^-1 \end{Verbatim} } \subsection{\textcolor{Chapter }{NonAbelianTensorSquareEpimorphism}} \logpage{[ 7, 9, 7 ]}\nobreak \hyperdef{L}{X86553D7B7DABF38F}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NonAbelianTensorSquareEpimorphism({\mdseries\slshape G})\index{NonAbelianTensorSquareEpimorphism@\texttt{NonAbelianTensorSquareEpimorphism}} \label{NonAbelianTensorSquareEpimorphism} }\hfill{\scriptsize (function)}}\\ returns for a polycyclic group \mbox{\texttt{\mdseries\slshape G}} the projection of the non\texttt{\symbol{45}}abelian tensor square $G\otimes G$ onto the non\texttt{\symbol{45}}abelian exterior square $G\wedge G$. The range of that epimorphism has the component \texttt{!.epimorphism} set to the projection of the non\texttt{\symbol{45}}abelian exterior square onto the derived group of \mbox{\texttt{\mdseries\slshape G}}. See also the function \texttt{NonAbelianExteriorSquare}. With the result of this function one can compute the groups in the commutative diagram at the beginning of the paper \cite{EickNickel07}. 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)$. \begin{Verbatim}[commandchars=@|B,fontsize=\small,frame=single,label=Example] @gapprompt|gap>B @gapinput|G := DihedralPcpGroup(0);B Pcp-group with orders [ 2, 0 ] @gapprompt|gap>B @gapinput|G := DirectProduct(G,G);B Pcp-group with orders [ 2, 0, 2, 0 ] @gapprompt|gap>B @gapinput|alpha := NonAbelianTensorSquareEpimorphism( G );B [ 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 ] @gapprompt|gap>B @gapinput|gamma := Range( alpha )!.epimorphism;B [ g2^-2*g6, g4^-2*g12, g8, g9, g10, g11 ] -> [ g2^-2, g4^-2, id, id, id, id ] @gapprompt|gap>B @gapinput|JG := Kernel( alpha * gamma );B Pcp-group with orders [ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 ] @gapprompt|gap>B @gapinput|Image( alpha, JG );B Pcp-group with orders [ 2, 2, 2, 2 ] @gapprompt|gap>B @gapinput|AbelianInvariantsMultiplier( G );B [ [ 2, 4 ] ] \end{Verbatim} } \subsection{\textcolor{Chapter }{NonAbelianTensorSquare}} \logpage{[ 7, 9, 8 ]}\nobreak \hyperdef{L}{X7C0DF7C97F78C666}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NonAbelianTensorSquare({\mdseries\slshape G})\index{NonAbelianTensorSquare@\texttt{NonAbelianTensorSquare}} \label{NonAbelianTensorSquare} }\hfill{\scriptsize (attribute)}}\\ computes for a polycyclic group \mbox{\texttt{\mdseries\slshape G}} the non\texttt{\symbol{45}}abelian tensor square $G\otimes G$. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=Example] !gapprompt@gap>| !gapinput@G := AlternatingGroup( IsPcGroup, 4 );| !gapprompt@gap>| !gapinput@PcGroupToPcpGroup( G );| Pcp-group with orders [ 3, 2, 2 ] !gapprompt@gap>| !gapinput@NonAbelianTensorSquare( last );| Pcp-group with orders [ 2, 2, 2, 3 ] !gapprompt@gap>| !gapinput@PcpGroupToPcGroup( last );| !gapprompt@gap>| !gapinput@DirectFactorsOfGroup( last );| [ Group([ f1, f2, f3 ]), Group([ f4 ]) ] !gapprompt@gap>| !gapinput@List( last, Size );| [ 8, 3 ] !gapprompt@gap>| !gapinput@IdGroup( last2[1] );| [ 8, 4 ] # the quaternion group of Order 8 !gapprompt@gap>| !gapinput@G := DihedralPcpGroup( 0 );| Pcp-group with orders [ 2, 0 ] !gapprompt@gap>| !gapinput@ten := NonAbelianTensorSquare( G );| Pcp-group with orders [ 0, 2, 2, 2 ] !gapprompt@gap>| !gapinput@IsAbelian( ten );| true \end{Verbatim} } \subsection{\textcolor{Chapter }{NonAbelianExteriorSquarePlusEmbedding}} \logpage{[ 7, 9, 9 ]}\nobreak \hyperdef{L}{X7AE75EC1860FFE7A}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NonAbelianExteriorSquarePlusEmbedding({\mdseries\slshape G})\index{NonAbelianExteriorSquarePlusEmbedding@\texttt{Non}\-\texttt{Abelian}\-\texttt{Exterior}\-\texttt{Square}\-\texttt{Plus}\-\texttt{Embedding}} \label{NonAbelianExteriorSquarePlusEmbedding} }\hfill{\scriptsize (function)}}\\ returns an embedding from the non\texttt{\symbol{45}}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 \cite{EickNickel07}. The range of the epimorphism is the group $\tau(G)$ in that paper. } \subsection{\textcolor{Chapter }{NonAbelianTensorSquarePlusEpimorphism}} \logpage{[ 7, 9, 10 ]}\nobreak \hyperdef{L}{X7D96C84E87925B0F}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NonAbelianTensorSquarePlusEpimorphism({\mdseries\slshape G})\index{NonAbelianTensorSquarePlusEpimorphism@\texttt{Non}\-\texttt{Abelian}\-\texttt{Tensor}\-\texttt{Square}\-\texttt{Plus}\-\texttt{Epimorphism}} \label{NonAbelianTensorSquarePlusEpimorphism} }\hfill{\scriptsize (function)}}\\ returns an epimorphisms of $\nu(G)$ onto $\tau(G)$. The group $\nu(G)$ is an extension of the non\texttt{\symbol{45}}abelian tensor square $G\otimes G$ of $G$ by $G\times G$. The group $\tau(G)$ is an extension of the non\texttt{\symbol{45}}abelian exterior square $G\wedge G$ by $G\times G$. For details see \cite{EickNickel07}. } \subsection{\textcolor{Chapter }{NonAbelianTensorSquarePlus}} \logpage{[ 7, 9, 11 ]}\nobreak \hyperdef{L}{X8746533787C4E8BC}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{NonAbelianTensorSquarePlus({\mdseries\slshape G})\index{NonAbelianTensorSquarePlus@\texttt{NonAbelianTensorSquarePlus}} \label{NonAbelianTensorSquarePlus} }\hfill{\scriptsize (function)}}\\ returns the group $\nu(G)$ in \cite{EickNickel07}. } \subsection{\textcolor{Chapter }{WhiteheadQuadraticFunctor}} \logpage{[ 7, 9, 12 ]}\nobreak \hyperdef{L}{X78F9184078B2761A}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{WhiteheadQuadraticFunctor({\mdseries\slshape G})\index{WhiteheadQuadraticFunctor@\texttt{WhiteheadQuadraticFunctor}} \label{WhiteheadQuadraticFunctor} }\hfill{\scriptsize (function)}}\\ returns Whitehead's universal quadratic functor of $G$, see \cite{EickNickel07} for a description. } } \section{\textcolor{Chapter }{Schur covers}}\label{Schur covers} \logpage{[ 7, 10, 0 ]} \hyperdef{L}{X7D3023697BA5CE5A}{} { This section contains a function to determine the Schur covers of a finite $p$\texttt{\symbol{45}}group up to isomorphism. \subsection{\textcolor{Chapter }{SchurCovers}} \logpage{[ 7, 10, 1 ]}\nobreak \hyperdef{L}{X7D90B44E7B96AFF1}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SchurCovers({\mdseries\slshape G})\index{SchurCovers@\texttt{SchurCovers}} \label{SchurCovers} }\hfill{\scriptsize (function)}}\\ Let \mbox{\texttt{\mdseries\slshape G}} be a finite $p$\texttt{\symbol{45}}group defined as a pcp group. This function returns a complete and irredundant set of isomorphism types of Schur covers of \mbox{\texttt{\mdseries\slshape G}}. The algorithm implements a method of Nickel's Phd Thesis. } } } \chapter{\textcolor{Chapter }{Cohomology for pcp\texttt{\symbol{45}}groups}}\label{Cohomology for pcp-groups} \logpage{[ 8, 0, 0 ]} \hyperdef{L}{X796AB9787E2A752C}{} { The \textsf{GAP} 4 package \textsf{Polycyclic} provides methods to compute the first and second cohomology group for a pcp\texttt{\symbol{45}}group $U$ and a finite dimensional ${\ensuremath{\mathbb Z}} U$ or $FU$ module $A$ where $F$ is a finite field. The algorithm for determining the first cohomology group is outlined in \cite{Eic00}. As a preparation for the cohomology computation, we introduce the cohomology records. These records provide the technical setup for our cohomology computations. \section{\textcolor{Chapter }{Cohomology records}}\label{Cohomology records} \logpage{[ 8, 1, 0 ]} \hyperdef{L}{X875758FA7C6F5CE1}{} { Cohomology records provide the necessary technical setup for the cohomology computations for polycyclic groups. \subsection{\textcolor{Chapter }{CRRecordByMats}} \logpage{[ 8, 1, 1 ]}\nobreak \hyperdef{L}{X7C97442C7B78806C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{CRRecordByMats({\mdseries\slshape U, mats})\index{CRRecordByMats@\texttt{CRRecordByMats}} \label{CRRecordByMats} }\hfill{\scriptsize (function)}}\\ creates an external module. Let \mbox{\texttt{\mdseries\slshape U}} be a pcp group which acts via the list of matrices \mbox{\texttt{\mdseries\slshape mats}} on a vector space of the form ${\ensuremath{\mathbb Z}}^n$ or $\mathbb{F}_p^n$. Then this function creates a record which can be used as input for the cohomology computations. } \subsection{\textcolor{Chapter }{CRRecordBySubgroup}} \logpage{[ 8, 1, 2 ]}\nobreak \hyperdef{L}{X8646DFA1804D2A11}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{CRRecordBySubgroup({\mdseries\slshape U, A})\index{CRRecordBySubgroup@\texttt{CRRecordBySubgroup}} \label{CRRecordBySubgroup} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{CRRecordByPcp({\mdseries\slshape U, pcp})\index{CRRecordByPcp@\texttt{CRRecordByPcp}} \label{CRRecordByPcp} }\hfill{\scriptsize (function)}}\\ creates an internal module. Let \mbox{\texttt{\mdseries\slshape U}} be a pcp group and let \mbox{\texttt{\mdseries\slshape A}} be a normal elementary or free abelian normal subgroup of \mbox{\texttt{\mdseries\slshape U}} or let \mbox{\texttt{\mdseries\slshape pcp}} be a pcp of a normal elementary of free abelian subfactor of \mbox{\texttt{\mdseries\slshape U}}. Then this function creates a record which can be used as input for the cohomology computations. The returned cohomology record \mbox{\texttt{\mdseries\slshape C}} contains the following entries: \begin{description} \item[{\mbox{\texttt{\mdseries\slshape factor}}}] a pcp of the acting group. If the module is external, then this is \mbox{\texttt{\mdseries\slshape Pcp(U)}}. If the module is internal, then this is \mbox{\texttt{\mdseries\slshape Pcp(U, A)}} or \mbox{\texttt{\mdseries\slshape Pcp(U, GroupOfPcp(pcp))}}. \item[{\mbox{\texttt{\mdseries\slshape mats}}, \mbox{\texttt{\mdseries\slshape invs}} and \mbox{\texttt{\mdseries\slshape one}}}] the matrix action of \mbox{\texttt{\mdseries\slshape factor}} with acting matrices, their inverses and the identity matrix. \item[{\mbox{\texttt{\mdseries\slshape dim}} and \mbox{\texttt{\mdseries\slshape char}}}] the dimension and characteristic of the matrices. \item[{\mbox{\texttt{\mdseries\slshape relators}} and \mbox{\texttt{\mdseries\slshape enumrels}}}] the right hand sides of the polycyclic relators of \mbox{\texttt{\mdseries\slshape factor}} as generator exponents lists and a description for the corresponding left hand sides. \item[{\mbox{\texttt{\mdseries\slshape central}}}] is true, if the matrices \mbox{\texttt{\mdseries\slshape mats}} are all trivial. This is used locally for efficiency reasons. \end{description} And additionally, if $C$ defines an internal module, then it contains: \begin{description} \item[{\mbox{\texttt{\mdseries\slshape group}}}] the original group \mbox{\texttt{\mdseries\slshape U}}. \item[{\mbox{\texttt{\mdseries\slshape normal}}}] this is either \mbox{\texttt{\mdseries\slshape Pcp(A)}} or the input \mbox{\texttt{\mdseries\slshape pcp}}. \item[{\mbox{\texttt{\mdseries\slshape extension}}}] information on the extension of \mbox{\texttt{\mdseries\slshape A}} by \mbox{\texttt{\mdseries\slshape U/A}}. \end{description} } } \section{\textcolor{Chapter }{Cohomology groups}}\label{Cohomology groups} \logpage{[ 8, 2, 0 ]} \hyperdef{L}{X874759D582393441}{} { Let $U$ be a pcp\texttt{\symbol{45}}group and $A$ a free or elementary abelian pcp\texttt{\symbol{45}}group and a $U$\texttt{\symbol{45}}module. By $Z^i(U, A)$ be denote the group of $i$\texttt{\symbol{45}}th cocycles and by $B^i(U, A)$ the $i$\texttt{\symbol{45}}th coboundaries. The factor $Z^i(U,A) / B^i(U,A)$ is the $i$\texttt{\symbol{45}}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 \textsf{Polycyclic} package provides methods to compute first and second cohomology group for a polycyclic group \mbox{\texttt{\mdseries\slshape 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\texttt{\symbol{45}}cocycles and 1\texttt{\symbol{45}}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\texttt{\symbol{45}}presentation of $H$ extending the pc\texttt{\symbol{45}}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. \subsection{\textcolor{Chapter }{OneCoboundariesCR}} \logpage{[ 8, 2, 1 ]}\nobreak \hyperdef{L}{X85EF170387D39D4A}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{OneCoboundariesCR({\mdseries\slshape C})\index{OneCoboundariesCR@\texttt{OneCoboundariesCR}} \label{OneCoboundariesCR} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{OneCocyclesCR({\mdseries\slshape C})\index{OneCocyclesCR@\texttt{OneCocyclesCR}} \label{OneCocyclesCR} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{TwoCoboundariesCR({\mdseries\slshape C})\index{TwoCoboundariesCR@\texttt{TwoCoboundariesCR}} \label{TwoCoboundariesCR} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{TwoCocyclesCR({\mdseries\slshape C})\index{TwoCocyclesCR@\texttt{TwoCocyclesCR}} \label{TwoCocyclesCR} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{OneCohomologyCR({\mdseries\slshape C})\index{OneCohomologyCR@\texttt{OneCohomologyCR}} \label{OneCohomologyCR} }\hfill{\scriptsize (function)}}\\ \noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{TwoCohomologyCR({\mdseries\slshape C})\index{TwoCohomologyCR@\texttt{TwoCohomologyCR}} \label{TwoCohomologyCR} }\hfill{\scriptsize (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. \begin{description} \item[{\mbox{\texttt{\mdseries\slshape gcc}}}] the basis of the cocycles of \mbox{\texttt{\mdseries\slshape C}}. \item[{\mbox{\texttt{\mdseries\slshape gcb}}}] the basis of the coboundaries of \mbox{\texttt{\mdseries\slshape C}}. \item[{\mbox{\texttt{\mdseries\slshape 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 \mbox{\texttt{\mdseries\slshape A}} is free abelian and thus we use a description of this additive map as record. This record contains \begin{description} \item[{\mbox{\texttt{\mdseries\slshape gens}}}] a base for the image. \item[{\mbox{\texttt{\mdseries\slshape rels}}}] relative orders for the image. \item[{\mbox{\texttt{\mdseries\slshape imgs}}}] the images for the elements in \mbox{\texttt{\mdseries\slshape gcc}}. \item[{\mbox{\texttt{\mdseries\slshape prei}}}] preimages for the elements in \mbox{\texttt{\mdseries\slshape gens}}. \item[{\mbox{\texttt{\mdseries\slshape denom}}}] the kernel of the map; that is, another basis for \mbox{\texttt{\mdseries\slshape gcb}}. \end{description} \end{description} 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 } \subsection{\textcolor{Chapter }{TwoCohomologyModCR}} \logpage{[ 8, 2, 2 ]}\nobreak \hyperdef{L}{X79B48D697A8A84C8}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{TwoCohomologyModCR({\mdseries\slshape C, lat})\index{TwoCohomologyModCR@\texttt{TwoCohomologyModCR}} \label{TwoCohomologyModCR} }\hfill{\scriptsize (function)}}\\ where \mbox{\texttt{\mdseries\slshape C}} is a cohomology record and \mbox{\texttt{\mdseries\slshape lat}} is a basis for a sublattice of a free abelian module. The output format is the same as for \texttt{TwoCohomologyCR}. } } \section{\textcolor{Chapter }{Extended 1\texttt{\symbol{45}}cohomology}}\label{Extended 1-cohomology} \logpage{[ 8, 3, 0 ]} \hyperdef{L}{X79610E9178BD0C54}{} { 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. \subsection{\textcolor{Chapter }{OneCoboundariesEX}} \logpage{[ 8, 3, 1 ]}\nobreak \hyperdef{L}{X7E87E3EA81C84621}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{OneCoboundariesEX({\mdseries\slshape C})\index{OneCoboundariesEX@\texttt{OneCoboundariesEX}} \label{OneCoboundariesEX} }\hfill{\scriptsize (function)}}\\ returns a record consisting of the entries \begin{description} \item[{\mbox{\texttt{\mdseries\slshape basis}}}] a basis for $B^1(U, A) \leq A^n$. \item[{\mbox{\texttt{\mdseries\slshape transf}}}] There is a derivation mapping from $A$ to $B^1(U,A)$. This mapping is described here as transformation from $A$ to \mbox{\texttt{\mdseries\slshape basis}}. \item[{\mbox{\texttt{\mdseries\slshape fixpts}}}] the fixpoints of $A$. This is also the kernel of the derivation mapping. \end{description} } \subsection{\textcolor{Chapter }{OneCocyclesEX}} \logpage{[ 8, 3, 2 ]}\nobreak \hyperdef{L}{X8111D2087C16CC0C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{OneCocyclesEX({\mdseries\slshape C})\index{OneCocyclesEX@\texttt{OneCocyclesEX}} \label{OneCocyclesEX} }\hfill{\scriptsize (function)}}\\ returns a record consisting of the entries \begin{description} \item[{\mbox{\texttt{\mdseries\slshape basis}}}] a basis for $Z^1(U, A) \leq A^n$. \item[{\mbox{\texttt{\mdseries\slshape 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. \end{description} } \subsection{\textcolor{Chapter }{OneCohomologyEX}} \logpage{[ 8, 3, 3 ]}\nobreak \hyperdef{L}{X84718DDE792FB212}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{OneCohomologyEX({\mdseries\slshape C})\index{OneCohomologyEX@\texttt{OneCohomologyEX}} \label{OneCohomologyEX} }\hfill{\scriptsize (function)}}\\ returns the combined information on the first cohomology group. } } \section{\textcolor{Chapter }{Extensions and Complements}}\label{Extensions and Complements} \logpage{[ 8, 4, 0 ]} \hyperdef{L}{X853E51787A24AE00}{} { The natural applications of first and second cohomology group is the determination of extensions and complements. Let $C$ be a cohomology record. \subsection{\textcolor{Chapter }{ ComplementCR}} \logpage{[ 8, 4, 1 ]}\nobreak \hyperdef{L}{X7DA9162085058006}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ ComplementCR({\mdseries\slshape C, c})\index{ ComplementCR@\texttt{ ComplementCR}} \label{ ComplementCR} }\hfill{\scriptsize (function)}}\\ returns the complement corresponding to the 1\texttt{\symbol{45}}cocycle \mbox{\texttt{\mdseries\slshape c}}. In the case that \mbox{\texttt{\mdseries\slshape C}} is an external module, we construct the split extension of $U$ with $A$ first and then determine the complement. In the case that \mbox{\texttt{\mdseries\slshape C}} is an internal module, the vector \mbox{\texttt{\mdseries\slshape c}} must be an element of the affine space corresponding to the complements as described by \texttt{OneCocyclesEX}. } \subsection{\textcolor{Chapter }{ ComplementsCR}} \logpage{[ 8, 4, 2 ]}\nobreak \hyperdef{L}{X7F8984D386A813D6}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ ComplementsCR({\mdseries\slshape C})\index{ ComplementsCR@\texttt{ ComplementsCR}} \label{ ComplementsCR} }\hfill{\scriptsize (function)}}\\ returns all complements using the correspondence to $Z^1(U,A)$. Further, this function returns fail, if $Z^1(U,A)$ is infinite. } \subsection{\textcolor{Chapter }{ ComplementClassesCR}} \logpage{[ 8, 4, 3 ]}\nobreak \hyperdef{L}{X7FAB3EB0803197FA}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ ComplementClassesCR({\mdseries\slshape C})\index{ ComplementClassesCR@\texttt{ ComplementClassesCR}} \label{ ComplementClassesCR} }\hfill{\scriptsize (function)}}\\ returns complement classes using the correspondence to $H^1(U,A)$. Further, this function returns fail, if $H^1(U,A)$ is infinite. } \subsection{\textcolor{Chapter }{ ComplementClassesEfaPcps}} \logpage{[ 8, 4, 4 ]}\nobreak \hyperdef{L}{X8759DC59799DD508}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ ComplementClassesEfaPcps({\mdseries\slshape U, N, pcps})\index{ ComplementClassesEfaPcps@\texttt{ ComplementClassesEfaPcps}} \label{ ComplementClassesEfaPcps} }\hfill{\scriptsize (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$\texttt{\symbol{45}}invariant efa series of $N$ described by \mbox{\texttt{\mdseries\slshape 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$.) } \subsection{\textcolor{Chapter }{ ComplementClasses}} \logpage{[ 8, 4, 5 ]}\nobreak \hyperdef{L}{X7B0EC76D81A056AB}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ ComplementClasses({\mdseries\slshape [V, ]U, N})\index{ ComplementClasses@\texttt{ ComplementClasses}} \label{ ComplementClasses} }\hfill{\scriptsize (function)}}\\ Let $N$ and $U$ be normal subgroups of $V$ with $N \leq U \leq V$. This function attempts to compute the $V$\texttt{\symbol{45}}conjugacy classes of complements to $N$ in $U$. The algorithm proceeds by iteration over a $V$\texttt{\symbol{45}}invariant efa series of $N$. If at some stage in this iteration infinitely many complements are discovered, then the algorithm returns fail. } \subsection{\textcolor{Chapter }{ExtensionCR}} \logpage{[ 8, 4, 6 ]}\nobreak \hyperdef{L}{X85F3B55C78CF840B}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ExtensionCR({\mdseries\slshape C, c})\index{ExtensionCR@\texttt{ExtensionCR}} \label{ExtensionCR} }\hfill{\scriptsize (function)}}\\ returns the extension corresponding to the 2\texttt{\symbol{45}}cocycle $c$. } \subsection{\textcolor{Chapter }{ExtensionsCR}} \logpage{[ 8, 4, 7 ]}\nobreak \hyperdef{L}{X81DC85907E0948FD}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ExtensionsCR({\mdseries\slshape C})\index{ExtensionsCR@\texttt{ExtensionsCR}} \label{ExtensionsCR} }\hfill{\scriptsize (function)}}\\ returns all extensions using the correspondence to $Z^2(U,A)$. Further, this function returns fail, if $Z^2(U,A)$ is infinite. } \subsection{\textcolor{Chapter }{ExtensionClassesCR}} \logpage{[ 8, 4, 8 ]}\nobreak \hyperdef{L}{X7AE16E3687E14B24}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{ExtensionClassesCR({\mdseries\slshape C})\index{ExtensionClassesCR@\texttt{ExtensionClassesCR}} \label{ExtensionClassesCR} }\hfill{\scriptsize (function)}}\\ returns extension classes using the correspondence to $H^2(U,A)$. Further, this function returns fail, if $H^2(U,A)$ is infinite. } \subsection{\textcolor{Chapter }{SplitExtensionPcpGroup}} \logpage{[ 8, 4, 9 ]}\nobreak \hyperdef{L}{X7986997B78AD3292}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SplitExtensionPcpGroup({\mdseries\slshape U, mats})\index{SplitExtensionPcpGroup@\texttt{SplitExtensionPcpGroup}} \label{SplitExtensionPcpGroup} }\hfill{\scriptsize (function)}}\\ returns the split extension of \mbox{\texttt{\mdseries\slshape U}} by the $U$\texttt{\symbol{45}}module described by \mbox{\texttt{\mdseries\slshape mats}}. } } \section{\textcolor{Chapter }{Constructing pcp groups as extensions}}\label{Constructing pcp groups as extensions} \logpage{[ 8, 5, 0 ]} \hyperdef{L}{X823771527DBD857D}{} { 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. \begin{Verbatim}[commandchars=@|A,fontsize=\small,frame=single,label=Example] # get the group and its matrix action @gapprompt|gap>A @gapinput|G := UnitriangularPcpGroup(3,0);A Pcp-group with orders [ 0, 0, 0 ] @gapprompt|gap>A @gapinput|mats := G!.mats;A [ [ [ 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 @gapprompt|gap>A @gapinput|C := CRRecordByMats(G,mats);;A # compute the second cohomology group @gapprompt|gap>A @gapinput|cc := TwoCohomologyCR(C);;A # the abelian invariants of H^2(G,M) @gapprompt|gap>A @gapinput|cc.factor.rels;A [ 2, 0, 0 ] # construct an extension which corresponds to a cocycle that has # infinite image in H^2(G,M) @gapprompt|gap>A @gapinput|c := cc.factor.prei[2];A [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, 1 ] @gapprompt|gap>A @gapinput|H := ExtensionCR( C, c);A Pcp-group with orders [ 0, 0, 0, 0, 0, 0 ] # check that the extension does not split - get the normal subgroup @gapprompt|gap>A @gapinput|N := H!.module;A Pcp-group with orders [ 0, 0, 0 ] # create the interal module @gapprompt|gap>A @gapinput|C := CRRecordBySubgroup(H,N);;A # use the complements routine @gapprompt|gap>A @gapinput|ComplementClassesCR(C);A [ ] \end{Verbatim} } } \chapter{\textcolor{Chapter }{Matrix Representations}}\label{Matrix Representations} \logpage{[ 9, 0, 0 ]} \hyperdef{L}{X858D1BB07A8FBF87}{} { This chapter describes functions which compute with matrix representations for pcp\texttt{\symbol{45}}groups. So far the routines in this package are only able to compute matrix representations for torsion\texttt{\symbol{45}}free nilpotent groups. \section{\textcolor{Chapter }{Unitriangular matrix groups}}\label{Unitriangular matrix groups} \logpage{[ 9, 1, 0 ]} \hyperdef{L}{X7D0ED06C7E6A457D}{} { \subsection{\textcolor{Chapter }{UnitriangularMatrixRepresentation}} \logpage{[ 9, 1, 1 ]}\nobreak \hyperdef{L}{X7E6F320F865E309C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{UnitriangularMatrixRepresentation({\mdseries\slshape G})\index{UnitriangularMatrixRepresentation@\texttt{UnitriangularMatrixRepresentation}} \label{UnitriangularMatrixRepresentation} }\hfill{\scriptsize (operation)}}\\ computes a faithful representation of a torsion\texttt{\symbol{45}}free nilpotent group \mbox{\texttt{\mdseries\slshape G}} as unipotent lower triangular matrices over the integers. The pc\texttt{\symbol{45}}presentation for \mbox{\texttt{\mdseries\slshape G}} must not contain any power relations. The algorithm is described in \cite{dGN02}. } \subsection{\textcolor{Chapter }{IsMatrixRepresentation}} \logpage{[ 9, 1, 2 ]}\nobreak \hyperdef{L}{X7F5E7F5F7DDB2E2C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsMatrixRepresentation({\mdseries\slshape G, matrices})\index{IsMatrixRepresentation@\texttt{IsMatrixRepresentation}} \label{IsMatrixRepresentation} }\hfill{\scriptsize (function)}}\\ checks if the map defined by mapping the $i$\texttt{\symbol{45}}th generator of the pcp\texttt{\symbol{45}}group \mbox{\texttt{\mdseries\slshape G}} to the $i$\texttt{\symbol{45}}th matrix of \mbox{\texttt{\mdseries\slshape matrices}} defines a homomorphism. } } \section{\textcolor{Chapter }{Upper unitriangular matrix groups}}\label{Upper unitriangular matrix groups} \logpage{[ 9, 2, 0 ]} \hyperdef{L}{X79A8A51B84E4BF8C}{} { 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,{\ensuremath{\mathbb Z}})$ is torsion\texttt{\symbol{45}}free nilpotent. The $k$\texttt{\symbol{45}}th term of its lower central series is the set of all matrices of weight $k-1$. The ${\ensuremath{\mathbb Z}}$\texttt{\symbol{45}}rank of the $k$\texttt{\symbol{45}}th term of the lower central series modulo the $(k+1)$\texttt{\symbol{45}}th term is $n-k$. \subsection{\textcolor{Chapter }{IsomorphismUpperUnitriMatGroupPcpGroup}} \logpage{[ 9, 2, 1 ]}\nobreak \hyperdef{L}{X8434972E7DDB68C1}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{IsomorphismUpperUnitriMatGroupPcpGroup({\mdseries\slshape G})\index{IsomorphismUpperUnitriMatGroupPcpGroup@\texttt{Isomorphism}\-\texttt{Upper}\-\texttt{Unitri}\-\texttt{Mat}\-\texttt{Group}\-\texttt{Pcp}\-\texttt{Group}} \label{IsomorphismUpperUnitriMatGroupPcpGroup} }\hfill{\scriptsize (function)}}\\ takes a group \mbox{\texttt{\mdseries\slshape G}} generated by upper unitriangular matrices over the integers and computes a polycyclic 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\texttt{\symbol{45}}free nilpotent. } \subsection{\textcolor{Chapter }{SiftUpperUnitriMatGroup}} \logpage{[ 9, 2, 2 ]}\nobreak \hyperdef{L}{X843C9D427FFA2487}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SiftUpperUnitriMatGroup({\mdseries\slshape G})\index{SiftUpperUnitriMatGroup@\texttt{SiftUpperUnitriMatGroup}} \label{SiftUpperUnitriMatGroup} }\hfill{\scriptsize (function)}}\\ takes a group \mbox{\texttt{\mdseries\slshape G}} generated by upper unitriangular matrices over the integers and returns a recursive data structure \mbox{\texttt{\mdseries\slshape L}} with the following properties: \mbox{\texttt{\mdseries\slshape L}} contains a polycyclic generating sequence for \mbox{\texttt{\mdseries\slshape G}}, using \mbox{\texttt{\mdseries\slshape L}} one can decide if a given upper unitriangular matrix is contained in \mbox{\texttt{\mdseries\slshape G}}, a given element of \mbox{\texttt{\mdseries\slshape G}} can be written as a word in the polycyclic generating sequence. \mbox{\texttt{\mdseries\slshape L}} is a representation of a chain of subgroups of \mbox{\texttt{\mdseries\slshape G}} refining the lower centrals series of \mbox{\texttt{\mdseries\slshape G}}.. It contains for each subgroup in the chain a minimal generating set. } \subsection{\textcolor{Chapter }{RanksLevels}} \logpage{[ 9, 2, 3 ]}\nobreak \hyperdef{L}{X7CF8B8F981931846}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{RanksLevels({\mdseries\slshape L})\index{RanksLevels@\texttt{RanksLevels}} \label{RanksLevels} }\hfill{\scriptsize (function)}}\\ takes the data structure returned by \texttt{SiftUpperUnitriMat} and prints the ${\ensuremath{\mathbb Z}}$\texttt{\symbol{45}}rank of each the subgroup in \mbox{\texttt{\mdseries\slshape L}}. } \subsection{\textcolor{Chapter }{MakeNewLevel}} \logpage{[ 9, 2, 4 ]}\nobreak \hyperdef{L}{X81F3760186734EA7}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{MakeNewLevel({\mdseries\slshape m})\index{MakeNewLevel@\texttt{MakeNewLevel}} \label{MakeNewLevel} }\hfill{\scriptsize (function)}}\\ creates one level of the data structure returned by \texttt{SiftUpperUnitriMat} and initialises it with weight \mbox{\texttt{\mdseries\slshape m}}. } \subsection{\textcolor{Chapter }{SiftUpperUnitriMat}} \logpage{[ 9, 2, 5 ]}\nobreak \hyperdef{L}{X851A216C85B74574}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{SiftUpperUnitriMat({\mdseries\slshape gens, level, M})\index{SiftUpperUnitriMat@\texttt{SiftUpperUnitriMat}} \label{SiftUpperUnitriMat} }\hfill{\scriptsize (function)}}\\ takes the generators \mbox{\texttt{\mdseries\slshape gens}} of an upper unitriangular group, the data structure returned \mbox{\texttt{\mdseries\slshape level}} by \texttt{SiftUpperUnitriMat} and another upper unitriangular matrix \mbox{\texttt{\mdseries\slshape M}}. It sift \mbox{\texttt{\mdseries\slshape M}} through \mbox{\texttt{\mdseries\slshape level}} and adds \mbox{\texttt{\mdseries\slshape M}} at the appropriate place if \mbox{\texttt{\mdseries\slshape M}} is not contained in the subgroup represented by \mbox{\texttt{\mdseries\slshape level}}. The function \texttt{SiftUpperUnitriMatGroup} illustrates the use of \texttt{SiftUpperUnitriMat}. \begin{Verbatim}[commandchars=!@|,fontsize=\small,frame=single,label=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 ); \end{Verbatim} } \subsection{\textcolor{Chapter }{DecomposeUpperUnitriMat}} \logpage{[ 9, 2, 6 ]}\nobreak \hyperdef{L}{X86D711217C639C2C}{} {\noindent\textcolor{FuncColor}{$\triangleright$\enspace\texttt{DecomposeUpperUnitriMat({\mdseries\slshape level, M})\index{DecomposeUpperUnitriMat@\texttt{DecomposeUpperUnitriMat}} \label{DecomposeUpperUnitriMat} }\hfill{\scriptsize (function)}}\\ takes the data structure \mbox{\texttt{\mdseries\slshape level}} returned by \texttt{SiftUpperUnitriMatGroup} and a upper unitriangular matrix \mbox{\texttt{\mdseries\slshape M}} and decomposes \mbox{\texttt{\mdseries\slshape M}} into a word in the polycyclic generating sequence of \mbox{\texttt{\mdseries\slshape level}}. } } } \appendix \chapter{\textcolor{Chapter }{Obsolete Functions and Name Changes}}\label{app:Obsolete} \logpage{[ "A", 0, 0 ]} \hyperdef{L}{X874ECE907CAF380D}{} { Over time, the interface of \textsf{Polycyclic} has changed. This was done to get the names of \textsf{Polycyclic} functions to agree with the general naming conventions used throughout GAP. Also, some \textsf{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 \textsf{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. \index{SchurCovering@\texttt{SchurCovering}} \index{SchurMultPcpGroup@\texttt{SchurMultPcpGroup}} \begin{center} \begin{tabular}{l|l}\emph{OLD}& \emph{NOW USE}\\ \hline \texttt{SchurCovering}& \texttt{SchurCover} (\ref{SchurCover})\\ \texttt{SchurMultPcpGroup}& \texttt{AbelianInvariantsMultiplier} (\ref{AbelianInvariantsMultiplier})\\ \end{tabular}\\[2mm] \end{center} } \def\bibname{References\logpage{[ "Bib", 0, 0 ]} \hyperdef{L}{X7A6F98FD85F02BFE}{} } \bibliographystyle{alpha} \bibliography{polycyclicbib.xml} \addcontentsline{toc}{chapter}{References} \def\indexname{Index\logpage{[ "Ind", 0, 0 ]} \hyperdef{L}{X83A0356F839C696F}{} } \cleardoublepage \phantomsection \addcontentsline{toc}{chapter}{Index} \printindex \immediate\write\pagenrlog{["Ind", 0, 0], \arabic{page},} \newpage \immediate\write\pagenrlog{["End"], \arabic{page}];} \immediate\closeout\pagenrlog \end{document} polycyclic-2.17/doc/chapBib.txt0000644000175100001660000001071415054022512016015 0ustar runnerdocker 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. [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. [LS90] Leedham-Green, C. R. and Soicher, L. H., Collection from the left and other strategies, J. Symbolic Comput., 9, 5-6 (1990), 665--675. [LS98] Leedham-Green, C. R. and Soicher, L. H., Symbolic collection using Deep Thought, LMS J. Comput. Math., 1 (1998), 9--24 (electronic). [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). [Vau90] Vaughan-Lee, M. R., Collection from the left, J. Symbolic Comput., 9, 5-6 (1990), 725--733.  polycyclic-2.17/doc/cohom.xml0000644000175100001660000003264115054022512015556 0ustar runnerdocker 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( C, 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.17/doc/defins.xml0000644000175100001660000002072115054022512015715 0ustar runnerdocker 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.17/doc/manual.css0000644000175100001660000001575415054022512015724 0ustar runnerdocker/* 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.17/doc/polycyclicbib.xml0000644000175100001660000002355115054022512017300 0ustar runnerdocker 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.17/doc/_entities.xml0000644000175100001660000000006515054022512016427 0ustar runnerdockerpolycyclic'> polycyclic-2.17/doc/chap1.txt0000644000175100001660000000553015054022512015461 0ustar runnerdocker 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.17/doc/chap6_mj.html0000644000175100001660000003302115054022512016275 0ustar runnerdocker 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 )
‣ AbelianPcpGroup( 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 or infinity. If n is not provided then the length of rels is used. If rels is omitted then all generators will have infinite order.

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.17/doc/ragged.css0000644000175100001660000000023115054022512015660 0ustar runnerdocker/* times.css Frank Lübeck */ /* Change default CSS to use Times font. */ body { text-align: left; } polycyclic-2.17/doc/chap5_mj.html0000644000175100001660000014124615054022512016305 0ustar runnerdocker 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.17/doc/polycyclic.xml0000644000175100001660000000174715054022512016626 0ustar runnerdocker 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.17/PackageInfo.g0000644000175100001660000001340515054022512015476 0ustar runnerdocker############################################################################# ## #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.17", Date := "28/08/2025", # 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", GitHubUsername:= "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 := "mhorn@rptu.de", WWWHome := "https://www.quendi.de/math", GitHubUsername:= "fingolfin", PostalAddress := Concatenation( "Fachbereich Mathematik\n", "RPTU Kaiserslautern-Landau\n", "Gottlieb-Daimler-Straße 48\n", "67663 Kaiserslautern\n", "Germany" ), Place := "Kaiserslautern, Germany", Institution := "RPTU Kaiserslautern-Landau", ), rec( LastName := "Fernández Ayala", FirstNames := "Óscar", IsAuthor := false, IsMaintainer := true, Email := "oscar00ayala@gmail.com", WWWHome := "https://Osferay.github.io", GitHubUsername := "osferay", #PostalAddress := TODO, Place := "Braunschweig", Institution := "TU Braunschweig", ), rec( LastName := "Tertooy", FirstNames := "Sam", IsAuthor := false, IsMaintainer := true, Email := "sam.tertooy@kuleuven.be", WWWHome := "https://stertooy.github.io/", GitHubUsername := "stertooy", PostalAddress := """ Wiskunde KU Leuven, Kulak Kortrijk Campus Etienne Sabbelaan 53 8500 Kortrijk Belgium """, Place := "Kortrijk, Belgium", Institution := "KU Leuven, Kulak Kortrijk Campus", ), ], 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_mj.html", PDFFile := "doc/manual.pdf", SixFile := "doc/manual.six", LongTitle := "Computation with polycyclic groups", ), Dependencies := rec( GAP := ">= 4.10", 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.17/gap/0000755000175100001660000000000015054022512013723 5ustar runnerdockerpolycyclic-2.17/gap/cohom/0000755000175100001660000000000015054022512015030 5ustar runnerdockerpolycyclic-2.17/gap/cohom/abelaut.gd0000644000175100001660000000130415054022512016757 0ustar runnerdocker############################################################################# ## #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" ); BindGlobal("APEndoFamily", NewFamily( "APEndoFam", IsAPEndo, IsAPEndo )); DeclareRepresentation( "IsAPEndoRep", IsComponentObjectRep, ["mat", "dim", "exp", "prime"] ); DeclareGlobalFunction( "APEndoNC" ); DeclareGlobalFunction( "APEndo" ); polycyclic-2.17/gap/cohom/abelaut.gi0000644000175100001660000000666115054022512016777 0ustar runnerdocker############################################################################# ## #W abelaut.gi Polycyc Bettina Eick ## BindGlobal( "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 ); BindGlobal( "IdentityAPEndo", function( exp, p ) return APEndoNC( IdentityMat(Length(exp)), exp, p ); end ); BindGlobal( "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.17/gap/cohom/solcohom.gi0000644000175100001660000001731615054022512017204 0ustar runnerdocker############################################################################# ## #W solcohom.gi Polycyc Bettina Eick ## ############################################################################# ## #F CRSystem( d, l, c ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ## BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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. ## BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ## BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ## BindGlobal( "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.17/gap/cohom/norcom.gi0000644000175100001660000001610515054022512016651 0ustar runnerdocker############################################################################# ## #W norcom.gi Polycyc Bettina Eick ## ## ## computing normal complements ## ############################################################################# ## #F ComplementsCR( C ) . . . . . . . . . . . . . . . . . . . . all complements ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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 FixedPointsOfAction( pts, gens, oper ) ## BindGlobal( "FixedPointsOfAction", function( pts, gens, oper ) return Filtered( pts, x -> ForAll( gens, y -> oper( x, y ) = x ) ); end ); ############################################################################# ## #F InvariantComplementsCR( C ) . . . . . . . . . . .invariant under operation ## BindGlobal( "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 := FixedPointsOfAction( 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. ## BindGlobal( "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 ## BindGlobal( "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.17/gap/cohom/README0000644000175100001660000000074315054022512015714 0ustar runnerdocker 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.17/gap/cohom/intcohom.gi0000644000175100001660000001603615054022512017177 0ustar runnerdocker############################################################################# ## #W intcohom.gi Polycyc Bettina Eick ## ############################################################################# ## ## IntKernelCR( A, sys, lat, bat ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/cohom/onecohom.gi0000644000175100001660000001434415054022512017166 0ustar runnerdocker############################################################################# ## #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 ) ## BindGlobal( "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 ## BindGlobal( "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 ) ## BindGlobal( "ComplementByH1Element", function( A, coh, elm ) local coc; coc := coh.FactorToCoc( coh, elm ) + coh.sol; return ComplementCR( A, coc ); end ); polycyclic-2.17/gap/cohom/general.gi0000644000175100001660000001251015054022512016765 0ustar runnerdocker############################################################################# ## #W general.gi Polycyc Bettina Eick ## ## General stuff for cohomology computations. ## ############################################################################# ## #F CollectedOneCR( A, w ) . . . . . . . . . . . . . . . . . . . . . comb word ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/cohom/cohom.gi0000644000175100001660000002153615054022512016465 0ustar runnerdocker############################################################################# ## #W cohom.gi Polycyc Bettina Eick ## ## Defining a module for the cohomology functions. ## ############################################################################# ## #F WordOfVectorCR( v ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/cohom/grpext.gi0000644000175100001660000002575415054022512016677 0ustar runnerdocker############################################################################# ## #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 ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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 ); ############################################################################# ## #M SemiDirectProduct( G, alpha, N ) . . . . . . . . . . . . . for pcp groups ## InstallMethod( SemidirectProduct, "for pcp groups", [ IsPcpGroup, IsGroupHomomorphism, IsPcpGroup ], function( G, alpha, N ) local auts, groups, S, f, info; auts := List( Igs( G ), g -> ImagesRepresentative( alpha, g ) ); groups := [ G, N ]; S := SplitExtensionByAutomorphisms( N, G, auts ); f := [ 1, Length( Igs( G ) )+1, Length( Igs( S ) )+1 ]; info := rec(groups := groups, first := f, embeddings := [ ], projections := false); SetSemidirectProductInfo( S, info ); if ForAny( groups, H -> HasIsFinite( H ) and not IsFinite( H ) ) then SetSize( S ,infinity ); elif ForAll( groups, HasSize ) then SetSize( S, Product( List( groups, Size ) ) ); fi; return S; end ); ############################################################################# ## #A Embedding ## InstallMethod( Embedding, true, [ IsPcpGroup and HasSemidirectProductInfo, IsPosInt ], 0, function( S, i ) local info, G, imgs, hom, gens; # check info := SemidirectProductInfo( S ); if IsBound( info.embeddings[i] ) then return info.embeddings[i]; fi; # compute embedding G := info.groups[i]; gens := Igs( G ); imgs := Igs( S ){[info.first[i] .. info.first[i+1]-1]}; hom := GroupHomomorphismByImagesNC( G, S, gens, imgs ); SetIsInjective( hom, true ); # store information info.embeddings[i] := hom; return hom; end ); ############################################################################# ## #A Projection ## InstallOtherMethod( Projection, true, [ IsPcpGroup and HasSemidirectProductInfo ], 0, function( S ) local info, G, imgs, hom, N, gens; # check info := SemidirectProductInfo( S ); if not IsBool( info.projections ) then return info.projections; fi; # compute projection G := info.groups[1]; gens := Igs( S ); imgs := Concatenation( Igs( G ), List( [info.first[2]..Length(gens)], x -> One(G))); hom := GroupHomomorphismByImagesNC( S, G, gens, imgs ); SetIsSurjective( hom, true ); # add kernel N := SubgroupNC( S, gens{[info.first[2]..Length(gens)]}); SetKernelOfMultiplicativeGeneralMapping( hom, N ); # store information info.projections := hom; return hom; end ); polycyclic-2.17/gap/cohom/addgrp.gi0000644000175100001660000001742515054022512016623 0ustar runnerdocker############################################################################# ## #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 ## BindGlobal( "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 ) ## BindGlobal( "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. ## BindGlobal( "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 ); BindGlobal( "SizeAddFactor", function( fact ) if ForAny( fact.rels, x -> x = 0 ) then return infinity; else return Product( fact.rels ); fi; end ); BindGlobal( "ElementsAddFactor", function( fact ) return ExponentsByRels( fact.rels ); end ); polycyclic-2.17/gap/cohom/cohom.gd0000644000175100001660000000156615054022512016461 0ustar runnerdocker############################################################################# ## #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.17/gap/cohom/grpcom.gi0000644000175100001660000002340515054022512016644 0ustar runnerdocker############################################################################# ## #W grpcom.gi Polycyc Bettina Eick ## ## ## computing conjugacy classes of complements ## ############################################################################# ## #F PushVector( mats, invs, one, coc, exp ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/cohom/solabel.gi0000644000175100001660000001573415054022512017004 0ustar runnerdocker############################################################################# ## #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 BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "Strip", function( mat, l ) return mat{[StripIt(mat,l)..Length(mat)]}; end ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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.17/gap/cohom/twocohom.gi0000644000175100001660000003110015054022512017203 0ustar runnerdocker############################################################################# ## #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] ) ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/pcpgrp/0000755000175100001660000000000015054022512015216 5ustar runnerdockerpolycyclic-2.17/gap/pcpgrp/maxsub.gi0000644000175100001660000000504015054022512017035 0ustar runnerdocker############################################################################# ## #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. ## BindGlobal( "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.17/gap/pcpgrp/pcpgrp.gd0000644000175100001660000000243715054022512017033 0ustar runnerdocker############################################################################# ## #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.17/gap/pcpgrp/polyz.gi0000644000175100001660000000366515054022512016726 0ustar runnerdocker############################################################################ ## #W polyz.gi Polycyc Bettina Eick ## ############################################################################ ## #F GeneratorsOfCentralizerOfPcp( gens, pcp ) ## BindGlobal( "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.17/gap/pcpgrp/normcon.gi0000644000175100001660000002441115054022512017214 0ustar runnerdocker############################################################################ ## #W normcon.gi Polycyc Bettina Eick ## ## Computing normalizers of subgroups. ## Solving the conjugacy problem for subgroups. ## ############################################################################# ## #F AffineActionOnH1( CR, cc ) ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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; c := VectorByComplement( CR, H ); if not IsBool( cc.fld ) then c := c * One( cc.fld ); fi; # stabilize vector if Length( cc.factor.rels ) > 0 then Info( InfoPcpGrp, 2, " H1 is of type ",cc.factor.rels); 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 ) ## BindGlobal( "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 ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/pcpgrp/grpinva.gi0000644000175100001660000001540115054022512017206 0ustar runnerdocker############################################################################# ## #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 ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 := FixedPointsOfAction( 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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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. ## BindGlobal( "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 ); BindGlobal( "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.17/gap/pcpgrp/schur.gi0000644000175100001660000002735115054022512016673 0ustar runnerdocker############################################################################# ## #A ReduceTail ## BindGlobal( "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 BindGlobal( "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( PreImagesRepresentativeNC( lift, g ), PreImagesRepresentativeNC( 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 BindGlobal( "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 ); BindGlobal( "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.17/gap/pcpgrp/nilpot.gi0000644000175100001660000001124415054022512017046 0ustar runnerdocker############################################################################ ## #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 ) ## BindGlobal( "MinimalGeneratingSetNilpotentPcpGroup", function( G ) return GeneratorsOfPcp( Pcp( G, DerivedSubgroup(G), "snf" ) ); end ); ############################################################################# ## #F PcpNextStepCentralizer( gens, cent, pcp ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/pcpgrp/findex.gi0000644000175100001660000002214615054022512017021 0ustar runnerdocker############################################################################# ## #W findex.gi Polycyc Bettina Eick ## ## Conjugacy classes of subgroups of given index. ## ############################################################################# ## #F SubgroupsFirstLayerByIndex( G, pcp, n ) ## ## All subgroup of index dividing n. ## BindGlobal( "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 ) ## BindGlobal( "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. ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/pcpgrp/centcon.gi0000644000175100001660000002443315054022512017176 0ustar runnerdocker############################################################################ ## #W centcon.gi Polycyc Bettina Eick ## ## Computing centralizers of elements and subgroups. ## Solving the conjugacy problem for elements. ## ############################################################################# ## #F AffineActionByElement( gens, pcp, g ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ## BindGlobal( "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( Igs(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 ## BindGlobal( "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 TryNextMethod(); 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 ) ## BindGlobal( "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 ) ## BindGlobal( "ConjugacyElementsBySeries", function( G, g, h, pcps ) local C, k, eg, eh, i, pcp, rel, p, d, t, 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; t := TransversalElement( j, stb, One(G) ); stb := List( stb.stab, x -> x^t ); stb := AddIgsToIgs( stb, Igs(M) ); C := SubgroupByIgs( G, stb ); k := k * t; # 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, c ); 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 * PreImagesRepresentativeNC( 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.17/gap/pcpgrp/inters.gi0000644000175100001660000001143515054022512017047 0ustar runnerdocker############################################################################# ## #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.17/gap/pcpgrp/wreath.gi0000644000175100001660000001114215054022512017030 0ustar runnerdocker############################################################################# ## #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.17/gap/pcpgrp/nindex.gi0000644000175100001660000001524415054022512017032 0ustar runnerdocker############################################################################# ## #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. ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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 NilpotentByAbelianByFiniteSeries. ## 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.17/gap/pcpgrp/README0000644000175100001660000000260715054022512016103 0ustar runnerdocker 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.17/gap/pcpgrp/tensor_nq.gi0000644000175100001660000001165615054022512017560 0ustar runnerdocker## ## 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) ## BindGlobal( "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) ## BindGlobal( "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 ); BindGlobal( "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) ## BindGlobal( "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.17/gap/pcpgrp/torsion.gi0000644000175100001660000003740515054022512017245 0ustar runnerdocker############################################################################ ## #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. ## BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/pcpgrp/pcpattr.gi0000644000175100001660000000321315054022512017213 0ustar runnerdocker############################################################################# ## #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 PreImagesSetNC(iso, SylowSubgroup(Image(iso),p)); end ); polycyclic-2.17/gap/pcpgrp/general.gi0000644000175100001660000001220015054022512017147 0ustar runnerdocker############################################################################# ## #F ExponentRelationMatrix( pcp ) ## BindGlobal( "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. ## FIXME: the redefinition will be in the GAP 4.12 library; so at some ## point in the future, we should get rid of our code variant and just ## rely on the library version. ## 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. ## BindGlobal( "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 iso, K, F; if not IsFinite(G) then Error("Sorry - no algorithm available"); fi; # HACK: Until we write a proper native method, use that for pc groups iso := IsomorphismPcGroup(G); K := Image(iso); F := FrattiniSubgroup(K); return PreImagesSetNC(iso, F); 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.17/gap/pcpgrp/torsion.gd0000644000175100001660000000443415054022512017234 0ustar runnerdocker############################################################################ ## ## 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.17/gap/pcpgrp/fitting.gi0000644000175100001660000001666715054022512017223 0ustar runnerdocker############################################################################# ## #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], SUM_FLAGS, # Prevent generic GAP library method for finite groups being ranked higher 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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/pcpgrp/tensor.gi0000644000175100001660000004041415054022512017054 0ustar runnerdocker############################################################################# ## #F AddSystem( sys, t1, t2) ## BindGlobal( "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 ) ## BindGlobal( "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) ## BindGlobal( "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 BindGlobal( "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 BindGlobal( "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 BindGlobal( "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->PreImagesRepresentativeNC( 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 BindGlobal( "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.17/gap/action/0000755000175100001660000000000015054022512015200 5ustar runnerdockerpolycyclic-2.17/gap/action/extend.gi0000644000175100001660000001026215054022512017011 0ustar runnerdocker############################################################################# ## #W extend.gi Bettina Eick ## #W Enlarge a base pcgs by normalizing elements. ## ############################################################################# ## #F SubsWordPlus( w, gens, invs, id ) . . . . . . . .use inverses and identity ## BindGlobal( "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 SubsAndInvertDefn( w, defns ) . . . . . . . . . . . .substitute and invert ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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 ) ## BindGlobal( "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 . ## BindGlobal( "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.17/gap/action/freegens.gi0000644000175100001660000000444515054022512017326 0ustar runnerdocker############################################################################# ## #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 ## BindGlobal( "FreeGensByRelationMat", function( gens, mat ) local S, H, Q, I, pos, i; # first try to simplify mat mat := ShallowCopy( mat ); SortBy( mat, PositionNonZero ); # fill up mat if Length(mat) < Length(mat[1]) then for i in [Length(mat)+1..Length(mat[1])] do Add( mat, mat[1] * 0 ); od; fi; # solve it S := NormalFormIntMat( mat, 9 ); H := S.normal; Q := S.coltrans; I := Q^-1; pos := Filtered( [1..Length(gens)], x -> 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 ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/action/orbstab.gi0000644000175100001660000005324415054022512017165 0ustar runnerdocker############################################################################# ## #W orbstab.gi Polycyc Bettina Eick ## ## The orbit-stabilizer algorithm for elements of Z^d. ## ############################################################################# ## #F CheckStabilizer( G, S, mats, v ) ## BindGlobal( "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 ) ## BindGlobal( "CheckOrbit", function( G, g, mats, e, f ) return e * InducedByPcp( Pcp(G), g, mats ) = f; end ); ############################################################################# ## #F OrbitStabilizerTranslationAction( K, derK ) . . . . . for transl. subgroup ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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. ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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) ## BindGlobal( "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. ## BindGlobal( "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; # This is a temporary fix, see bugfix.tst # 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.17/gap/action/kernels.gi0000644000175100001660000002431715054022512017173 0ustar runnerdocker############################################################################# ## #W kernels.gi Polycyc Bettina Eick ## ############################################################################# ## #F InducedByPcp( pcpG, pcpU, actG ) ## BindGlobal( "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 ) ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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 ) ## BindGlobal( "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! ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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. ## BindGlobal( "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 := RelationLatticeOfUnits( 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.17/gap/action/basepcgs.gi0000644000175100001660000001507415054022512017317 0ustar runnerdocker############################################################################# ## #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.) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "GeneratorsBasePcgs", function( pcgs ) return pcgs.gens; end ); ############################################################################# ## #F SiftByBasePcgs( pcgs, g ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "MemberTestByBasePcgs", function( pcgs, g ) return pcgs.trivl( SiftByBasePcgs( pcgs,g ) ); end ); ############################################################################# ## #F WordByBasePcgs( pcgs, g ) ## BindGlobal( "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. ## BindGlobal( "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.17/gap/action/README0000644000175100001660000000074315054022512016064 0ustar runnerdocker 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.17/gap/action/orbnorm.gi0000644000175100001660000005377715054022512017222 0ustar runnerdocker############################################################################# ## #W orbnorm.gi Polycyc Bettina Eick ## ## The orbit-stabilizer algorithm for subgroups of Z^d. ## ############################################################################# ## #F Action function LatticeBases( base, mat ) ## BindGlobal( "OnLatticeBases", function( base, mat ) local imgs; imgs := base * mat; return NormalFormIntMat( imgs, 2 ).normal; end ); ############################################################################# ## #F CheckNormalizer( G, S, linG, U ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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. ## BindGlobal( "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 ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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. ## BindGlobal( "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. ## BindGlobal( "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? ## BindGlobal( "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) ## BindGlobal( "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? ## BindGlobal( "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) ## BindGlobal( "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. ## BindGlobal( "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.17/gap/action/dixon.gi0000644000175100001660000001237615054022512016653 0ustar runnerdocker############################################################################# ## #W dixon.gi Bettina Eick ## ## Determine Dixon's Bound for torsion free semisimple matrix groups. ## ############################################################################# ## #F PadicValue( rat, p ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/exam/0000755000175100001660000000000015054022512014655 5ustar runnerdockerpolycyclic-2.17/gap/exam/pcplib.gi0000644000175100001660000001637315054022512016461 0ustar runnerdocker############################################################################# ## #W pcplib.gi Polycyc Bettina Eick #W Werner Nickel ## ############################################################################# ## #F ExamplesOfSomePcpGroups(n) ## InstallGlobalFunction( ExamplesOfSomePcpGroups, function(n) if not IsInt(n) then return fail; fi; if n < 1 or n > 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; ## ## A nilpotent group from Eick/Fernandez paper on canonical conjugates ## if n = 13 then FTL := FromTheLeftCollector( 21 ); SetRelativeOrder( FTL, 1, 255 ); SetPower( FTL, 1, [ ] ); SetRelativeOrder( FTL, 2, 585 ); SetPower( FTL, 2, [ 3, -3 ] ); SetRelativeOrder( FTL, 7, 15 ); SetPower( FTL, 7, [ 8, 30 ] ); SetRelativeOrder( FTL, 8, 51 ); SetPower( FTL, 8, [ ] ); SetRelativeOrder( FTL, 9, 3 ); SetPower( FTL, 9, [ ] ); SetRelativeOrder( FTL, 10, 255 ); SetPower( FTL, 10, [ ] ); SetRelativeOrder( FTL, 11, 585 ); SetPower( FTL, 11, [ 12, -3 ] ); SetRelativeOrder( FTL, 13, 255 ); SetPower( FTL, 13, [ ] ); SetRelativeOrder( FTL, 14, 585 ); SetPower( FTL, 14, [ 15, -3 ] ); SetRelativeOrder( FTL, 17, 255 ); SetPower( FTL, 17, [ ] ); SetRelativeOrder( FTL, 18, 585 ); SetPower( FTL, 18, [ 19, -3 ] ); SetConjugate( FTL, 2, 1, [ 2, 1, 7, 1 ] ); SetConjugate( FTL, 2, -1, [ 2, 1, 7, 14, 8, 21 ] ); SetConjugate( FTL, 3, 1, [ 3, 1, 8, 1 ] ); SetConjugate( FTL, 3, -1, [ 3, 1, 8, 50 ] ); SetConjugate( FTL, 3, 2, [ 3, 1, 9, 1 ] ); SetConjugate( FTL, 3, -2, [ 3, 1, 9, 2 ] ); SetConjugate( FTL, 4, 1, [ 4, 1, 10, 1 ] ); SetConjugate( FTL, 4, -1, [ 4, 1, 10, 254 ] ); SetConjugate( FTL, 4, 2, [ 4, 1, 11, 1 ] ); SetConjugate( FTL, 4, -2, [ 4, 1, 11, 584, 12, 3 ] ); SetConjugate( FTL, 4, 3, [ 4, 1, 12, 1 ] ); SetConjugate( FTL, 4, -3, [ 4, 1, 12, -1 ] ); SetConjugate( FTL, 5, 1, [ 5, 1, 13, 1 ] ); SetConjugate( FTL, 5, -1, [ 5, 1, 13, 254 ] ); SetConjugate( FTL, 5, 2, [ 5, 1, 14, 1 ] ); SetConjugate( FTL, 5, -2, [ 5, 1, 14, 584, 15, 3 ] ); SetConjugate( FTL, 5, 3, [ 5, 1, 15, 1 ] ); SetConjugate( FTL, 5, -3, [ 5, 1, 15, -1 ] ); SetConjugate( FTL, 5, 4, [ 5, 1, 16, 1 ] ); SetConjugate( FTL, 5, -4, [ 5, 1, 16, -1 ] ); SetConjugate( FTL, 6, 1, [ 6, 1, 17, 1 ] ); SetConjugate( FTL, 6, -1, [ 6, 1, 17, 254 ] ); SetConjugate( FTL, 6, 2, [ 6, 1, 18, 1 ] ); SetConjugate( FTL, 6, -2, [ 6, 1, 18, 584, 19, 3 ] ); SetConjugate( FTL, 6, 3, [ 6, 1, 19, 1 ] ); SetConjugate( FTL, 6, -3, [ 6, 1, 19, -1 ] ); SetConjugate( FTL, 6, 4, [ 6, 1, 20, 1 ] ); SetConjugate( FTL, 6, -4, [ 6, 1, 20, -1 ] ); SetConjugate( FTL, 6, 5, [ 6, 1, 21, 1 ] ); SetConjugate( FTL, 6, -5, [ 6, 1, 21, -1 ] ); return PcpGroupByCollector( FTL ); fi; return fail; end ); polycyclic-2.17/gap/exam/nqlib.gi0000644000175100001660000007630715054022512016320 0ustar runnerdocker############################################################################# ## #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.17/gap/exam/bgnilp.gi0000644000175100001660000001015115054022512016447 0ustar runnerdocker############################################################################# ## #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.17/gap/exam/metacyc.gi0000644000175100001660000000410315054022512016621 0ustar runnerdocker############################################################################# ## #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.17/gap/exam/matlib.gi0000644000175100001660000003122215054022512016446 0ustar runnerdocker############################################################################# ## #W matexam.gi Polycyc BE & WN ## ## Some examples of polycyclic matrix groups -- MatExamples ## ############################################################################# ## #F MatExamples(n) ## BindGlobal( "MatExamples", function( n ) local MG; ## ## Plesken/Nebe 1 ## if n = 1 then MG := []; MG[1] := [ [ 0, -1, 0, -1, 1, -1, 0, 0, 1, 0, 1, 1, 0, 1, 0, -1 ], [ 0, 1, 1, 0, -1, 0, 0, 0, -1, 0, -1, -1, 0, 0, 0, 0 ], [ 0, 0, 1, -1, -1, 0, 0, -1, -1, 0, 0, 0, 0, 1, -1, 0 ], [ 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0 ], [ 0, 1, 1, 2, 0, 1, 0, 0, 0, -1, -1, -1, -1, -1, 1, 0 ], [ 0, 1, 0, 1, 0, 0, 0, 0, 0, -1, 0, -1, 0, -1, 1, 1 ], [ 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, -1, 0 ], [ 0, 0, 0, -1, -1, 0, 0, -1, 0, 0, 0, 1, 0, 1, -1, 0 ], [ 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1 ], [ 0, 1, 0, 0, -1, 0, -1, 0, -1, 0, -1, -1, 0, 0, 0, 0 ], [ 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ], [ 0, 0, 0, -1, -1, 0, 0, 0, -1, 0, 0, 0, 1, 1, 0, 0 ], [ 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, -1, 0, -1, 0, 0 ], [ 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0 ], [ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 1, 0, 1, 1, 0, 1, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0 ] ]; MG[2] := [ [ 0, 0, 0, 0, 0, -1, -1, -1, -1, -1, 1, 1, 0, 0, -1, 1 ], [ 0, 2, -2, 1, -1, 0, -2, 2, 0, 0, -1, -1, -1, 1, 0, 0 ], [ 0, 2, -1, 1, -1, 0, -1, 1, -1, -1, -1, -1, 0, 0, 0, 1 ], [ 0, -1, -1, -1, 0, -1, 0, 0, 0, 1, 1, 1, 1, 0, -1, 0 ], [ 0, 1, 0, 1, 1, 1, 0, 2, 1, 0, -2, -1, -2, 0, 2, -2 ], [ 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, -1, 0, 0, 1, 0, -1 ], [ -1, -1, 0, 0, 0, 0, 0, -1, 1, 0, 1, 0, 0, -1, 0, 0 ], [ -1, 1, 0, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0, -1, 0, 1 ], [ 0, 2, -1, 1, -1, 0, -1, 1, -1, -1, -1, -1, 0, 0, 1, 1 ], [ 1, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, -1, 0 ], [ 0, 0, -1, 0, 0, -1, -1, 0, 0, 0, 1, 0, 0, 0, -1, 1 ], [ 1, 1, -1, 0, -1, 0, -1, 1, -1, 0, 0, 0, 1, 1, -1, 1 ], [ 0, 1, -1, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 1, -1 ], [ -1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1, -1, 0, 1, -1 ], [ 1, -1, 0, -1, 0, 0, 0, 0, -1, 1, 1, 1, 1, 1, -2, 0 ], [ 0, 1, 0, 1, 0, 1, 0, 1, 0, -1, -1, -1, -1, 0, 1, 0 ] ]; MG[3] := [ [ -2, 1, 0, 1, -1, -1, -2, 0, 1, -1, 1, -2, -1, -1, 0, 0 ], [ 0, -2, 0, -2, 0, 1, 3, -1, 1, 1, 1, 2, 1, 1, 0, 0 ], [ 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 1, 0, 0 ], [ 1, -2, 0, -2, 2, 1, 3, -1, 0, 2, 0, 2, 0, 0, 0, -1 ], [ 1, -1, 0, -1, 1, 1, 2, 0, 0, 1, 0, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, -1, -1, 0, 0, 0, 0, 0, 1, 1, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 1, 2, 0, 1, 0, 0, 0, 0, 0, 1, 1 ], [ -1, 0, 0, 0, -1, 0, 0, 0, 1, -1, 1, 0, 0, 1, 1, 1 ], [ 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 ], [ 0, -1, 0, -1, 1, 0, 1, -1, 0, 0, 0, 1, 0, 0, 0, -1 ], [ 1, -1, 1, -1, 1, 1, 2, -1, 0, 1, -1, 1, 0, 0, 0, -1 ], [ 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 1 ], [ 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, -1, 0, 0, 0, 0, 0 ] ]; MG[4] := [ [ 0, 0, 0, 2, 0, 0, -1, 1, 0, 0, 0, -1, 0, -1, 0, 1 ], [ 0, -1, 0, -1, 1, 0, 2, -1, 2, 0, 1, 1, 0, 0, 1, 0 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0 ], [ 0, -1, 1, -2, 1, 0, 2, -2, 0, 0, 1, 1, 0, 0, 0, 0 ], [ 0, 0, 0, -1, 0, 0, 1, -1, 0, 0, 0, 1, 0, 0, 0, 0 ], [ 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 1, -1, 0 ], [ 0, 0, 1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0 ], [ 0, 0, -1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0 ], [ 0, -1, 1, -1, 1, 0, 1, -1, 0, 0, 0, 1, 0, 0, 0, -1 ], [ 0, 0, 0, -1, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, -1, 0, 0, 0, 0, 0 ], [ 0, 0, 1, -1, 0, 0, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0 ] ]; MG[5] := [ [ 0, 1, -1, 1, -1, -1, -3, 1, -1, -1, 1, -1, 0, 1, 0, 2 ], [ 1, -2, -1, -2, 1, -1, 1, 0, -1, 2, 1, 3, 2, 0, -2, -1 ], [ 1, -2, 0, -2, 0, 0, 1, -1, 0, 2, 1, 2, 2, 1, -2, -1 ], [ 0, 2, -1, 2, 0, 0, -2, 2, -1, -1, -1, -1, -1, 0, 1, 1 ], [ 0, -3, 1, -3, 1, -1, 3, -2, 1, 3, 1, 2, 1, 0, -1, -2 ], [ 1, 0, 0, -1, 0, 0, 1, 0, -1, 1, -1, 1, 1, 0, -1, -1 ], [ -2, 1, 0, 2, 0, 0, -1, 0, 1, -2, 0, -2, -2, -1, 2, 1 ], [ 0, -1, 1, 0, 0, 1, 1, -1, 1, 0, 0, 0, 0, 0, 0, 0 ], [ 1, -2, 0, -2, 0, 0, 1, -1, 0, 2, 1, 2, 1, 1, -2, -1 ], [ 1, 1, -1, 0, 0, 0, 0, 1, -1, 0, -1, 1, 1, 0, 0, 0 ], [ 0, 1, -1, 1, 0, -1, -2, 1, -1, -1, 0, 0, 0, 0, 0, 1 ], [ 2, 0, -1, -1, 0, 0, 0, 1, -2, 1, 0, 2, 2, 1, -2, 0 ], [ 1, -1, 0, -1, 0, 0, 1, 0, 0, 2, 0, 1, 1, 1, -1, -1 ], [ -1, -1, 1, 0, 1, 0, 1, -1, 1, 0, 0, 0, -1, -1, 1, -1 ], [ 1, 2, -1, 1, -1, 0, -2, 2, -2, -1, -1, 0, 1, 1, 0, 1 ], [ 0, -2, 1, -2, 0, 0, 2, -2, 1, 2, 1, 1, 1, 0, -1, -1 ] ]; MG[6] := [ [ 0, 2, -3, 1, -1, -1, -4, 3, -2, -1, 1, -1, 1, 0, 0, 2 ], [ -1, -1, 3, 1, 1, 2, 2, -1, 3, -1, 0, -2, -2, 0, 3, -1 ], [ 0, -1, 1, 1, 2, 0, 0, 1, 1, 0, 0, 0, -2, 0, 1, -1 ], [ -2, 1, -1, 1, -1, 0, -1, -1, 1, -2, 1, -2, -1, -2, 1, 2 ], [ 1, -2, 3, -2, 2, 2, 6, -2, 3, 2, -2, 1, 0, 0, 1, -4 ], [ 1, -1, 2, 0, 0, 2, 2, -1, 1, 1, -1, 0, 0, 1, 0, -1 ], [ -2, 1, -1, 0, -1, -2, -1, -1, 0, -1, 1, -1, 0, -1, 1, 2 ], [ -1, -1, 0, 0, 1, -2, -1, 0, 0, 0, 1, 1, -1, 0, 0, 0 ], [ 0, -2, 1, -1, 2, 0, 1, 0, 1, 1, 1, 1, -1, 0, 0, -2 ], [ 0, 1, 1, 2, -1, 2, 0, 0, 1, -1, -1, -2, -1, 0, 1, 1 ], [ -2, 1, 0, 2, -1, 0, -2, 0, 1, -2, 1, -3, -1, -1, 2, 2 ], [ 0, -1, 1, 1, 0, 1, -1, 0, 1, 0, 1, -1, -1, 1, 0, 0 ], [ 0, -2, 1, -1, 2, 1, 3, -1, 2, 1, 0, 1, -1, -1, 0, -2 ], [ 0, -1, 1, -2, 0, 0, 3, -2, 1, 1, 0, 1, 1, 0, 0, -1 ], [ 1, 1, -1, 1, -2, 1, -2, 1, -1, 0, 0, -1, 1, 1, -1, 2 ], [ 1, -1, 1, -1, 1, 0, 2, 0, 1, 2, -1, 1, 0, 1, 0, -2 ] ]; MG[7] := [ [ 0, 0, 1, -1, -1, -1, 0, -2, 0, 1, -1, -1, 0, 0, -1, -1 ], [ 0, 0, 0, 1, 1, 0, 0, 1, 0, -1, 0, 0, 0, 1, 2, 1 ], [ 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, -1, 1, 0 ], [ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1 ], [ 0, 0, -2, 1, 0, 1, 0, 3, 0, -1, 2, 1, 2, 1, 1, 2 ], [ 0, 0, -1, 0, 0, 0, 0, 1, 0, -1, 1, 1, 1, 1, 1, 1 ], [ 1, 0, 0, -1, 0, 0, 0, 0, -1, 1, -1, 1, 0, 1, -1, -1 ], [ 0, 0, 1, 0, 0, 0, 0, -1, 0, 1, -1, -1, -1, -1, 0, 0 ], [ 1, 1, 0, 1, 0, 1, 0, 1, -1, 0, -1, -1, 0, -1, 0, 1 ], [ -1, 0, 0, 1, 1, 0, 0, 0, 1, -2, 0, 0, -1, 0, 2, 0 ], [ -1, -1, 1, -1, 1, -1, 1, -2, 1, 0, 0, 0, -1, 0, 1, -1 ], [ 0, -1, 1, 0, 1, 0, 1, -1, 1, 0, 0, 0, -1, 0, 1, 0 ], [ 1, 1, -1, 1, 0, 1, 0, 2, -1, 0, 0, 0, 1, 0, 0, 1 ], [ 0, 1, -1, 1, 0, 1, 0, 2, 0, -1, 0, 0, 0, 0, 1, 1 ], [ -1, 0, 0, 0, 0, -1, 0, -1, 1, -1, 0, 0, -1, 0, 1, 0 ], [ 0, 0, 0, 1, 0, 1, 0, 1, 0, -1, 0, 0, 0, 0, 1, 1 ] ]; MG[8] := [ [ -1, 0, 0, -1, 0, -1, -1, -1, 0, 0, 1, 0, 0, 0, -1, -2 ], [ 0, 0, 0, 0, 0, 0, 1, -1, 1, 0, 0, -1, -1, 0, 1, 1 ], [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0 ], [ 0, 1, -1, 0, -1, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, 0, 0, 2, 0, 1, 0, 0, 0, -1, 0, 1, 2 ], [ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1 ], [ 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, -1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ], [ 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, -1, 0, 0, 0, 0, 0 ], [ 0, 0, 0, -1, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, -1 ], [ 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ], [ 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1 ], [ 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1 ], [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1 ], [ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1 ] ]; MG[9] := [ [ -1, 0, 0, 1, 0, 0, 0, 0, 1, -1, 1, 0, 0, -1, 1, 1 ], [ 1, 0, 0, 0, 0, 0, -1, 1, -2, 1, -1, 0, 1, 0, -1, -1 ], [ 0, 0, 0, 0, 0, 0, 0, 1, -1, 0, 0, 0, 1, 0, 0, 0 ], [ 0, 0, 0, 0, 1, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, -1 ], [ 2, -1, 1, -1, 0, 1, 0, 0, -2, 2, -2, 1, 0, 1, -2, -1 ], [ 1, 0, 0, 0, 0, 0, 0, 0, -1, 1, -1, 0, 0, 0, -1, 0 ], [ -1, 0, 0, -1, 0, -1, 0, -1, 1, 0, 1, 0, 0, 0, 0, -1 ], [ -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, 0, 0, 0, 0, 0 ], [ 1, -1, 0, 0, 0, 1, 0, 1, -1, 1, 0, 1, 1, 1, -1, 0 ], [ 1, 1, 0, 1, 0, 0, -1, 1, -1, 0, -1, -1, 0, 0, 0, 0 ], [ -1, 1, 0, 1, 0, -1, -1, 0, 0, -1, 0, -1, 0, -1, 1, 0 ], [ 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, -1, 0, -1, 1, 0 ], [ 1, -1, 0, -1, 0, 0, 0, 0, -1, 1, 0, 1, 1, 1, -1, 0 ], [ 1, 0, 0, -1, 0, 0, 0, 0, -1, 1, -1, 1, 0, 1, -1, -1 ], [ 0, 1, 0, 1, 0, 0, 0, 0, 0, -1, 0, -1, 0, -1, 1, 1 ], [ 1, 0, 0, 0, 0, 1, 0, 1, -1, 1, -1, 0, 0, 0, -1, 0 ] ]; return Group(MG, MG[1]^0 ); elif n = 2 then MG := []; MG[1] := [ [ 2, -1, -1, 3 ], [ -1, 0, 1, -4 ], [ 0, 1, 2, -2 ], [ -1, 1, 2, -4 ]]; MG[2] := [ [ 1, 1, 1, -2 ], [ 1, 1, -1, 1 ], [ -2, 1, 0, -3 ], [ -1, 1, 0, -2 ]]; MG[3] := [ [ 1, 0, -1, 0 ], [ -2, 5, 5, -11 ], [ -1, 0, 2, 0 ], [ -1, 1, 2, -2 ]]; MG[4] := [ [ 0, 3, 2, -7 ], [ -1, 6, 4, -13 ], [ -1, 2, 1, -5 ], [ -1, 3, 2, -7 ]]; MG[5] := [ [ 0, 4, 5, -12 ], [ 2, -4, 1, 5 ], [ 1, -4, -1, 8 ], [ 1, -3, 0, 5 ]]; MG[6] := [ [ 1, 0, -1, 0 ], [ -2, 5, 5, -11 ], [ -1, 0, 2, 0 ], [ -1, 1, 2, -2 ]]; return Group( MG, MG[1]^0 ); elif n = 3 then MG := []; MG[1] := [ [ 5, 3, 3, -5 ], [ 7, 4, 5, -8 ], [ 2, 1, 2, -2 ], [ 10, 6, 7, -11 ]]; MG[2] := [ [ -2, -4, -3, 5 ], [ 4, 7, 5, -8 ], [ -3, -3, -3, 4 ], [ 1, 2, 1, -2 ]]; MG[3] := [ [ 1, -1, 0, 1 ], [ -4, -2, -2, 5 ], [ -4, -2, -3, 3 ], [ -3, -2, -2, 4 ]]; MG[4] := [ [ 8, 8, 3, -10 ], [ -13, -14, -5, 17 ], [ -13, -14, -5, 18 ], [ -8, -9, -3, 11 ]]; MG[5] := [ [ 0, -1, -1, 2 ], [ 2, 2, 1, -1 ], [ 2, 3, 1, -5 ], [ 3, 3, 1, -3 ]]; MG[6] := [ [ 2, 1, 0, 0 ], [ 3, 2, 0, 0 ], [ -7, -8, -3, 11 ], [ -1, -3, -2, 7 ]]; return Group( MG, MG[1]^0 ); elif n = 4 then MG := [ [ [ -492568055, -715902540, -559233360, 913773168 ], [ 853152732, 1239979321, 968620464, -1582701120 ], [ 796991748, 1158354480, 904858501, -1478515764 ], [ 543797628, 790360020, 617396520, -1008810083 ] ], [ [ -348686135, -530151780, -271469520, 913773168 ], [ -603941868, -918249479, -470198736, 1582701120 ], [ -215499732, -327651600, -167777219, 564742596 ], [ -793008492, -1205711460, -617396520, 2078172517 ] ], [ [ -151935722, -225045135, -150030090, 330066198 ], [ 45009027, 58106404, 90018054, 0 ], [ 105021063, 150030090, 133121449, -165033099 ], [ -45009027, -75015045, 0, 193133485 ] ], [ [ 13, 0, -21, 0 ], [ 147, 181, 105, -231 ], [ -21, 0, 34, 0 ], [ 84, 105, 63, -134 ] ], [ [ 670415977, 715902540, 271469520, -913773168 ], [ 1161194148, 1239979321, 470198736, -1582701120 ], [ -1084755588, -1158354480, -439246739, 1478515764 ], [ 607257732, 648459180, 245895000, -827688875 ] ], [ [ 814297897, 530151780, 559233360, -913773168 ], [ -1410405012, -918249479, -968620464, 1582701120 ], [ 503263572, 327651600, 345625141, -564742596 ], [ -358046868, -233107740, -245895000, 401786125 ] ], [ [ 141577573978831, 92174617804950, 97230886083807, -158872797828558 ], [ -245219551343241, -159651121205951, -168408834761919, 275175757779087 ], [ 87499752763389, 56967046703190, 60091992355948, -98188788945456 ], [ -62251695137616, -40529202794985, -42752445237981, 69856409444878 ] ] ]; return Group( MG, MG[1]^0 ); fi; return fail; end ); polycyclic-2.17/gap/exam/generic.gi0000644000175100001660000001376115054022512016622 0ustar runnerdocker############################################################################# ## #W generic.gi Polycyc Bettina Eick ## ############################################################################# ## #M AbelianPcpGroup ## InstallGlobalFunction( AbelianPcpGroup, function( arg ) local r, n; # catch arguments if Length(arg) = 1 and IsInt(arg[1]) then r:= ListWithIdenticalEntries(arg[1], 0); elif Length(arg) = 1 and IsList(arg[1]) then r:= arg[1]; elif Length(arg) = 2 then n:= arg[1]; r:= arg[2]; if n < Length(r) then r:= r{[1..n]}; elif Length(r) < n then r:= Concatenation(r, ListWithIdenticalEntries(n-Length(r), 0)); fi; fi; # construct group return AbelianGroupCons(IsPcpGroup, r); end ); ############################################################################# ## #M DihedralPcpGroup ## InstallGlobalFunction( DihedralPcpGroup, function( n ) if n = 0 then n:= infinity; fi; return DihedralGroupCons( IsPcpGroup, n ); 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 -> PreImagesRepresentativeNC(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) ## BindGlobal( "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) ## BindGlobal( "BlowUpPcpPGroup", function(G) local p, e, f, c, i, j, k; # set up p := PrimePGroup(G); e := ShallowCopy(AsList(G)); SortBy(e, a -> PDepth(G, a)); # fill up collector c := FromTheLeftCollector(Length(e)-1); for i in [1..Length(e)-1] do SetRelativeOrder(c,i,p); # power j := Position(e, e[i]^p); if j < Length(e) then SetPower(c,i,[j,1]); fi; # commutators for k in [1..i-1] do j := Position(e, Comm(e[i], e[k])); if j < Length(e) then SetCommutator(c,i,k,[j,1]); fi; od; od; return PcpGroupByCollector(c); end ); polycyclic-2.17/gap/exam/README0000644000175100001660000000043215054022512015534 0ustar runnerdocker gap/exam: Some example lists of pcp groups: allexam.gi -- PcpExamples eddie.gi -- EddiesExamples nqexam.gi -- NqExamples matexam.gi -- MatExamples metagrp.gi -- construct metabelian group metacyc.gi -- construct infinite metacyclic groups polycyclic-2.17/gap/exam/exam.gd0000644000175100001660000000112315054022512016120 0ustar runnerdocker DeclareGlobalFunction( "AbelianPcpGroup" ); DeclareGlobalFunction( "DihedralPcpGroup" ); DeclareGlobalFunction( "HeisenbergPcpGroup" ); DeclareGlobalFunction( "UnitriangularPcpGroup" ); DeclareGlobalFunction( "MaximalOrderByUnitsPcpGroup"); DeclareGlobalFunction( "SubgroupUnitriangularPcpGroup" ); DeclareGlobalFunction( "BurdeGrunewaldPcpGroup" ); DeclareGlobalFunction( "InfiniteMetacyclicPcpGroup" ); DeclareGlobalFunction( "ExampleOfMetabelianPcpGroup" ); DeclareGlobalFunction( "ExamplesOfSomePcpGroups" ); DeclareGlobalFunction( "PcpExamples"); DeclareGlobalFunction( "NqExamples"); polycyclic-2.17/gap/exam/metagrp.gi0000644000175100001660000000463115054022512016641 0ustar runnerdocker############################################################################# ## #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.17/gap/obsolete.gd0000644000175100001660000000537215054022512016062 0ustar runnerdocker############################################################################ ## ## 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.17/gap/cover/0000755000175100001660000000000015054022512015041 5ustar runnerdockerpolycyclic-2.17/gap/cover/const/0000755000175100001660000000000015054022512016167 5ustar runnerdockerpolycyclic-2.17/gap/cover/const/com.gi0000644000175100001660000000505615054022512017274 0ustar runnerdockerBindGlobal( "NrToElm", 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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "GetExponents", function(pcp, x) return ExponentsByPcp(pcp, MappedVector(x,pcp)); end ); BindGlobal( "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 ); BindGlobal( "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.17/gap/cover/const/aut.gi0000644000175100001660000001161615054022512017306 0ustar runnerdockerBindGlobal( "AddPermOper", 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 ); BindGlobal( "ReduceAuto", function( auto, C, isom, gens, imgs ) local news; news := List(imgs, x -> Image(auto, x)); news := List(news, x -> PreImagesRepresentativeNC(isom, x)); news := GroupHomomorphismByImagesNC( C, C, gens, news ); SetIsBijective( news, true ); return news; end ); BindGlobal( "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 ); BindGlobal( "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.17/gap/cover/const/cov.gi0000644000175100001660000000435715054022512017310 0ustar runnerdocker## ## Covers ## BindGlobal( "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 Info(InfoPcpGrp, 2, " Schur Mult has type ",AbelianInvariants(M)); # 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 Info(InfoPcpGrp, 2, " Schur Mult new type ",AbelianInvariants(T)); # 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.17/gap/cover/const/bas.gi0000644000175100001660000000317215054022512017260 0ustar runnerdocker BindGlobal( "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 ); BindGlobal( "IdPcpGroup", function(G) return IdGroup(PcpGroupToPcGroup(RefinedPcpGroup(G))); end ); BindGlobal( "ExtensionModule", function(K,H) return SubgroupByIgs(K,Igs(K){[Length(Igs(H))+1..Length(Igs(K))]}); end ); BindGlobal( "ReduceMod", function(vec, rels) return List([1..Length(vec)], i -> vec[i] mod rels[i]); end ); BindGlobal( "ProductPcpGroups", function(G, U, V) return Subgroup(G, Concatenation(Igs(U), Igs(V))); end ); BindGlobal( "ExponentAbelianPcpGroup", function( G ) return Maximum(RelativeOrdersOfPcp(Pcp(G,"snf"))); end ); BindGlobal( "OmegaAbelianPcpGroup", function(G, e) return Subgroup(G, List(Igs(G), x -> x^e)); end ); BindGlobal( "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 ); BindGlobal( "AddMOrder", function(G) if IsBound(G!.mord) then return; fi; AddSExtension(G); end ); BindGlobal( "CoverCode", function(G) if IsList(G) then return G; fi; AddMOrder(G); return [Size(G), G!.mord, CodePcGroup(PcpGroupToPcGroup(RefinedPcpGroup(G)))]; end ); BindGlobal( "CodeCover", function(c) local G; G := PcGroupCode(c[3], c[1]); G := PcGroupToPcpGroup(G); G!.mord := c[2]; return G; end ); polycyclic-2.17/gap/cover/const/ord.gi0000644000175100001660000000273315054022512017301 0ustar runnerdocker ## ## List iterated Schur covers of order p^n and rank d and print to file. ## BindGlobal( "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.17/gap/cover/const/ccc.gi0000644000175100001660000000400315054022512017235 0ustar runnerdocker ## ## List iterated Schur covers of coclass r and rank d. ## BindGlobal( "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.17/gap/cover/const/orb.gi0000644000175100001660000000543215054022512017276 0ustar runnerdockerBindGlobal( "AgOrbitCover", 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 ); BindGlobal( "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 ); BindGlobal( "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.17/gap/cover/trees/0000755000175100001660000000000015054022512016163 5ustar runnerdockerpolycyclic-2.17/gap/cover/trees/xtree.gi0000644000175100001660000001740515054022512017642 0ustar runnerdockerWIDTH := 25; DEPTH := 40; SSIZE := [1000, 700]; BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "CoclassPGroup", function(G) local n,c; n := Length(Factors(Size(G))); c := Length(LowerCentralSeriesOfGroup(G))-1; return n-c; end ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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.17/gap/README0000644000175100001660000000122415054022512014602 0ustar runnerdocker 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.17/gap/matrep/0000755000175100001660000000000015054022512015213 5ustar runnerdockerpolycyclic-2.17/gap/matrep/matrep.gi0000644000175100001660000005367515054022512017044 0ustar runnerdocker############################################################################# ## #W matrep.gi Polycyclic Werner Nickel ## 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( 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 ########################################################## ## BindGlobal( "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 ); BindGlobal( "RepresentationForPcpCollector", 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 ################################################### 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( RepresentationForPcpCollector( 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 ); polycyclic-2.17/gap/matrep/unitri.gd0000644000175100001660000000167715054022512017054 0ustar runnerdocker############################################################################# ## #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.17/gap/matrep/README0000644000175100001660000000030415054022512016070 0ustar runnerdocker 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.17/gap/matrep/unitri.gi0000644000175100001660000003177315054022512017061 0ustar runnerdocker############################################################################# ## #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.17/gap/matrep/matrep.gd0000644000175100001660000000103315054022512017014 0ustar runnerdocker############################################################################# ## ## 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.17/gap/matrep/affine.gi0000644000175100001660000000406415054022512016770 0ustar runnerdocker BindGlobal( "AdjustPresentation", function( G ) G := G / TorsionSubgroup(G); return PcpGroupBySeries( UpperCentralSeriesOfGroup(G), "snf" ); end ); BindGlobal( "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; MakeReadOnlyGlobal( "NextStepRepresentation" ); BindGlobal( "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.17/gap/basic/0000755000175100001660000000000015054022512015004 5ustar runnerdockerpolycyclic-2.17/gap/basic/pcpsers.gi0000644000175100001660000003576515054022512017024 0ustar runnerdocker############################################################################# ## #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) ## BindGlobal( "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 ) ## BindGlobal( "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.17/gap/basic/grphoms.gd0000644000175100001660000000146515054022512017005 0ustar runnerdocker############################################################################# ## #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.17/gap/basic/colftl.gi0000644000175100001660000001661615054022512016622 0ustar runnerdockerBindGlobal( "CollectPolycyclicGap", 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 ); BindGlobal( "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.17/gap/basic/collect.gd0000644000175100001660000001132615054022512016750 0ustar runnerdocker############################################################################# ## #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 and IsPositionalObjectRep, [] ); 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.17/gap/basic/pcppcps.gd0000644000175100001660000000317615054022512016777 0ustar runnerdocker############################################################################# ## #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.17/gap/basic/pcpgrps.gi0000644000175100001660000003677015054022512017020 0ustar runnerdocker############################################################################# ## #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); if Length( pcp ) = 0 then return One( G ); fi; 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 ## BindGlobal( "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 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 ## BindGlobal( "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.17/gap/basic/collect.gi0000644000175100001660000010537115054022512016761 0ustar runnerdocker############################################################################# ## #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 ] := []; 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.17/gap/basic/pcppcps.gi0000644000175100001660000006570615054022512017013 0ustar runnerdocker############################################################################# ## #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 ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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..n] 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); BindGlobal( "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. ## BindGlobal( "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. ## BindGlobal( "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 ) ## BindGlobal( "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; ## BindGlobal( "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 not IsPcpGroup(U) then Error(" must be a pcp group"); fi; 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 ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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 ## BindGlobal( "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 ) ## BindGlobal( "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 ## BindGlobal( "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 ); BindGlobal( "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.17/gap/basic/pcpfact.gi0000644000175100001660000000465015054022512016752 0ustar runnerdocker############################################################################# ## #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.17/gap/basic/orbstab.gi0000644000175100001660000001711615054022512016767 0ustar runnerdocker############################################################################# ## #W orbstab.gi Polycyc Bettina Eick #W Werner Nickel ## ############################################################################# ## #F TransversalInverse( j, trels ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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! ## BindGlobal( "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! ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "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 ) ## BindGlobal( "RandomNormalizerPcpGroup", function( G, N ) local gens, stab; gens := Igs(G); stab := RandomPcpOrbitStabilizer( N, gens, gens, OnPoints); return Subgroup( G, stab.stab ); end ); polycyclic-2.17/gap/basic/convert.gi0000644000175100001660000003547315054022512017021 0ustar runnerdocker############################################################################# ## #W convert.gi Polycyc Bettina Eick ## Werner Nickel ############################################################################# ## ## Convert finite pcp groups to pc groups. ## BindGlobal( "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. ## BindGlobal( "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. ## BindGlobal( "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 -> PreImagesRepresentativeNC( 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. ## BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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 ); BindGlobal( "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.17/gap/basic/colrec.gi0000644000175100001660000000524415054022512016601 0ustar runnerdocker############################################################################# ## #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.17/gap/basic/construct.gi0000644000175100001660000001600615054022512017354 0ustar runnerdocker############################################################################# ## #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, gens, pos; if not ForAll( ints, x -> IsInt(x) or IsInfinity(x) ) 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; r := Filtered( ints, x -> x <> 1 ); n := Length(r); # construct group coll := FromTheLeftCollector( n ); for i in [1..n] do if IsBound( r[i] ) and r[i] > 0 and r[i] <> infinity then SetRelativeOrder( coll, i, r[i] ); fi; od; UpdatePolycyclicCollector(coll); grp := PcpGroupByCollectorNC( coll ); if 1 in ints then gens:= []; pos:= 1; for i in [ 1 .. Length( ints ) ] do if ints[i] = 1 then gens[i]:= One( grp ); else gens[i]:= GeneratorsOfGroup( grp )[ pos ]; pos:= pos + 1; fi; od; grp:= GroupWithGenerators( gens ); fi; 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.17/gap/basic/README0000644000175100001660000000224415054022512015666 0ustar runnerdocker 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.17/gap/basic/pcpelms.gd0000644000175100001660000000373315054022512016771 0ustar runnerdocker############################################################################# ## #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.17/gap/basic/infos.gd0000644000175100001660000000075015054022512016440 0ustar runnerdocker############################################################################# ## #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.17/gap/basic/pcpexpo.gi0000644000175100001660000001261715054022512017012 0ustar runnerdocker############################################################################# ## #W pcpexpo.gi Polycyc Bettina Eick ## ############################################################################# ## #F ReducingCoefficient( , ) . . . . . . . . .f with g * h^-f = 1 mod r #F ReducingCoefficient( , , ) . . . . . . . . . . . . . . a/b mod r ## BindGlobal( "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( pcs, 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 ) ## BindGlobal( "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.17/gap/basic/grphoms.gi0000644000175100001660000002762415054022512017017 0ustar runnerdocker############################################################################# ## #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]; if Length(gens) = 0 then return true; fi; # 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 if Length(gens) = 0 then return C; fi; # 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( PreImagesRepresentativeNC, "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( PreImagesSetNC, "for PcpGHBI", CollFamRangeEqFamElms, [ IsFromPcpGHBI and IsToPcpGHBI, IsPcpGroup ], function( hom, U ) local prei, gens, kern; prei := List( Igs(U), x -> PreImagesRepresentativeNC(hom,x) ); if fail in prei then gens := GeneratorsOfGroup( Intersection( ImagesSource(hom), U ) ); prei := List( gens, x -> PreImagesRepresentativeNC(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.17/gap/basic/pcppara.gi0000644000175100001660000001262515054022512016761 0ustar runnerdocker############################################################################# ## #W pcppara.gi Polycyc Bettina Eick #W Werner Nickel ## ## Parallel versions of the non-commuatative gauss algorithm. ## ############################################################################# ## #F UpdateCounterPara( ind, c ) . . . . . . . . . . . . small help function ## BindGlobal( "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.17/gap/basic/colsave.gi0000644000175100001660000001160415054022512016763 0ustar  runnerdocker#############################################################################
##
#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
##
BindGlobal( "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.17/gap/basic/coldt.gi0000644000175100001660000000263315054022512016436 0ustar  runnerdocker
##
#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.17/gap/basic/pcpgrps.gd0000644000175100001660000000206515054022512017001 0ustar  runnerdocker#############################################################################
##
#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.17/gap/basic/colcom.gi0000644000175100001660000004766515054022512016623 0ustar  runnerdocker
##  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.
BindGlobal( "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,
));


BindGlobal( "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 );

BindGlobal( "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 );


BindGlobal( "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.17/gap/basic/pcpelms.gi0000644000175100001660000004003515054022512016772 0ustar  runnerdocker#############################################################################
##
#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.
BindGlobal( "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;
##
BindGlobal( "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.
BindGlobal( "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.
##
BindGlobal( "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 )
##
BindGlobal( "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( InverseMutable,
               "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.17/gap/basic/basic.gd0000644000175100001660000000363615054022512016411 0ustar  runnerdocker#############################################################################
##
#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.17/gap/basic/chngpcp.gi0000644000175100001660000002447415054022512016762 0ustar  runnerdocker############################################################################
##
#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!
##
BindGlobal( "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.
##
BindGlobal( "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 )
##
BindGlobal( "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.
##
BindGlobal( "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.
##
BindGlobal( "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.
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "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.17/gap/matrix/0000755000175100001660000000000015054022512015227 5ustar  runnerdockerpolycyclic-2.17/gap/matrix/rowbases.gi0000644000175100001660000001634315054022512017404 0ustar  runnerdocker#############################################################################
##
#W  rowbases.gi                                                  Bettina Eick
##
##  Methods to compute with rational vector spaces.
##

#############################################################################
##
#F  VectorspaceBasis( gens )
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "ProjectionByNHSEB", function( vec, hom )
    return CoefficientsByNHSEB( vec, hom ).coeff2;
end );

#############################################################################
##
#F  ImageByNHSEB( vec, hom )
##
BindGlobal( "ImageByNHSEB", function( vec, hom )
    return CoefficientsByNHSEB( vec, hom ).coeff1;
end );

#############################################################################
##
#F  PreimagesRepresentativeByNHSEB( vec, hom )
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "IsSemiEchelonBase", function( base )
    return IsSSortedList( List( base, PositionNonZero ) );
end );

#############################################################################
##
#F  IsEchelonBase( base )
##
BindGlobal( "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.17/gap/matrix/triangle.gi0000644000175100001660000001612515054022512017362 0ustar  runnerdocker#############################################################################
##
#W  triangle.gi                 Polycyclic                      Werner Nickel
##

#############################################################################
##
#F  PreImageSubspaceIntMat( ,  )
##
##  Find all v such that v *  in .
##
BindGlobal( "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
##  .
##
BindGlobal( "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.
##
BindGlobal( "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.
##
BindGlobal( "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.
##
BindGlobal( "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.
##
BindGlobal( "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.
##
BindGlobal( "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.
##
BindGlobal( "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.17/gap/matrix/matrix.gd0000644000175100001660000000113015054022512017042 0ustar  runnerdockerDeclareGlobalFunction("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.17/gap/matrix/README0000644000175100001660000000050615054022512016110 0ustar  runnerdocker
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.17/gap/matrix/hnf.gi0000644000175100001660000002452515054022512016333 0ustar  runnerdocker
#############################################################################
##
#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.
##
BindGlobal( "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 );

BindGlobal( "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 );

BindGlobal( "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 );

BindGlobal( "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
##
BindGlobal( "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
##
BindGlobal( "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
##
BindGlobal( "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'
##
BindGlobal( "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.
##
BindGlobal( "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.
##
BindGlobal( "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.17/gap/matrix/modules.gi0000644000175100001660000002520115054022512017220 0ustar  runnerdocker#############################################################################
##
#W  modules.gi                   Polycyc                         Bettina Eick
##

#############################################################################
##
#F RadicalSeriesOfFiniteModule( mats, d, f )
##
BindGlobal( "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
##
BindGlobal( "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 )
##
BindGlobal( "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
##
BindGlobal( "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
##
BindGlobal( "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 )
##
BindGlobal( "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;
MakeReadOnlyGlobal( "HomogeneousSeriesOfCongruenceModule" );

#############################################################################
##
#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;
MakeReadOnlyGlobal( "HomogeneousSeriesOfRationalModule" );

#############################################################################
##
#F RefineSplitting( mats, subs ) . . . . . . . . . . for congruence subgroups
##
BindGlobal( "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.17/gap/matrix/lattices.gi0000644000175100001660000001423015054022512017360 0ustar  runnerdocker#############################################################################
##
#W  lattices.gi                 Polycyclic                       Bettina Eick
##
##  Methods to compute with integral lattices.
##

#############################################################################
##
#F  InducedByField( mats, f )
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "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
##
BindGlobal( "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.17/gap/matrix/latbases.gi0000644000175100001660000001600515054022512017350 0ustar  runnerdocker#############################################################################
##
#W  latbases.gi                                                  Bettina Eick
##
##  Methods to compute with lattice bases.
##

#############################################################################
##
#F  LatticeBasis( gens )
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "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
##
BindGlobal( "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
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "ProjectionByNHLB", function( vec, hom )
    return CoefficientsByNHLB( vec, hom ).kcoeff;
end );

#############################################################################
##
#F  ImageByNHLB( vec, hom )
##
BindGlobal( "ImageByNHLB", function( vec, hom )
    return CoefficientsByNHLB( vec, hom ).fcoeff;
end );

#############################################################################
##
#F  ImageOfNHLB( hom )
##
BindGlobal( "ImageOfNHLB", function( hom )
    return hom.cyc.base;
end );

#############################################################################
##
#F  KernelOfNHLB( hom )
##
BindGlobal( "KernelOfNHLB", function( hom )
    return hom.fac.kern;
end );

#############################################################################
##
#F  PreimagesBasisOfNHLB( hom )
##
BindGlobal( "PreimagesBasisOfNHLB", function( hom )
    return hom.cyc.gens;
end );

#############################################################################
##
#F  PreimagesRepresentativeByNHLB( vec, hom )
##
BindGlobal( "PreimagesRepresentativeByNHLB", function( vec, hom )
    return vec * hom.cyc.gens;
end );

#############################################################################
##
#F  PreimageByNHLB( base, hom )
##
BindGlobal( "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 )
##
BindGlobal( "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 )
##
BindGlobal( "InducedActionFactorByNHLB", function( mat, hom )
    return List( hom.cyc.gens, x -> CoefficientsByNHLB(x*mat, hom).fcoeff );
end );

#############################################################################
##
#F  InducedActionSubspaceByNHLB( mat, hom )
##
BindGlobal( "InducedActionSubspaceByNHLB", function( mat, hom )
    return List( hom.fac.kern, x -> CoefficientsByNHLB(x*mat, hom).kcoeff );
end );

polycyclic-2.17/init.g0000644000175100001660000000343715054022512014276 0ustar  runnerdocker#############################################################################
##
#W    init.g            GAP 4 package 'polycyclic'               Bettina Eick
#W                                                              Werner Nickel
#W                                                                   Max Horn
##

#############################################################################
##I introduce the NC versions of PreImages...
if not IsBound( PreImagesNC ) then
    BindGlobal( "PreImagesNC", PreImages );
fi;
if not IsBound( PreImagesSetNC ) then
    BindGlobal( "PreImagesSetNC", PreImagesSet );
fi;
if not IsBound( PreImagesRepresentativeNC ) then
    BindGlobal( "PreImagesRepresentativeNC", PreImagesRepresentative );
fi;

#############################################################################
##
#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.17/tst/0000755000175100001660000000000015054022512013766 5ustar  runnerdockerpolycyclic-2.17/tst/semidirect.tst0000644000175100001660000000125415054022512016654 0ustar  runnerdockergap> START_TEST("Test of semidirect products");

#
gap> N := DihedralPcpGroup( 0 );;
gap> A := Group([
>    InnerAutomorphism( N, N.1 ),
>    InnerAutomorphism( N, N.2 ),
>    GroupHomomorphismByImagesNC( N, N, [ N.1, N.2 ], [ N.1*N.2, N.2^-1 ] )
> ]);;
gap> G := AbelianPcpGroup( [ 0 ] );;
gap> alpha := GroupHomomorphismByImagesNC( G, A, [ G.1 ], [ A.1*A.3 ] );;
gap> S := SemidirectProduct( G, alpha, N );
Pcp-group with orders [ 0, 2, 0 ]
gap> e1 := Embedding( S, 1 );;
gap> e2 := Embedding( S, 2 );;
gap> p := Projection( S );;
gap> e1 * p = IdentityMapping( G );
true
gap> ClosureGroup( Image( e1, G ), Image( e2, N ) ) = S;
true

#
gap> STOP_TEST( "semidirect.tst", 10000000);
polycyclic-2.17/tst/exam/0000755000175100001660000000000015054022512014720 5ustar  runnerdockerpolycyclic-2.17/tst/exam/generic.tst0000644000175100001660000000210315054022512017064 0ustar  runnerdocker#
# 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);;

#
gap> IdGroup(BlowUpPcpPGroup(SmallGroup(2,1)));
[ 2, 1 ]
gap> IdGroup(BlowUpPcpPGroup(SmallGroup(4,1)));
[ 8, 2 ]
gap> IdGroup(BlowUpPcpPGroup(SmallGroup(4,2)));
[ 8, 5 ]
gap> List([1..5], i->IdGroup(BlowUpPcpPGroup(SmallGroup(8,i))));
[ [ 128, 1601 ], [ 128, 2319 ], [ 128, 2320 ], [ 128, 2321 ], [ 128, 2328 ] ]
gap> IdGroup(BlowUpPcpPGroup(SmallGroup(3,1)));
[ 9, 2 ]
gap> BlowUpPcpPGroup(SmallGroup(9,1));
Pcp-group with orders [ 3, 3, 3, 3, 3, 3, 3, 3 ]
gap> BlowUpPcpPGroup(SmallGroup(9,2));
Pcp-group with orders [ 3, 3, 3, 3, 3, 3, 3, 3 ]
polycyclic-2.17/tst/factor.tst0000644000175100001660000000133515054022512016002 0ustar  runnerdockergap> 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.17/tst/inters.tst0000644000175100001660000001102015054022512016020 0ustar  runnerdockergap> 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 := PreImagesNC(iso,H1);
Group([ f2, f3*f4, f6 ])
gap> G2 := PreImagesNC(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 := PreImagesNC(iso,H1);
Group([ f2, f3*f4, f6^2 ])
gap> G2 := PreImagesNC(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.17/tst/homs.tst0000644000175100001660000002037215054022512015474 0ustar  runnerdockergap> 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(PreImagesSetNC(map,G));
2
gap> List([IsTotal,IsSingleValued,IsSurjective,IsInjective], f->f(map));
[ false, true, false, true ]
gap> map2:=map*map;;
gap> Size(PreImagesSetNC(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.17/tst/AddToIgs.tst0000644000175100001660000001452415054022512016166 0ustar  runnerdockergap> 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 ]

#
# Fix a bug in AddToIgs
# See https://github.com/gap-packages/polycyclic/issues/66
#
gap> G := PcGroupToPcpGroup( SmallGroup( 36, 9 ) );;
gap> gensG := [ G.1, G.4 ];;
gap> G = Subgroup( G, gensG );
true

# second example for issue #66
gap> G := ExamplesOfSomePcpGroups( 10 );;
gap> S := Subgroup( G, [ G.1, G.2, G.4 ] );;
gap> Igs(S);
[ g1, g2, g3^3, g4 ]
gap> G.3^3 in S;
true
gap> G.2^-1*G.4*G.2*G.4^-2;
g3^3

#
# third example for issue #66
#
gap> H := SmallGroup( 36, 9 );;
gap> gensH := [ H.1, H.4 ];;
gap> H = Subgroup( H, gensH );
true
gap> iso := IsomorphismPcpGroup( H );;
gap> G := Range( iso );;
gap> gensG := List( gensH, h -> h^iso );;
gap> S := Subgroup( G, gensG );;
gap> Igs( S );
[ g1, g2, g3*g4^2, g4 ]
gap> G = S;
true

# another example, this time from issue #56
gap> A:=AbelianPcpGroup([3,2,12]);;
gap> m:=MinimalGeneratingSet(A);;
gap> Length(m);
2
gap> List(AddToIgs([],m), Depth);
[ 1, 2, 3 ]

#
gap> STOP_TEST( "AddToIgs.tst", 1);
polycyclic-2.17/tst/bugfix.tst0000644000175100001660000005541115054022512016014 0ustar  runnerdockergap> 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. Experiment with that some more
gap> G := Parent(G);; # does nothing in GAP >= 4.13 -- possibly due to https://github.com/gap-system/gap/pull/5631
gap> igs := [ G.1, G.2*G.5, G.3*G.4*G.5^2, G.4*G.5, G.5 ];;
gap> G := Subgroup(G, igs);;
gap> SetIgs(G, igs);
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 ] ]

#
# 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, PreImagesNC(iso, U) )) = N;
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 ] ]

#
# bug in StabilizerIntegralAction
# 
#
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 AddToIgs: in infinite pcp groups, we must also take inverses of
# generators into account.
# 
#
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

#
# bug in IsConjugate: it should return a boolean, but instead of 'true' it
# returned a conjugating element.
# 
#
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
gap> DihedralPcpGroup( 2 );;  # used to run into an error

#
# bug in OrbitIntegralAction
# 
#
gap> G:=ExamplesOfSomePcpGroups(8);
Pcp-group with orders [ 0, 0, 0, 0, 0 ]
gap> mats:=RepresentationForPcpCollector(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

#
# 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> PreImagesRepresentativeNC( phi, One(G) );
id
gap> PreImagesRepresentativeNC( 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> PreImagesRepresentativeNC( phi, One(G) );
id
gap> PreImagesRepresentativeNC( phi, G.2 );
g2
gap> PreImagesRepresentativeNC( 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

#
# Fix a bug in NormalizerOfComplement
# 
# 
#
gap> C := AbelianPcpGroup( [ 2, 3 ] );;
gap> H := Subgroup( C, [ C.1 ] );;
gap> N := Subgroup( C, [ C.2 ] );;
gap> I := NormalIntersection( N, H );;
gap> NormalizerOfComplement( C, H, N, I );
Pcp-group with orders [ 2, 3 ]

# second test case for issue #45 / #88
gap> G:= AbelianPcpGroup( 2, [ 2, 3 ] );;
gap> Length( ConjugacyClassesSubgroups( G ) ) = 4;
true

#
# Fix a bug in IsNormal
# 
#
gap> g := PcGroupToPcpGroup(SmallGroup(48,1));
Pcp-group with orders [ 2, 2, 2, 2, 3 ]
gap> S := SylowSubgroup( g, 2 );
Pcp-group with orders [ 2, 2, 2, 2 ]
gap> T := S^g.5;
Pcp-group with orders [ 2, 2, 2, 2 ]
gap> IsNormal( S, T );
false
gap> IsNormal( T, S );
false

#
# PreImages resp. PreImagesSet used to run into a "method not found"
# error when the input set is not contained in the image of the map.
# 
#
gap> G := AbelianPcpGroup([0]);
Pcp-group with orders [ 0 ]
gap> phi := GroupHomomorphismByImages(G,G,[G.1],[One(G)]);
[ g1 ] -> [ id ]
gap> H := PreImagesSet(phi, G);
Pcp-group with orders [ 0 ]
gap> G = H;
true

#
# TorsionSubgroup returned a wrong results for abelian groups
# (was not in a released version)
# 
#
gap> TorsionSubgroup(AbelianPcpGroup([3,2,0,0]));
Pcp-group with orders [ 3, 2 ]
gap> TorsionSubgroup(AbelianPcpGroup([2,3,0,0]));
Pcp-group with orders [ 2, 3 ]

#
# Wrong result for intersection of subgroups of an abelian group
# (was not in a released version)
# 
#
gap> G := AbelianPcpGroup([4,2]);;
gap> M := Group(G.1);;
gap> N := Group(G.1*G.2);;
gap> G.1^2 in N;
true
gap> G.1^2 in M;
true
gap> G.1^2 in Intersection(N,M);
true

#
# Fix a bug in ConjugacyElementsBySeries
# 
#
gap> G := ExamplesOfSomePcpGroups( 10 );;
gap> g := G.1;;
gap> h := g^(G.2*G.3);;
gap> k := ConjugacyElementsBySeries( G, g, h, PcpsOfEfaSeries( G ) );;
gap> g^k = h;
true

#
# Fix a bug causing Random to fail for the trivial group
# 
#
gap> Random( TrivialGroup( IsPcpGroup ) );
id

#
# Allow Centralizer to fall back on generic GAP methods
# 
#
gap> G := PcGroupToPcpGroup( SmallGroup( 16, 11 ) );;
gap> g := G.1*G.3*G.4;;
gap> H := Subgroup( G,[ G.2, G.3, G.4 ] );;
gap> Centralizer( H, g );
Pcp-group with orders [ 2, 2 ]

#
# Fix a bug in CentralizerBySeries
# 
#
gap> G := PcGroupToPcpGroup( SmallGroup( 16, 11 ) );;
gap> g := G.2*G.3*G.4;;
gap> cc := ConjugacyClass( G, g );;
gap> C := Centralizer( cc );
Pcp-group with orders [ 2, 2, 2 ]
gap> Igs( C );
[ g2, g3, g4 ]

#
# Fix bug with IsSingleValued / CoKernelOfMultiplicativeGeneralMapping
# for certain trivial maps, which used to raise an error in the example
# below, because MappedVector was called with an empty list of generators.
#
gap> G:=TrivialGroup(IsPcpGroup);;
gap> H:=AbelianGroup(IsPcpGroup,[0]);;
gap> GroupHomomorphismByImages(G, H, [One(G)], [One(H)]);
[ id ] -> [ id ]

#
# Fix a bug in the AbelianGroupCons method for IsPcpGroup.
# (Generators of order 1 are in principle supported,
# but we got an error when all generators had order 1,
# and the group was corrupted when some but not all generators had order 1.)
#
gap> AbelianGroup( IsPcpGroup, [ 1 ] );
Pcp-group with orders [  ]
gap> g:= AbelianGroup( IsPcpGroup, [ 1, 2 ] );
Pcp-group with orders [ 2 ]
gap> List( GeneratorsOfGroup( g ), Order );
[ 1, 2 ]
gap> AbelianPcpGroup( 1 );
Pcp-group with orders [ 0 ]
gap> AbelianPcpGroup( [ 1 ] );
Pcp-group with orders [  ]
gap> AbelianPcpGroup( 1, [ 1 ] );
Pcp-group with orders [  ]
gap> AbelianPcpGroup( 2 );
Pcp-group with orders [ 0, 0 ]
gap> AbelianPcpGroup( [ 2, 3 ] );
Pcp-group with orders [ 2, 3 ]
gap> AbelianPcpGroup( 2, [ 2, 3 ] );
Pcp-group with orders [ 2, 3 ]
gap> AbelianPcpGroup( 2, [ 2, 3, 4 ] );
Pcp-group with orders [ 2, 3 ]
gap> AbelianPcpGroup( 2, [ 2 ] );
Pcp-group with orders [ 2, 0 ]

#
# Fix bug in FrattiniSubgroup
# Reported by Heiko Dietrich (2024-02-19)
#
gap> G:=PcGroupToPcpGroup(SmallGroup(11025,6));;
gap> F:=FrattiniSubgroup(G);;
gap> Size(F);  # used to produce a group of order 49
21

#
# Fixed a bug in IsConjugate for a finite pcp-group
# 
#
gap> G := PcGroupToPcpGroup( SmallGroup( 1600, 10260 ) );;
gap> G := Subgroup( G, [ G.1, G.2, G.3, G.4 ] );;
gap> g := G.2*G.4;; h := g^(G.1*G.3);;
gap> IsConjugate( G, g, h );
true

#
# Fixed a bug in stabilizer integral action
# 
#
gap> G := ExamplesOfSomePcpGroups( 10 );;
gap> g := G.1^2*G.3^3*G.4^-3;;
gap> h := g^(G.1*G.2^2);;
gap> pcps := PcpsOfEfaSeries( G );;
gap> k := ConjugacyElementsBySeries( G, g, h, pcps );
g1*g2^2
gap> g^k = h;
true

#
# Fixed a bug in OrbitIntegralAction
# This fix bug has a temporary solution by commenting the code in
# gap/action/orbstab.gi lines 592-594
#
gap> ftl := FromTheLeftCollector( 2 );;
gap> SetRelativeOrder( ftl, 2, 2 );
gap> G := PcpGroupByCollector( ftl );;
gap> A := [ [ 1, 1, 0, 0], [ 0, 1, 0 , 0], [ 0, 0, 1, 0], [ 0, 0, 0, 1] ];;
gap> B := DiagonalMat( [-1, -1, -1, -1] );;
gap> OrbitIntegralAction( G, [A,B], [1,0,0,0], [-1,0,0,0] );
rec( prei := g2, stab := Pcp-group with orders [  ] )

#
# Fix a bug in SchurCovers
# 
#
gap> SchurCovers( CyclicGroup( 4 ) );
[  ]

#
gap> STOP_TEST( "bugfix.tst" );
polycyclic-2.17/tst/isom.tst0000644000175100001660000000315315054022512015473 0ustar  runnerdockergap> 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.17/tst/testall.g0000644000175100001660000000020315054022512015601 0ustar  runnerdockerLoadPackage("polycyclic");
TestDirectory(DirectoriesPackageLibrary("polycyclic", "tst"), rec(exitGAP := true));
FORCE_QUIT_GAP(1);