1############################################################################# 2## 3## This file is part of GAP, a system for computational discrete algebra. 4## This file's authors include Gábor Horváth, Stefan Kohl, Markus Püschel, Sebastian Egner. 5## 6## Copyright of GAP belongs to its developers, whose names are too numerous 7## to list here. Please refer to the COPYRIGHT file for details. 8## 9## SPDX-License-Identifier: GPL-2.0-or-later 10## 11## This file contains a method for determining structure descriptions for 12## given finite groups and implementations of related functionality. 13## 14## The purpose of this method is to give a human reader a rough impression 15## of the group structure -- it does neither determine the group up to 16## isomorphism (this would make the description for larger groups quite long 17## and difficult to read) nor is it usually the only ``sensible'' 18## description for a given group. 19## 20## The code has been translated, simplified and extended by Stefan Kohl 21## from GAP3 code written by Markus Püschel and Sebastian Egner. 22## 23 24 25############################################################################# 26## 27#M IsTrivialNormalIntersectionInList( <L>, <U>, <V> ) . . . . generic method 28## 29InstallGlobalFunction( IsTrivialNormalIntersectionInList, 30 31 function( MinNs, U, V ) 32 local N, g; 33 34 for N in MinNs do 35 g := First(GeneratorsOfGroup(N), g -> g<>One(N)); 36 if g <> fail and g in U and g in V then 37 return false; 38 fi; 39 od; 40 return true; 41 end); 42 43############################################################################# 44## 45#M IsTrivialNormalIntersection( <G>, <U>, <V> ) . . . . . . . generic method 46## 47 48InstallMethod( IsTrivialNormalIntersection, 49 "if minimal normal subgroups are computed", IsFamFamFam, 50 [ IsGroup and HasMinimalNormalSubgroups, IsGroup, IsGroup ], 51 52 function( G, U, V ) 53 local N, g; 54 55 for N in MinimalNormalSubgroups(G) do 56 g := First(GeneratorsOfGroup(N), g -> g<>One(N)); 57 if g <> fail and g in U and g in V then 58 # found a nontrivial common element 59 return false; 60 fi; 61 od; 62 # if U and V intersect nontrivially, then their intersection must contain 63 # a minimal normal subgroup, and therefore both U and V contains any of 64 # its generators 65 return true; 66 end); 67 68InstallMethod( IsTrivialNormalIntersection, 69 "generic method", IsFamFamFam, 70 [ IsGroup, IsGroup, IsGroup ], 71 72 function( G, U, V ) 73 74 return IsTrivial(NormalIntersection(U, V)); 75 end); 76 77############################################################################# 78## 79#M UnionIfCanEasilySortElements( <L1>[, <L2>, ... ] ) . . . . generic method 80## 81InstallGlobalFunction( UnionIfCanEasilySortElements, 82 83 function( arg ) 84 local i, N; 85 86 for i in [1..Length(arg)] do 87 for N in arg[i] do 88 if not CanEasilySortElements(N) then 89 return Concatenation(arg); 90 fi; 91 od; 92 od; 93 return Union(arg); 94 end); 95 96############################################################################# 97## 98#M AddSetIfCanEasilySortElements( <list>[, <obj> ) . . . . . generic method 99## 100InstallGlobalFunction( AddSetIfCanEasilySortElements, 101 102 function( list, obj ) 103 104 if CanEasilySortElements( list ) and IsSet( list ) then 105 AddSet( list, obj ); 106 else 107 Add( list, obj ); 108 fi; 109 end); 110 111############################################################################# 112## 113#M NormalComplement( <G>, <N> ) . . . . . . . . . . . generic method 114## 115InstallMethod( NormalComplement, 116 "generic method", IsIdenticalObj, [ IsGroup, IsGroup ], 117 118 function( G, N ) 119 120 # if <N> is trivial then the only complement is <G> 121 if IsTrivial(N) then 122 return G; 123 124 # if <G> and <N> are equal then the only complement is trivial 125 elif G = N then 126 return TrivialSubgroup(G); 127 128 elif not (IsSubgroup(G, N) and IsNormal(G, N)) then 129 Error("N must be a normal subgroup of G"); 130 131 else 132 return NormalComplementNC(G, N); 133 fi; 134 end); 135 136InstallMethod( NormalComplementNC, 137 "generic method", IsIdenticalObj, [ IsGroup, IsGroup ], 138 139 function( G, N ) 140 141 local F, # G/N 142 DfF, # Direct factors of F=G/N 143 gF, # element of F=G/N 144 g, # element of G corresponding to gF 145 x, # element of G 146 i, # running index 147 l, # list for storing stuff 148 b, # elements of abelian complement 149 C, # Center of G 150 S, # Subgroup of C 151 r, # RationalClass of C 152 R, # right coset 153 gens, # generators of subgroup of C 154 B, # complement to N 155 T, # = [C_G(N), G] = 1 x B' 156 Gf, # = G/T = N x B/B' 157 Nf, # = NT/T 158 Bf, # abelian complement to Nf in Gf ( = B/B') 159 BfF; # Direct factors of Bf 160 161 # if <N> is trivial then the only complement is <G> 162 if IsTrivial(N) then 163 return G; 164 165 # if <G> and <N> are equal then the only complement is trivial 166 elif G = N then 167 return TrivialSubgroup(G); 168 169 # if G/N is abelian 170 elif HasAbelianFactorGroup(G, N) then 171 F := FactorGroupNC(G, N); 172 b := []; 173 l := []; 174 i:=0; 175 for gF in IndependentGeneratorsOfAbelianGroup(F) do 176 i := i+1; 177 g := PreImagesRepresentative(NaturalHomomorphism(F), gF); 178 R := RightCoset(N, g); 179 # DirectFactorsOfGroup already computed Center and RationalClasses 180 # when calling NormalComplement 181 if HasCenter(G) and HasRationalClasses(Center(G)) then 182 l := []; 183 C := Center(G); 184 for r in RationalClasses(C) do 185 if Order(Representative(r)) = Order(gF) then 186 for x in Set(r) do 187 if x in R then 188 l := [x]; 189 break; 190 fi; 191 od; 192 fi; 193 od; 194 # Intersection(l, R) can take a long time 195 l := First(l, x -> true); 196 # if N is big, then Center is hopefully small and fast to compute 197 elif HasCenter(G) or Size(N) > Index(G, N) then 198 C := Center(G); 199 # it is enough to look for the Order(gF)-part of C 200 gens := []; 201 for x in IndependentGeneratorsOfAbelianGroup(C) do 202 Add(gens, x^(Order(x)/GcdInt(Order(x), Order(gF)))); 203 od; 204 S := SubgroupNC(C, gens); 205 if Size(S) > Size(N) then 206 # Intersection(S, R) can take a long time 207 l := First(R, x -> Order(x) = Order(gF) and x in S); 208 else 209 # Intersection(C, R) can take a long time 210 l := First(S, x -> Order(x) = Order(gF) and x in R); 211 fi; 212 # N is small, then looping through its elements might be more 213 # efficient than computing the Center 214 else 215 l := First(R, x -> Order(x) = Order(gF) 216 and IsCentral(G, SubgroupNC(G, [x]))); 217 fi; 218 if l <> fail then 219 b[i] := l; 220 else 221 return fail; 222 fi; 223 od; 224 B := SubgroupNC(G, b); 225 return B; 226 227 # if G/N is not abelian 228 else 229 T := CommutatorSubgroup(Centralizer(G, N), G); 230 if not IsTrivial(T) and IsTrivialNormalIntersection(G, T, N) then 231 Gf := FactorGroupNC(G, T); 232 Nf := Image(NaturalHomomorphism(Gf), N); 233 # not quite sure if this check is needed 234 if HasAbelianFactorGroup(Gf, Nf) then 235 Bf := NormalComplementNC(Gf, Nf); 236 if Bf = fail then 237 return fail; 238 else 239 B := PreImage(NaturalHomomorphism(Gf), Bf); 240 return B; 241 fi; 242 else 243 return fail; 244 fi; 245 else 246 return fail; 247 fi; 248 fi; 249 end); 250 251############################################################################# 252## 253#M DirectFactorsOfGroup( <G> ) . . . . . . . . . . . . . . . generic method 254## 255InstallMethod(DirectFactorsOfGroup, 256 "for direct products if normal subgroups are computed", true, 257 [ IsGroup and HasDirectProductInfo and HasNormalSubgroups ], 0, 258 259 function(G) 260 local i, info, Ns, MinNs, H, Df, DfNs, DfMinNs, N, g, gs; 261 262 Ns := NormalSubgroups(G); 263 MinNs := MinimalNormalSubgroups(G); 264 Df := []; 265 info := DirectProductInfo(G).groups; 266 for i in [1..Length(info)] do 267 H := Image(Embedding(G,i),info[i]); 268 DfMinNs := Filtered(MinNs, N ->IsSubset(H, N)); 269 if Length(DfMinNs) = 1 then 270 # size of DfMinNs is an upper bound to the number of components of H 271 Df := UnionIfCanEasilySortElements(Df, [H]); 272 else 273 DfNs := Filtered(Ns, N ->IsSubset(H, N)); 274 gs := [ ]; 275 for N in DfMinNs do 276 g := First(GeneratorsOfGroup(N), g -> g<>One(N)); 277 if g <> fail then 278 AddSetIfCanEasilySortElements(gs, g); 279 fi; 280 od; 281 # normal subgroup containing all minimal subgroups cannot have complement in H 282 DfNs := Filtered(DfNs, N -> not IsSubset(N, gs)); 283 Df := UnionIfCanEasilySortElements(Df, 284 DirectFactorsOfGroupFromList(H, DfNs, DfMinNs)); 285 fi; 286 od; 287 return Df; 288 end); 289 290InstallMethod(DirectFactorsOfGroup, "for direct products", true, 291 [ IsGroup and HasDirectProductInfo ], 0, 292 293 function(G) 294 local i, info, Ns; 295 296 Ns := []; 297 info := DirectProductInfo(G).groups; 298 for i in [1..Length(info)] do 299 Ns := UnionIfCanEasilySortElements(Ns, 300 DirectFactorsOfGroup(Image(Embedding(G,i),info[i]))); 301 od; 302 return Ns; 303 end); 304 305InstallMethod(DirectFactorsOfGroup, "if normal subgroups are computed", true, 306 [ IsGroup and HasNormalSubgroups ], 0, 307 308 function(G) 309 local Ns, MinNs, GGd, g, N, gs; 310 311 Ns := NormalSubgroups(G); 312 MinNs := MinimalNormalSubgroups(G); 313 314 if Length(MinNs)= 1 then 315 # size of MinNs is an upper bound to the number of components 316 return [ G ]; 317 fi; 318 319 if IsSolvableGroup(G) then 320 GGd := CommutatorFactorGroup(G); 321 if IsCyclic(GGd) and IsPrimePowerInt(Size(GGd)) then 322 # G is direct indecomposable, because has a unique maximal subgroup 323 return [ G ]; 324 fi; 325 else 326 GGd := CommutatorFactorGroup(G); 327 # if GGd is not cyclic of prime power size then there are at least two 328 # maximal subgroups 329 if (IsTrivial(GGd) or (IsCyclic(GGd) and IsPrimePowerInt(Size(GGd)))) 330 and Length(MaximalNormalSubgroups(G))= 1 then 331 # size of MaximalNormalSubgroups is an upper bound to the number of 332 # components 333 return [ G ]; 334 fi; 335 fi; 336 337 gs := [ ]; 338 for N in MinNs do 339 g := First(GeneratorsOfGroup(N), g -> g<>One(N)); 340 if g <> fail then 341 AddSetIfCanEasilySortElements(gs, g); 342 fi; 343 od; 344 # normal subgroup containing all minimal subgroups cannot have complement 345 Ns := Filtered(Ns, N -> not IsSubset(N, gs)); 346 347 return DirectFactorsOfGroupFromList(G, Ns, MinNs); 348 end); 349 350InstallMethod(DirectFactorsOfGroup, "generic method", true, 351 [ IsGroup ], 0, 352 function(G) 353 354 local Gd, # G' 355 GGd, # G/G' 356 C, # Center(G) 357 D, # Intersection(C, Gd) 358 Ns, # list of normal subgroups 359 MinNs, # list of minimal normal subgroups 360 gs, # list containing one generator for each MinNs 361 g, # group element 362 N, # (possible) component of G, containing g 363 B; # (possible) complement of G in N 364 365 # for abelian groups return the canonical decomposition 366 if IsTrivial(G) then 367 return [G]; 368 elif IsAbelian(G) then 369 Ns := []; 370 for g in IndependentGeneratorsOfAbelianGroup(G) do 371 Ns := UnionIfCanEasilySortElements(Ns, [SubgroupNC(G, [g])]); 372 od; 373 return Ns; 374 fi; 375 376 if not IsFinite(G) then TryNextMethod(); fi; 377 378 # the KN method performs slower in practice, only called if forced 379 if ValueOption("useKN") = true then 380 return DirectFactorsOfGroupByKN(G); 381 fi; 382 383 # nilpotent groups are direct products of Sylow subgroups 384 if IsNilpotentGroup(G) then 385 if not IsPGroup(G) then 386 Ns := [ ]; 387 for N in SylowSystem(G) do 388 Ns := UnionIfCanEasilySortElements(Ns, DirectFactorsOfGroup(N)); 389 od; 390 return Ns; 391 elif IsCyclic(Center(G)) then 392 # G is direct indecomposable, because has a unique minimal subgroup 393 return [ G ]; 394 fi; 395 # nonabelian p-groups cannot have a unique maximal subgroup 396 elif IsSolvableGroup(G) then 397 GGd := CommutatorFactorGroup(G); 398 if IsCyclic(GGd) and IsPrimePowerInt(Size(GGd)) then 399 # G is direct indecomposable, because has a unique maximal subgroup 400 return [ G ]; 401 fi; 402 fi; 403 404 # look for abelian cyclic component from the center 405 C := Center(G); 406 # abelian cyclic components have trivial intersection with the commutator 407 Gd := DerivedSubgroup(G); 408 D := Intersection(C, Gd); 409 for g in RationalClasses(C) do 410 N := Subgroup(C, [Representative(g)]); 411 if not IsTrivial(N) and IsTrivialNormalIntersection(C, D, N) then 412 B := NormalComplementNC(G, N); 413 # if B is a complement to N 414 if B <> fail then 415 return UnionIfCanEasilySortElements( DirectFactorsOfGroup(N), 416 DirectFactorsOfGroup(B)); 417 fi; 418 fi; 419 od; 420 421 # all components are nonabelian 422 if IsCyclic(Gd) and IsPrimePowerInt(Size(Gd)) then 423 # if A and B are two nonabelian components, then 424 # A' and B' must be nontrivial 425 # this can only help for some metabelian groups 426 return [ G ]; 427 fi; 428 429 if not IsTrivial(C) and HasAbelianFactorGroup(G, C) 430 and Length(DirectFactorsOfGroup(G/C)) < 4 then 431 # if A and B are two nonabelian components, 432 # where A/Center(A) and B/Center(B) are abelian, then 433 # A/Center(A) and B/Center(B) must have at least two components each 434 return [ G ]; 435 fi; 436 437 # if everything else fails, compute all normal subgroups 438 Ns := NormalSubgroups(G); 439 if IsNilpotentGroup(G) then 440 # minimal normal subgroups are central in nilpotent groups 441 MinNs := MinimalNormalSubgroups(Center(G)); 442 else 443 # MinimalNormalSubgroups seems to compute faster after NormalSubgroups 444 MinNs := MinimalNormalSubgroups(G); 445 fi; 446 447 if Length(MinNs)= 1 then 448 # size of MinNs is an upper bound to the number of components 449 return [ G ]; 450 fi; 451 452 if not IsSolvableGroup(G) then 453 GGd := CommutatorFactorGroup(G); 454 # if GGd is not cyclic of prime power size then there are at least two 455 # maximal subgroups 456 if (IsTrivial(GGd) or (IsCyclic(GGd) and IsPrimePowerInt(Size(GGd)))) 457 and Length(MaximalNormalSubgroups(G))= 1 then 458 # size of MaximalNormalSubgroups is an upper bound to the number of 459 # components 460 return [ G ]; 461 fi; 462 fi; 463 464 gs := [ ]; 465 for N in MinNs do 466 g := First(GeneratorsOfGroup(N), g -> g<>One(N)); 467 if g <> fail then 468 AddSetIfCanEasilySortElements(gs, g); 469 fi; 470 od; 471 # normal subgroup containing all minimal subgroups cannot have complement 472 Ns := Filtered(Ns, N -> not IsSubset(N, gs)); 473 474 return DirectFactorsOfGroupFromList(G, Ns, MinNs); 475 end); 476 477InstallMethod(CharacteristicFactorsOfGroup, "generic method", true, 478 [ IsGroup ], 0, 479function(G) 480local Ns,MinNs,sel,a,sz,j,gs,g,N; 481 482 Ns := ShallowCopy(CharacteristicSubgroups(G)); 483 SortBy(Ns,Size); 484 MinNs:=[]; 485 sel:=[2..Length(Ns)-1]; 486 while Length(sel)>0 do 487 a:=sel[1]; 488 sz:=Size(Ns[a]); 489 RemoveSet(sel,sel[1]); 490 Add(MinNs,Ns[a]); 491 for j in ShallowCopy(sel) do 492 if Size(Ns[j])>sz and Size(Ns[j]) mod sz=0 and IsSubset(Ns[j],Ns[a]) then 493 RemoveSet(sel,j); 494 fi; 495 od; 496 od; 497 498 if Length(MinNs)= 1 then 499 # size of MinNs is an upper bound to the number of components 500 return [ G ]; 501 fi; 502 503 gs := [ ]; 504 for N in MinNs do 505 g := First(GeneratorsOfGroup(N), g -> g<>One(N)); 506 if g <> fail then 507 AddSetIfCanEasilySortElements(gs, g); 508 fi; 509 od; 510 # normal subgroup containing all minimal subgroups cannot have complement 511 Ns := Filtered(Ns, N -> not IsSubset(N, gs)); 512 513 return DirectFactorsOfGroupFromList(G, Ns, MinNs); 514end); 515 516InstallGlobalFunction( DirectFactorsOfGroupFromList, 517 518 function ( G, NList, MinList ) 519 520 local g, N, gs, Ns, MinNs, NNs, MinNNs, facts, sizes, i, j, s1, s2; 521 522 if Length(MinList)=1 then 523 # size of MinList is an upper bound to the number of components 524 return [ G ]; 525 fi; 526 527 Ns := ShallowCopy(NList); 528 MinNs := ShallowCopy(MinList); 529 gs := [ ]; 530 for N in MinNs do 531 g := First(GeneratorsOfGroup(N), g -> g<>One(N)); 532 if g <> fail then 533 AddSetIfCanEasilySortElements(gs, g); 534 fi; 535 od; 536 # normal subgroup containing all minimal subgroups cannot have complement 537 Ns := Filtered(Ns, N -> not IsSubset(N, gs)); 538 sizes := List(Ns,Size); 539 SortParallel(sizes,Ns); 540 for s1 in Difference(Set(sizes),[Size(G),1]) do 541 i := PositionSet(sizes,s1); 542 s2 := Size(G)/s1; 543 if s1 <= s2 then 544 repeat 545 if s2 > s1 then 546 j := PositionSet(sizes,s2); 547 if j = fail then break; fi; 548 else 549 j := i + 1; 550 fi; 551 while j <= Size(sizes) and sizes[j] = s2 do 552 if IsTrivialNormalIntersectionInList(MinList,Ns[i],Ns[j]) then 553 # Ns[i] is the smallest direct factor, hence direct irreducible 554 # we keep from Ns only the subgroups of Ns[j] having size min. s1 555 NNs := Filtered(Ns{[i..j]}, N -> Size(N) >= s1 556 and s2 mod Size(N) = 0 and IsSubset(Ns[j], N)); 557 MinNNs := Filtered(MinNs, N -> s2 mod Size(N) = 0 558 and IsSubset(Ns[j], N)); 559 return UnionIfCanEasilySortElements( [ Ns[i] ], 560 DirectFactorsOfGroupFromList(Ns[j], NNs, MinNNs)); 561 fi; 562 j := j + 1; 563 od; 564 i := i + 1; 565 until i>=Size(sizes) or sizes[i] <> s1; 566 fi; 567 od; 568 return [ G ]; 569 end ); 570 571InstallGlobalFunction(DirectFactorsOfGroupByKN, 572 573 function(G) 574 575 local Ns, # list of some direct components 576 i, # running index 577 Gd, # derived subgroup G' 578 p, # prime 579 N, # (possible) component of G 580 B, # complement of G in N 581 K, # normal subgroup 582 prodK, # product of normal subgroups 583 Z1, # contains a (unique) component with trivial center 584 g,a,b, # elements of G 585 C, # Center(G) 586 D, # Intersection(C, Gd) 587 Cl, # all conjugacy classes of G 588 Clf, # filtered list of conjugacy classes of G 589 c1,c2,c3, # conjugacy class of G 590 com, # true if c1 and c2 commute, false otherwise 591 prod, # product of c1 and c2 592 RedCl, # reducible conjugacy classes of G 593 IrrCl, # irreducible conjugacy classes of G 594 preedges, # pre-edges of the non-commuting graph 595 edges, # final edges of the non-commuting graph 596 e, # one edge of the non-commuting graph 597 comp, # components of the non-commuting graph 598 DfZ1, # nonabelian direct factors of Z1 599 S1; # index set corresponding to first component 600 601 # for abelian groups return the canonical decomposition 602 if IsTrivial(G) then 603 return [G]; 604 elif IsAbelian(G) then 605 Ns := []; 606 for g in IndependentGeneratorsOfAbelianGroup(G) do 607 Ns := UnionIfCanEasilySortElements(Ns, [SubgroupNC(G, [g])]); 608 od; 609 return Ns; 610 fi; 611 612 # look for abelian cyclic component from the center 613 C := Center(G); 614 # abelian cyclic components have trivial intersection with the commutator 615 Gd := DerivedSubgroup(G); 616 D := Intersection(C, Gd); 617 for g in RationalClasses(C) do 618 N := Subgroup(C, [Representative(g)]); 619 if not IsTrivial(N) and IsTrivialNormalIntersection(C, D, N) then 620 B := NormalComplementNC(G, N); 621 # if B is a complement to N 622 if B <> fail then 623 return UnionIfCanEasilySortElements( DirectFactorsOfGroup(N), 624 DirectFactorsOfGroup(B)); 625 fi; 626 fi; 627 od; 628 629 # all components are nonabelian 630 # !!! this can (and should) be made more efficient later !!! 631 # instead of conjugacy classes we should calculate by normal subgroups 632 # generated by 1 element 633 Cl := Set(ConjugacyClasses(G)); 634 RedCl := Set(Filtered(Cl, x -> Size(x) = 1)); 635 Clf := Difference(Cl, RedCl); 636 preedges := []; 637 for c1 in Clf do 638 for c2 in Clf do 639 com := true; 640 prod := []; 641 if c1 <> c2 then 642 for a in c1 do 643 for b in c2 do 644 g := a*b; 645 if g<>b*a then 646 com := false; 647 AddSetIfCanEasilySortElements(preedges, [c1, c2]); 648 break; 649 else 650 AddSetIfCanEasilySortElements(prod, g); 651 fi; 652 od; 653 if not com then 654 break; 655 fi; 656 od; 657 if com and Size(prod) = Size(c1)*Size(c2) then 658 a := Representative(c1); 659 b := Representative(c2); 660 c3 := ConjugacyClass(G, a*b); 661 if Size(c3)=Size(prod) then 662 AddSetIfCanEasilySortElements(RedCl, c3); 663 fi; 664 fi; 665 fi; 666 od; 667 od; 668 IrrCl := Difference(Clf, RedCl); 669 670 # need to remove edges corresponding to reducible classes 671 edges := ShallowCopy(preedges); 672 for e in preedges do 673 if Intersection(e, RedCl) <> [] then 674 RemoveSet(edges, e); 675 fi; 676 od; 677 678 # now we create the graph components of the irreducible class graph 679 # as an equivalence relation 680 # this is not compatible with and not using the grape package 681 comp := EquivalenceRelationPartition( 682 EquivalenceRelationByPairsNC(IrrCl, edges)); 683 684 # replace classes in comp by their representatives 685 comp := List(comp, x-> Set(x, Representative)); 686 687 # now replace every list by their generated normal subgroup 688 Ns := List(comp, x-> NormalClosure(G, SubgroupNC(G, x))); 689 Ns := UnionIfCanEasilySortElements(Ns); 690 if IsTrivial(C) or Size(Ns)=1 then 691 return Ns; 692 fi; 693 694 # look for the possible direct components from Ns 695 # partition to two sets, consider both 696 for S1 in IteratorOfCombinations(Ns) do 697 if S1 <> [] and S1<>Ns then 698 prodK := TrivialSubgroup(G); 699 for K in S1 do 700 prodK := ClosureGroup(prodK, K); 701 od; 702 Z1 := Centralizer(G, prodK); 703 # Z1 is nonabelian <==> contains a nonabelian factor 704 if not IsAbelian(Z1) then 705 N := First(DirectFactorsOfGroupByKN(Z1), x-> not IsAbelian(x)); 706 # not sure if IsNormal(G, N) should be checked 707 708 # this certainly can be done better, 709 # because we basically know all possible normal subgroups 710 # but it suffices for now 711 B := NormalComplement(G, N); 712 if B <> fail then 713 # N is direct indecomposable by construction 714 return UnionIfCanEasilySortElements([N], 715 DirectFactorsOfGroupByKN(B)); 716 fi; 717 fi; 718 fi; 719 od; 720 721 # if no direct component is found 722 return [ G ]; 723 end); 724 725 726############################################################################# 727## 728#M SemidirectDecompositions( <G> ) . . . . . . . . . . . . . generic method 729## 730InstallGlobalFunction(SemidirectDecompositionsOfFiniteGroup, function( arg ) 731 732 local G, Ns, fullNs, method, NHs, i, N, H, NH; #, sizes; 733 734 method := "all"; 735 if Length(arg) = 1 and IsGroup(arg[1]) then 736 G := arg[1]; 737 fullNs := true; 738 elif Length(arg) = 2 and IsGroup(arg[1]) and arg[2] in ["all", 739 "any", "str"] then 740 G := arg[1]; 741 method := arg[2]; 742 fullNs := true; 743 elif Length(arg) = 2 and IsGroup(arg[1]) and IsList(arg[2]) 744 and ForAll( Set(arg[2]), N -> 745 IsSubgroup(arg[1], N) and IsNormal(arg[1], N)) 746 then 747 G := arg[1]; 748 Ns := ShallowCopy(arg[2]); 749 fullNs := false; 750 elif Length(arg) = 3 and IsGroup(arg[1]) and IsList(arg[2]) 751 and ForAll( Set(arg[2]), N -> 752 IsSubgroup(arg[1], N) and IsNormal(arg[1], N)) 753 and arg[3] in ["all", "any", "str"] then 754 G := arg[1]; 755 Ns := ShallowCopy(arg[2]); 756 method := arg[3]; 757 fullNs := false; 758 else 759 Error("usage: SemidirectDecompositionsOfFiniteGroup(<G> [, <Ns>] [, <mthd>])"); 760 fi; 761 762 if HasSemidirectDecompositions(G) then 763 NHs := [ ]; 764 for NH in SemidirectDecompositions(G) do 765 N := NH[1]; 766 H := NH[2]; 767 if method in [ "any", "str" ] and not IsTrivial(N) 768 and not IsTrivial(H) and 769 ( not IsBound(Ns) or N in Ns ) then 770 return [ N, H ]; 771 elif method="all" and 772 ( not IsBound(Ns) or N in Ns ) then 773 AddSetIfCanEasilySortElements(NHs, [ N, H ]); 774 fi; 775 od; 776 if method in [ "any", "str" ] then 777 return fail; 778 elif method = "all" then 779 return NHs; 780 fi; 781 fi; 782 783 if method in [ "any", "str" ] then 784 if HasSemidirectProductInfo(G) then 785 N := Image(Embedding(G, 2)); 786 H := Image(Embedding(G, 1)); 787 if not IsTrivial(N) and not IsTrivial(H) and 788 ( not IsBound(Ns) or N in Ns ) then 789 return [ N, H ]; 790 fi; 791 fi; 792 N := NormalHallSubgroupsFromSylows(G, "any"); 793 if N <> fail then 794 # by the Schur-Zassenhaus theorem there must exist a complement 795 if method = "any" then 796 H := ComplementClassesRepresentatives(G, N)[1]; 797 return [ N, H ]; 798 # only the isomorphism type of the complement is interesting 799 elif method = "str" then 800 Assert(1, Length( ComplementClassesRepresentatives(G, N) ) > 0); 801 return [ N, G/N ]; 802 fi; 803 fi; 804 fi; 805 806 # simple groups have no nontrivial normal subgroups 807 if IsSimpleGroup(G) then 808 if method in [ "any", "str" ] then 809 return fail; 810 elif method = "all" then 811 return [ [TrivialSubgroup(G), G], [G, TrivialSubgroup(G)] ]; 812 fi; 813 fi; 814 815 if not IsBound(Ns) then 816 Ns := ShallowCopy(NormalSubgroups(G)); 817 fi; 818# does not seem to make things faster 819# if method in [ "any", "str" ] then 820# sizes := List(Ns, Size); 821# SortParallel(sizes, Ns); 822# Ns := Reversed(Ns); 823# fi; 824 825 NHs := [ ]; 826 for N in Ns do 827 if not IsSolvableGroup(N) and not HasSolvableFactorGroup(G, N) then 828 # compute subgroup lattice, currently no other method for complement 829 ConjugacyClassesSubgroups(G);; 830 fi; 831 H := ComplementClassesRepresentatives(G, N); 832 if Length(H)>0 then 833 if method in ["any", "str"] and not IsTrivial(N) 834 and not IsTrivial(H[1]) then 835 return [ N, H[1] ]; 836 else 837 for i in [1..Length(H)] do 838 AddSetIfCanEasilySortElements( NHs, [ N, H[i] ] ); 839 od; 840 fi; 841 fi; 842 od; 843 if method in [ "any", "str" ] then 844 # no nontrivial decompositions exist 845 if fullNs then 846 SetSemidirectDecompositions(G, 847 [ [TrivialSubgroup(G), G], [G, TrivialSubgroup(G)] ]); 848 fi; 849 return fail; 850 else 851 if fullNs then 852 SetSemidirectDecompositions(G, NHs); 853 fi; 854 return NHs; 855 fi; 856end); 857 858InstallMethod( SemidirectDecompositions, 859 "generic method", true, [ IsGroup and IsFinite ], 0, 860 861 function( G ) 862 return SemidirectDecompositionsOfFiniteGroup(G); 863 end ); 864 865RedispatchOnCondition( SemidirectDecompositions, true, 866 [ IsGroup ], 867 [ IsFinite ], 0); 868 869############################################################################# 870## 871#M DecompositionTypesOfGroup( <G> ) . . . . . . . . . . . . . generic method 872## 873InstallMethod( DecompositionTypesOfGroup, 874 "generic method", true, [ IsGroup ], 0, 875 876 function ( G ) 877 878 local AG, a, # abelian invariants; an invariant 879 CS, # conjugacy classes of non-(1-or-G) subgroups 880 H, # a subgroup (possibly normal) 881 N, # a normal subgroup 882 T, # an isom. type 883 TH, tH, # isom. types for H, a type 884 TN, tN, # isom. types for N, a type 885 DTypes; # the decomposition types 886 887 if not IsFinite(G) then TryNextMethod(); fi; 888 889 DTypes := [ ]; 890 891 # abelian special case 892 if IsAbelian(G) then 893 AG := AbelianInvariants(G); 894 if Length(AG) = 1 then DTypes := Set([AG[1]]); else 895 T := ["x"]; 896 for a in AG do Add(T,a); od; 897 DTypes := Set([T]); 898 fi; 899 return DTypes; 900 fi; 901 902 # brute force enumeration 903 CS := ConjugacyClassesSubgroups( G ); 904 CS := CS{[2..Length(CS)-1]}; 905 for N in Filtered(List(Reversed(CS),Representative), 906 N -> IsNormal(G,N)) do 907 for H in List(CS, Representative) do # Lemma1 (`SemidirectFactors...') 908 if Size(H)*Size(N) = Size(G) 909 and IsTrivial(NormalIntersection(N,H)) 910 then 911 # recursion (exponentially) on (semi-)factors 912 TH := DecompositionTypesOfGroup(H); 913 TN := DecompositionTypesOfGroup(N); 914 if IsNormal(G,H) 915 then 916 # non-trivial G = H x N 917 for tH in TH do 918 for tN in TN do 919 T := [ ]; 920 if IsList(tH) and tH[1] = "x" 921 then Append(T,tH{[2..Length(tH)]}); 922 else Add(T,tH); fi; 923 if IsList(tN) and tN[1] = "x" 924 then Append(T,tN{[2..Length(tN)]}); 925 else Add(T,tN); fi; 926 Sort(T); 927 AddSet(DTypes,Concatenation(["x"],T)); 928 od; 929 od; 930 else 931 # non-direct, non-trivial G = H semidirect N 932 for tH in TH do 933 for tN in TN do 934 AddSet(DTypes,[":",tH,tN]); 935 od; 936 od; 937 fi; 938 fi; 939 od; 940 od; 941 942 # default: a non-split extension 943 if Length(DTypes) = 0 then DTypes := Set([["non-split",Size(G)]]); fi; 944 945 return DTypes; 946 end ); 947 948############################################################################# 949## 950#M IsDihedralGroup( <G> ) . . . . . . . . . . . . . . . . . . generic method 951## 952 953DoComputeDihedralGenerators := function(G) 954 local Zn, G1, T, n, t, s, i; 955 956 if Size(G) mod 2 <> 0 then return fail; fi; 957 n := Size(G)/2; 958 959 # find a normal subgroup of G of type Zn 960 if n mod 2 <> 0 then 961 # G = < s, t | s^n = t^2 = 1, s^t = s^-1 > 962 # ==> Comm(s, t) = s^-1 t s t = s^-2 ==> G' = < s^2 > = < s > 963 Zn := DerivedSubgroup(G); 964 if not ( IsCyclic(Zn) and Size(Zn) = n ) then return fail; fi; 965 else # n mod 2 = 0 966 # G = < s, t | s^n = t^2 = 1, s^t = s^-1 > 967 # ==> Comm(s, t) = s^-1 t s t = s^-2 ==> G' = < s^2 > 968 G1 := DerivedSubgroup(G); 969 if not ( IsCyclic(G1) and Size(G1) = n/2 ) then return fail; fi; 970 # G/G1 = {1*G1, t*G1, s*G1, t*s*G1} 971 T := RightTransversal(G,G1); 972 i := 1; 973 repeat 974 Zn := ClosureGroup(G1,T[i]); 975 i := i + 1; 976 until i > 4 or ( IsCyclic(Zn) and Size(Zn) = n ); 977 if not ( IsCyclic(Zn) and Size(Zn) = n ) then return fail; fi; 978 fi; # now Zn is normal in G and Zn = < s | s^n = 1 > 979 980 # choose t in G\Zn and check dihedral structure 981 repeat t := Random(G); until not t in Zn; 982 if not (Order(t) = 2 and ForAll(GeneratorsOfGroup(Zn),s->t*s*t*s=s^0)) 983 then return fail; fi; 984 985 # choose generator s of Zn 986 repeat s := Random(Zn); until Order(s) = n; 987 return [t,s]; 988end; 989 990InstallMethod( IsDihedralGroup, 991 "for a group", 992 true, 993 [ IsGroup and IsFinite ], 0, 994function(G) 995 local gens; 996 997 gens := DoComputeDihedralGenerators(G); 998 if gens = fail then 999 return false; 1000 else 1001 SetDihedralGenerators(G, gens); 1002 fi; 1003 return true; 1004end); 1005 1006InstallMethod( DihedralGenerators, 1007 "for a group", 1008 [ IsGroup and IsFinite ], 1009function(G) 1010 local gens; 1011 1012 gens := DoComputeDihedralGenerators(G); 1013 SetIsDihedralGroup(G, gens <> fail); 1014 if gens = fail then 1015 ErrorNoReturn("G is not a dihedral group"); 1016 fi; 1017 return gens; 1018end); 1019 1020############################################################################# 1021## 1022#M IsQuaternionGroup( <G> ) . . . . . . . . . . . . . . . . . generic method 1023## 1024DoComputeGeneralisedQuaternionGenerators := function(G) 1025 local N, # size of G 1026 k, # ld(N) 1027 n, # N/2 1028 G1, # derived subgroup of G 1029 Zn, # cyclic normal subgroup of index 2 in G 1030 T, # transversal of G/G1 1031 t, s, # canonical generators of the quaternion group 1032 i; # counter 1033 1034 N := Size(G); 1035 k := LogInt(N,2); 1036 if not( 2^k = N and k >= 3 ) then return fail; fi; 1037 n := N/2; 1038 1039 # G = <t, s | s^(2^k) = 1, t^2 = s^(2^k-1), s^t = s^-1> 1040 # ==> Comm(s, t) = s^-1 t s t = s^-2 ==> G' = < s^2 > 1041 G1 := DerivedSubgroup(G); 1042 if not ( IsCyclic(G1) and Size(G1) = n/2 ) then return fail; fi; 1043 1044 # find a normal subgroup of G of type Zn 1045 # G/G1 = {1*G1, t*G1, s*G1, t*s*G1} 1046 T := RightTransversal(G, G1); 1047 i := 1; 1048 repeat 1049 Zn := ClosureGroup(G1,T[i]); 1050 i := i + 1; 1051 until i > 4 or ( IsCyclic(Zn) and Size(Zn) = n ); 1052 if not ( IsCyclic(Zn) and Size(Zn) = n ) then return fail; fi; 1053 1054 # now Zn is normal in G and Zn = < s | s^n = 1 > 1055 # choose t in G\Zn and check quaternion structure 1056 repeat t := Random(G); until not t in Zn; 1057 if not (Order(t) = 4 and ForAll(GeneratorsOfGroup(Zn), s->s^t*s = s^0)) 1058 then return fail; fi; 1059 1060 # choose generator s of Zn 1061 repeat s := Random(Zn); until Order(s) = n; 1062 return [t,s]; 1063end; 1064 1065InstallMethod( IsGeneralisedQuaternionGroup, 1066 "for a group", 1067 true, 1068 [ IsGroup and IsFinite ], 1069 0, 1070function(G) 1071 local gens; 1072 1073 gens := DoComputeGeneralisedQuaternionGenerators(G); 1074 if gens = fail then 1075 return false; 1076 else 1077 SetGeneralisedQuaternionGenerators(G, gens); 1078 fi; 1079 return true; 1080end); 1081 1082InstallMethod( GeneralisedQuaternionGenerators, 1083 "for a group", 1084 [ IsGroup and IsFinite ], 1085function(G) 1086 local gens; 1087 1088 gens := DoComputeGeneralisedQuaternionGenerators(G); 1089 SetIsGeneralisedQuaternionGroup(G, gens <> fail); 1090 if gens = fail then 1091 ErrorNoReturn("G is not a generalised quaternion group"); 1092 fi; 1093 return gens; 1094end); 1095############################################################################# 1096## 1097#M IsQuasiDihedralGroup( <G> ) . . . . . . . . . . . . . . . generic method 1098## 1099InstallMethod( IsQuasiDihedralGroup, 1100 "generic method", true, [ IsGroup ], 0, 1101 1102 function ( G ) 1103 1104 local N, # size of G 1105 k, # ld(N) 1106 n, # N/2 1107 G1, # derived subgroup of G 1108 Zn, # cyclic normal subgroup of index 2 in G 1109 T, # transversal of G/G1 1110 t, s, # canonical generators of the quasidihedral group 1111 i; # counter 1112 1113 if not IsFinite(G) then TryNextMethod(); fi; 1114 1115 N := Size(G); 1116 k := LogInt(N, 2); 1117 if not( 2^k = N and k >= 4 ) then return false; fi; 1118 n := N/2; 1119 1120 # G = <t, s | s^(2^n) = t^2 = 1, s^t = s^(-1 + 2^(n-1))>. 1121 # ==> Comm(s, t) = s^-1 t s t = s^(-2+2^(n-1)) 1122 # ==> G' = < s^(-2+2^(n-1)) >, |G'| = n/2. 1123 G1 := DerivedSubgroup(G); 1124 if not ( IsCyclic(G1) and Size(G1) = n/2 ) then return false; fi; 1125 1126 # find a normal subgroup of G of type Zn 1127 # G/G1 = {1*G1, t*G1, s*G1, t*s*G1} 1128 T := RightTransversal(G, G1); 1129 i := 1; 1130 repeat 1131 Zn := ClosureGroup(G1,T[i]); 1132 i := i + 1; 1133 until i > 4 or ( IsCyclic(Zn) and Size(Zn) = n ); 1134 if not ( IsCyclic(Zn) and Size(Zn) = n ) then return false; fi; 1135 1136 # now Zn is normal in G and Zn = < s | s^n = 1 > 1137 # now remain only the possibilities for the structure: 1138 # dihedral, quaternion, quasidihedral 1139 repeat t := Random(G); until not t in Zn; 1140 1141 # detect cases: dihedral, quaternion 1142 if ForAll(GeneratorsOfGroup(Zn), s -> s^t*s = s^0) 1143 then return false; fi; 1144 1145 # choose t in Zn of order 2 1146 repeat 1147 t := Random(G); 1148 until not( t in Zn and Order(t) = 2 ); # prob = 1/4 1149 1150 # choose generator s of Zn 1151 repeat s := Random(Zn); until Order(s) = n; 1152 1153 SetQuasiDihedralGenerators(G,[t,s]); 1154 return true; 1155 end ); 1156 1157############################################################################# 1158## 1159#M IsAlternatingGroup( <G> ) . . . . . . . . . . . . . . . . generic method 1160## 1161## This method additionally sets the attribute `AlternatingDegree' in case 1162## <G> is isomorphic to a natural alternating group. 1163## 1164InstallMethod( IsAlternatingGroup, 1165 "generic method", true, [ IsGroup ], 0, 1166 1167 function ( G ) 1168 1169 local n, ids, info; 1170 1171 if not IsFinite(G) then TryNextMethod(); fi; 1172 1173 if IsNaturalAlternatingGroup(G) then return true;fi; 1174 if Size(G) < 60 then 1175 if Size(G) = 1 then 1176 SetAlternatingDegree(G,0); return true; 1177 elif Size(G) = 3 then 1178 SetAlternatingDegree(G,3); return true; 1179 elif Size(G) = 12 and IdGroup(G) = [ 12, 3 ] then 1180 SetAlternatingDegree(G,4); return true; 1181 else return false; fi; 1182 fi; 1183 1184 if not IsSimpleGroup(G) then return false; fi; 1185 1186 info := IsomorphismTypeInfoFiniteSimpleGroup(G); 1187 if info.series = "A" 1188 then SetAlternatingDegree(G,info.parameter); return true; 1189 else return false; fi; 1190 end ); 1191 1192############################################################################# 1193## 1194#M AlternatingDegree( <G> ) generic method, dispatch to `IsAlternatingGroup' 1195## 1196InstallMethod( AlternatingDegree, 1197 "generic method, dispatch to `IsAlternatingGroup'", 1198 true, [ IsGroup ], 0, 1199 1200 function ( G ) 1201 if not IsFinite(G) then TryNextMethod(); fi; 1202 if IsNaturalAlternatingGroup(G) then return DegreeAction(G); fi; 1203 if IsAlternatingGroup(G) then return AlternatingDegree(G); 1204 else return fail; fi; 1205 end ); 1206 1207############################################################################# 1208## 1209#M IsNaturalAlternatingGroup( <G> ) . . . . . . . for non-permutation group 1210## 1211InstallOtherMethod( IsNaturalAlternatingGroup, "for non-permutation group", 1212 true, [ IsGroup ], 0, 1213 1214 function ( G ) 1215 if not IsFinite(G) then TryNextMethod(); fi; 1216 if not IsPermGroup(G) then return false; else TryNextMethod(); fi; 1217 end ); 1218 1219############################################################################# 1220## 1221#M IsSymmetricGroup( <G> ) . . . . . . . . . . . . . . . . . generic method 1222## 1223## This method additionally sets the attribute `SymmetricDegree' in case 1224## <G> is isomorphic to a natural symmetric group. 1225## 1226InstallMethod( IsSymmetricGroup, 1227 "generic method", true, [ IsGroup ], 0, 1228 1229 function ( G ) 1230 1231 local G1; 1232 1233 if IsNaturalSymmetricGroup(G) then return true;fi; 1234 if not IsFinite(G) then TryNextMethod(); fi; 1235 1236 # special treatment of small cases 1237 if Size(G)<=2 then SetSymmetricDegree(G,Size(G)); return true; 1238 elif Size(G)=6 and not IsAbelian(G) then 1239 SetSymmetricDegree(G,3);return true; 1240 fi; 1241 1242 G1 := DerivedSubgroup(G); 1243 if not (IsAlternatingGroup(G1) and Index(G,G1) = 2) 1244 # this requires deg>=4 1245 or not IsTrivial(Centralizer(G,G1)) 1246 or Size(G) = 720 and IdGroup(G) <> [ 720, 763 ] 1247 then return false; fi; 1248 SetSymmetricDegree(G,AlternatingDegree(G1)); 1249 return true; 1250 end ); 1251 1252############################################################################# 1253## 1254#M IsNaturalSymmetricGroup( <G> ) . . . . . . . . for non-permutation group 1255## 1256InstallOtherMethod( IsNaturalSymmetricGroup, "for non-permutation group", 1257 true, [ IsGroup ], 0, 1258 1259 function ( G ) 1260 if not IsFinite(G) then TryNextMethod(); fi; 1261 if not IsPermGroup(G) then return false; else TryNextMethod(); fi; 1262 end ); 1263 1264############################################################################# 1265## 1266#M SymmetricDegree( <G> ) . . generic method, dispatch to `IsSymmetricGroup' 1267## 1268InstallMethod( SymmetricDegree, 1269 "generic method, dispatch to `IsSymmetricGroup'", 1270 true, [ IsGroup ], 0, 1271 1272 function ( G ) 1273 if not IsFinite(G) then TryNextMethod(); fi; 1274 if IsNaturalSymmetricGroup(G) then return DegreeAction(G); fi; 1275 if IsSymmetricGroup(G) then return SymmetricDegree(G); 1276 else return fail; fi; 1277 end ); 1278 1279############################################################################# 1280## 1281#F SizeGL( <n>, <q> ) 1282## 1283InstallGlobalFunction( SizeGL, 1284 1285 function ( n, q ) 1286 1287 local N, qn, k; 1288 1289 N := 1; 1290 qn := q^n; 1291 for k in [0..n-1] do 1292 N := N * (qn - q^k); 1293 od; 1294 return N; 1295 end ); 1296 1297############################################################################# 1298## 1299#F SizeSL( <n>, <q> ) 1300## 1301InstallGlobalFunction( SizeSL, 1302 1303 function ( n, q ) 1304 1305 local N, qn, k; 1306 1307 N := 1; 1308 qn := q^n; 1309 for k in [0..n-1] do 1310 N := N * (qn - q^k); 1311 od; 1312 return N/(q - 1); 1313 end ); 1314 1315############################################################################# 1316## 1317#F SizePSL( <n>, <q> ) 1318## 1319InstallGlobalFunction( SizePSL, 1320 1321 function ( n, q ) 1322 1323 local N, qn, k; 1324 1325 N := 1; 1326 qn := q^n; 1327 for k in [0..n-1] do 1328 N := N * (qn - q^k); 1329 od; 1330 return N/((q - 1)*(Gcd(n, q - 1))); 1331 end ); 1332 1333############################################################################# 1334## 1335#F LinearGroupParameters( <N> ) 1336## 1337InstallGlobalFunction( LinearGroupParameters, 1338 1339 function ( N ) 1340 1341 local npeGL, # list of possible [n, p, e] for a GL 1342 npeSL, # list of possible [n, p, e] for a SL 1343 npePSL, # list of possible [n, p, e] for a PSL 1344 n, p, e, # N = Size(GL(n, p^e)) 1345 pe, p2, ep, # p^ep is maximal prime power divisor of N 1346 e2, # a divisor of ep 1347 x, r, G; # temporaries 1348 1349 if not IsPosInt(N) then Error("<N> must be positive integer"); fi; 1350 1351 # Formeln: 1352 # |GL(n, q)| = Product(q^n - q^k : k in [0..n-1]) 1353 # |SL(n, q)| = |GL(n, q)| / (q - 1) 1354 # |PSL(n, q)| = |SL(n, q)| / gcd(n, q - 1) 1355 # mit q = p^e f"ur p prim, e >= 1, n >= 1. 1356 1357 # Betrachte N = |GL(n,q)|. Dann gilt f"ur n >= 2 1358 # (1) nu_p(N) = e * Binomial(n,2) und 1359 # (2) (q - 1)^n teilt N. 1360 npeGL := [ ]; npeSL := [ ]; npePSL := [ ]; 1361 if N = 1 then 1362 return rec( npeGL := npeGL, npeSL := npeSL, npePSL := npePSL ); 1363 fi; 1364 for pe in Collected(Factors(N)) do 1365 p := pe[1]; 1366 ep := pe[2]; 1367 1368 # find e, n such that (1) e*Binomial(n,2) = ep 1369 for e in DivisorsInt(ep) do 1370 1371 # find n such that Binomial(n, 2) = ep/e 1372 # <==> 8 ep/e + 1 = (2 n - 1)^2 1373 x := 8*ep/e + 1; 1374 r := RootInt(x, 2); 1375 if r^2 = x then 1376 n := (r + 1)/2; 1377 1378 # decide it 1379 G := SizeGL(n, p^e); 1380 if N = G then Add(npeGL,[n, p, e]); fi; 1381 if N = G/(p^e - 1) then Add(npeSL, [n, p, e]); fi; 1382 if N = G/((p^e - 1)*GcdInt(p^e - 1, n)) then 1383 Add(npePSL, [n, p, e]); 1384 fi; 1385 fi; 1386 od; 1387 od; 1388 return rec( npeGL := npeGL, npeSL := npeSL, npePSL := npePSL ); 1389 end ); 1390 1391############################################################################# 1392## 1393#M IsPSL( <G> ) 1394## 1395InstallMethod( IsPSL, 1396 "generic method for finite groups", true, [ IsGroup ], 0, 1397 1398 function ( G ) 1399 1400 local npes, npe; # list of possible PSL-parameters 1401 1402 if not IsFinite(G) then TryNextMethod(); fi; 1403 1404 if Size(G)>12 and not IsSimpleGroup(G) then 1405 return false; 1406 fi; 1407 1408 # check if G has appropiate size 1409 npes := LinearGroupParameters(Size(G)).npePSL; 1410 if Length(npes) = 0 then return false; fi; 1411 1412 # more than one npe-triple should only 1413 # occur in the cases |G| in [60, 168, 20160] 1414 if Length(npes) > 1 and not( Size(G) in [60, 168, 20160] ) 1415 then Error("algebraic panic! propably npe does not work"); fi; 1416 1417 # set the parameters 1418 npe := npes[1]; 1419 1420 # catch the cases: 1421 # PSL(2, 2) ~= S3, PSL(2, 3) ~= A4, 1422 # in which the PSL is not simple 1423 1424 # PSL(2, 2) 1425 if npes[1] = [2, 2, 1] then 1426 if IsAbelian(G) then return false; fi; 1427 SetParametersOfGroupViewedAsPSL(G,npe); return true; 1428 1429 # PSL(2, 3) 1430 elif npes[1] = [2, 3, 1] then 1431 if Size(DerivedSubgroup(G)) <> 4 then return false; fi; 1432 SetParametersOfGroupViewedAsPSL(G,npe); return true; 1433 1434 # PSL(3, 4) / PSL(4, 2) 1435 elif npes = [ [ 4, 2, 1 ], [ 3, 2, 2 ] ] then 1436 if IdGroup(SylowSubgroup(G,2)) = [64,138] then npe := npes[1]; 1437 elif IdGroup(SylowSubgroup(G,2)) = [64,242] then npe := npes[2]; fi; 1438 SetParametersOfGroupViewedAsPSL(G,npe); return true; 1439 1440 # other cases 1441 else 1442 if not IsSimpleGroup(G) then return false; fi; 1443 SetParametersOfGroupViewedAsPSL(G,npe); return true; 1444 fi; 1445 end ); 1446 1447############################################################################# 1448## 1449#M PSLDegree( <G> ) . . . . . . . . . . . . generic method for finite groups 1450## 1451InstallMethod( PSLDegree, 1452 "generic method for finite groups", true, [ IsGroup ], 0, 1453 1454 function ( G ) 1455 if not IsFinite(G) then TryNextMethod(); fi; 1456 if not IsPSL(G) then return fail; fi; 1457 return ParametersOfGroupViewedAsPSL(G)[1]; 1458 end ); 1459 1460############################################################################# 1461## 1462#M PSLUnderlyingField( <G> ) . . . . . . . generic method for finite groups 1463## 1464InstallMethod( PSLUnderlyingField, 1465 "generic method for finite groups", true, [ IsGroup ], 0, 1466 1467 function ( G ) 1468 if not IsFinite(G) then TryNextMethod(); fi; 1469 if not IsPSL(G) then return fail; fi; 1470 return GF(ParametersOfGroupViewedAsPSL(G)[2]^ParametersOfGroupViewedAsPSL(G)[3]); 1471 end ); 1472 1473############################################################################# 1474## 1475#M IsSL( <G> ) . . . . . . . . . . . . . . generic method for finite groups 1476## 1477InstallMethod( IsSL, 1478 "generic method for finite groups", true, [ IsGroup ], 0, 1479 1480 function ( G ) 1481 1482 local npes, # list of possible SL-parameters 1483 C; # centre of G 1484 1485 if not IsFinite(G) then TryNextMethod(); fi; 1486 1487 # check if G has appropiate size 1488 npes := LinearGroupParameters(Size(G)).npeSL; 1489 if Length(npes) = 0 then return false; fi; 1490 1491 # more than one npe-triple should never occur 1492 if Length(npes) > 1 then 1493 Error("algebraic panic! this should not occur"); 1494 fi; 1495 npes := npes[1]; 1496 1497 # catch the cases: 1498 # SL(2, 2) ~= S3, SL(2, 3) 1499 # in which the corresponding FactorGroup PSL is not simple 1500 1501 # SL(2, 2) 1502 if npes = [2, 2, 1] then 1503 if IsAbelian(G) then return false; fi; 1504 SetParametersOfGroupViewedAsSL(G,npes); return true; 1505 1506 # SL(2, 3) 1507 elif npes = [2, 3, 1] then 1508 if Size(DerivedSubgroup(G)) <> 8 then return false; fi; 1509 SetParametersOfGroupViewedAsSL(G,npes); return true; 1510 1511 # other cases, in which the contained PSL is simple 1512 else 1513 1514 # calculate the centre C of G, which should have the 1515 # size gcd(n, p^e - 1), and if so, check if G/C (which 1516 # should be the corresponding PSL) is simple 1517 C := Centre(G); 1518 if Size(C) <> Gcd(npes[1],npes[2]^npes[3] - 1) 1519 or not IsSimpleGroup(G/C) 1520 or Size(G)/2 in List(NormalSubgroups(G),Size) 1521 then return false; fi; 1522 if IsomorphismGroups(G,SL(npes[1],npes[2]^npes[3])) = fail 1523 then return false; fi; 1524 SetParametersOfGroupViewedAsSL(G,npes); return true; 1525 fi; 1526 end ); 1527 1528############################################################################# 1529## 1530#M SLDegree( <G> ) . . . . . . . . . . . . generic method for finite groups 1531## 1532InstallMethod( SLDegree, 1533 "generic method for finite groups", true, [ IsGroup ], 0, 1534 1535 function ( G ) 1536 if not IsFinite(G) then TryNextMethod(); fi; 1537 if not IsSL(G) then return fail; fi; 1538 if HasIsNaturalSL(G) and IsNaturalSL(G) 1539 then return DimensionOfMatrixGroup(G); fi; 1540 return ParametersOfGroupViewedAsSL(G)[1]; 1541 end ); 1542 1543############################################################################# 1544## 1545#M SLUnderlyingField( <G> ) . . . . . . . . generic method for finite groups 1546## 1547InstallMethod( SLUnderlyingField, 1548 "generic method for finite groups", true, [ IsGroup ], 0, 1549 1550 function ( G ) 1551 if not IsFinite(G) then TryNextMethod(); fi; 1552 if not IsSL(G) then return fail; fi; 1553 if HasIsNaturalSL(G) and IsNaturalSL(G) 1554 then return FieldOfMatrixGroup(G); fi; 1555 return GF(ParametersOfGroupViewedAsSL(G)[2]^ParametersOfGroupViewedAsSL(G)[3]); 1556 end ); 1557 1558############################################################################# 1559## 1560#M IsGL( <G> ) 1561## 1562InstallMethod( IsGL, 1563 "generic method for finite groups", true, [ IsGroup ], 0, 1564 1565 function ( G ) 1566 1567 local npes, # list of possible GL-parameters 1568 G1, # derived subgroup of G 1569 C1; # centre of G1 1570 1571 if not IsFinite(G) then TryNextMethod(); fi; 1572 1573 # check if G has appropiate size 1574 npes := LinearGroupParameters(Size(G)).npeGL; 1575 if Length(npes) = 0 then return false; fi; 1576 1577 # more than one npe-triple should never occur 1578 if Length(npes) > 1 then 1579 Error("algebraic panic! this should not occur"); 1580 fi; 1581 npes := npes[1]; 1582 1583 # catch the cases: 1584 # GL(2, 2) ~= S3, GL(2, 3) 1585 # in which the contained group PSL is not simple 1586 1587 # GL(2, 2) 1588 if npes = [2, 2, 1] then 1589 if IsAbelian(G) then return false; fi; 1590 SetParametersOfGroupViewedAsGL(G,npes); return true; 1591 1592 # GL(2, 3) 1593 elif npes = [2, 3, 1] then 1594 if IdGroup(G) <> [48,29] then return false; fi; 1595 SetParametersOfGroupViewedAsGL(G,npes); return true; 1596 1597 # other cases, in which contained PSL is simple 1598 else 1599 1600 # calculate the derived subgroup which should be the 1601 # corresponding SL of index p^e - 1 1602 G1 := DerivedSubgroup(G); 1603 if Index(G, G1) <> npes[2]^npes[3] - 1 then return false; fi; 1604 1605 # calculate the centre C1 of G1, which should have the 1606 # size gcd(n, p^e - 1), and if so, check if G1/C1 1607 # (which should be the corresponding PSL) is simple 1608 C1 := Centre(G1); 1609 if Size(C1) <> Gcd(npes[1],npes[2]^npes[3] - 1) 1610 or not IsSimpleGroup(G1/C1) 1611 then return false; fi; 1612 if IsomorphismGroups(G,GL(npes[1],npes[2]^npes[3])) = fail 1613 then return false; fi; 1614 SetParametersOfGroupViewedAsGL(G,npes); return true; 1615 fi; 1616 end ); 1617 1618############################################################################# 1619## 1620#M GLDegree( <G> ) . . . . . . . . . . . . generic method for finite groups 1621## 1622InstallMethod( GLDegree, 1623 "generic method for finite groups", true, [ IsGroup ], 0, 1624 1625 function ( G ) 1626 if not IsFinite(G) then TryNextMethod(); fi; 1627 if not IsGL(G) then return fail; fi; 1628 if HasIsNaturalGL(G) and IsNaturalGL(G) 1629 then return DimensionOfMatrixGroup(G); fi; 1630 return ParametersOfGroupViewedAsGL(G)[1]; 1631 end ); 1632 1633############################################################################# 1634## 1635#M GLUnderlyingField( <G> ) . . . . . . . . generic method for finite groups 1636## 1637InstallMethod( GLUnderlyingField, 1638 "generic method for finite groups", true, [ IsGroup ], 0, 1639 1640 function ( G ) 1641 if not IsFinite(G) then TryNextMethod(); fi; 1642 if not IsGL(G) then return fail; fi; 1643 if HasIsNaturalGL(G) and IsNaturalGL(G) 1644 then return FieldOfMatrixGroup(G); fi; 1645 return GF(ParametersOfGroupViewedAsGL(G)[2]^ParametersOfGroupViewedAsGL(G)[3]); 1646 end ); 1647 1648############################################################################# 1649## 1650#M StructureDescription( <G> ) . . . . . . . . . for abelian or finite group 1651## 1652BindGlobal( "SD_insertsep", # function to join parts of name 1653 function ( strs, sep, brack ) 1654 1655 local short, s, i; 1656 1657 short := ValueOption("short") = true; 1658 1659 if strs = [] then return ""; fi; 1660 strs := Filtered(strs,str->str<>""); 1661 if Length(strs) > 1 then 1662 for i in [1..Length(strs)] do 1663 if Intersection(strs[i],brack) <> "" 1664 then strs[i] := Concatenation("(",strs[i],")"); fi; 1665 od; 1666 fi; 1667 s := strs[1]; 1668 for i in [2..Length(strs)] do 1669 s := Concatenation(s,sep,strs[i]); 1670 od; 1671 if short then RemoveCharacters(s," "); fi; 1672 return s; 1673 end); 1674 1675BindGlobal( "SD_cyclic", 1676 function ( n ) 1677 if n = 0 or n = infinity then 1678 return "Z"; 1679 fi; 1680 return Concatenation("C",String(n)); 1681 end); 1682 1683BindGlobal( "SD_cycsaspowers", # function to write C2 x C2 x C2 as 2^3, etc. 1684 function ( name, cycsizes ) 1685 1686 local short, g, d, k, j, n; 1687 1688 short := ValueOption("short") = true; 1689 if not short then return name; fi; 1690 RemoveCharacters(name," "); 1691 cycsizes := Collected(cycsizes); 1692 for n in cycsizes do 1693 d := n[1]; k := n[2]; 1694 g := SD_cyclic(d); 1695 if d = 0 then 1696 d := "Z"; 1697 else 1698 d := String(d); 1699 fi; 1700 if k > 1 then 1701 for j in Reversed([2..k]) do 1702 name := ReplacedString(name,SD_insertsep(List([1..j],i->g),"x",""), 1703 Concatenation(d,"^",String(j))); 1704 od; 1705 fi; 1706 od; 1707 RemoveCharacters(name,"C"); 1708 return name; 1709 end); 1710 1711BindGlobal( "StructureDescriptionForAbelianGroups", # for abelian groups 1712 1713 function ( G ) 1714 1715 local cycsizes; # sizes of cyclic factors of G 1716 1717 # special case trivial group 1718 if IsTrivial(G) then return "1"; fi; 1719 1720 # special case abelian group 1721 cycsizes := AbelianInvariants(G); 1722 cycsizes := Reversed(ElementaryDivisorsMat(DiagonalMat(cycsizes))); 1723 cycsizes := Filtered(cycsizes,n->n<>1); 1724 return SD_cycsaspowers(SD_insertsep(List(cycsizes, SD_cyclic), 1725 " x ",""), cycsizes); 1726 end ); 1727 1728BindGlobal( "StructureDescriptionForFiniteSimpleGroups", # for simple groups 1729 1730 function ( G ) 1731 1732 local name, # buffer for computed name 1733 series, # series of simple groups 1734 parameter; # parameters of G in series 1735 1736 # special case abelian group 1737 if IsAbelian(G) then return StructureDescriptionForAbelianGroups(G); fi; 1738 1739 # special case alternating group 1740 if IsAlternatingGroup(G) 1741 then return Concatenation("A",String(AlternatingDegree(G))); fi; 1742 1743 # special case PSL 1744 if IsPSL(G) then 1745 return Concatenation("PSL(",String(PSLDegree(G)),",", 1746 String(Size(PSLUnderlyingField(G))),")"); 1747 fi; 1748 1749 name := SplitString(IsomorphismTypeInfoFiniteSimpleGroup(G).name," "); 1750 name := name[1]; 1751 if Position(name,',') = fail then RemoveCharacters(name,"()"); else 1752 series := IsomorphismTypeInfoFiniteSimpleGroup(G).series; 1753 parameter := IsomorphismTypeInfoFiniteSimpleGroup(G).parameter; 1754 if series = "2A" then 1755 name := Concatenation("PSU(",String(parameter[1]+1),",", 1756 String(parameter[2]),")"); 1757 elif series = "B" then 1758 name := Concatenation("O(",String(2*parameter[1]+1),",", 1759 String(parameter[2]),")"); 1760 elif series = "2B" then 1761 name := Concatenation("Sz(",String(parameter),")"); 1762 elif series = "C" then 1763 name := Concatenation("PSp(",String(2*parameter[1]),",", 1764 String(parameter[2]),")"); 1765 elif series = "D" then 1766 name := Concatenation("O+(",String(2*parameter[1]),",", 1767 String(parameter[2]),")"); 1768 elif series = "2D" then 1769 name := Concatenation("O-(",String(2*parameter[1]),",", 1770 String(parameter[2]),")"); 1771 elif series = "3D" then 1772 name := Concatenation("3D(4,",String(parameter),")"); 1773 elif series in ["2F","2G"] and parameter > 2 then 1774 name := Concatenation("Ree(",String(parameter),")"); 1775 fi; 1776 fi; 1777 return name; 1778 end ); 1779 1780BindGlobal( "StructureDescriptionForFiniteGroups", # for finite groups 1781 1782 function ( G ) 1783 1784 local G1, # the group G reconstructed; result 1785 Hs, # split factors of G 1786 Gs, # factors of G 1787 cyclics, # cyclic factors of G 1788 cycsizes, # sizes of cyclic factors of G 1789 noncyclics, # noncyclic factors of G 1790 cycname, # part of name corresponding to cyclics 1791 noncycname, # part of name corresponding to noncyclics 1792 name, # buffer for computed name 1793 cname, # name for centre of G 1794 dname, # name for derived subgroup of G 1795 NH, H, N, N1, # semidirect factors of G 1796 NHs, # [N, H] decompositions 1797 NHname, # name of NH 1798 NHs1, # NH's with preferred N and H 1799 NHs1Names, # names of products in NHs1 1800 len, # maximal number of direct factors 1801 g, # an element of G 1802 id, # id of G in the library of perfect groups 1803 short, # short / long output format 1804 nice, # nice output (slower) 1805 i,j, # counters 1806 primes, # prime divisors of Size(G) 1807 d, # divisor of Size(G) 1808 k, # maximal power of d in Size(G) 1809 pi; # subset of primes 1810 1811 short := ValueOption("short") = true; 1812 nice := ValueOption("nice") = true; 1813 1814 # fetch name from precomputed list, if available 1815 if ValueOption("recompute") <> true and Size(G) <= 2000 then 1816 if IsBound(NAMES_OF_SMALL_GROUPS[Size(G)]) then 1817 i := IdGroup(G)[2]; 1818 if IsBound(NAMES_OF_SMALL_GROUPS[Size(G)][i]) then 1819 name := ShallowCopy(NAMES_OF_SMALL_GROUPS[Size(G)][i]); 1820 cycsizes := []; 1821 if short then 1822 # DivisorsInt is rather slow, but we only call it for small groups 1823 for d in Reversed(DivisorsInt(Size(G))) do 1824 if d >1 then 1825 k := LogInt(Size(G), d); 1826 if k>1 then 1827 for j in [1..k] do 1828 Add(cycsizes, d); 1829 od; 1830 fi; 1831 fi; 1832 od; 1833 fi; 1834 return SD_cycsaspowers(name, cycsizes); 1835 fi; 1836 fi; 1837 fi; 1838 1839 # special case trivial group 1840 if IsTrivial(G) then return "1"; fi; 1841 1842 # special case abelian group 1843 if IsAbelian(G) then return StructureDescriptionForAbelianGroups(G); fi; 1844 1845 # special case alternating group 1846 if IsAlternatingGroup(G) 1847 then return Concatenation("A",String(AlternatingDegree(G))); fi; 1848 1849 # special case symmetric group 1850 if IsSymmetricGroup(G) 1851 then return Concatenation("S",String(SymmetricDegree(G))); fi; 1852 1853 # special case dihedral group 1854 if IsDihedralGroup(G) and Size(G) > 6 1855 then return Concatenation("D",String(Size(G))); fi; 1856 1857 # special case quaternion group 1858 if IsQuaternionGroup(G) 1859 then return Concatenation("Q",String(Size(G))); fi; 1860 1861 # special case quasidihedral group 1862 if IsQuasiDihedralGroup(G) 1863 then return Concatenation("QD",String(Size(G))); fi; 1864 1865 # special case PSL 1866 if IsPSL(G) then 1867 return Concatenation("PSL(",String(PSLDegree(G)),",", 1868 String(Size(PSLUnderlyingField(G))),")"); 1869 fi; 1870 1871 # special case SL 1872 if IsSL(G) then 1873 return Concatenation("SL(",String(SLDegree(G)),",", 1874 String(Size(SLUnderlyingField(G))),")"); 1875 fi; 1876 1877 # special case GL 1878 if IsGL(G) then 1879 return Concatenation("GL(",String(GLDegree(G)),",", 1880 String(Size(GLUnderlyingField(G))),")"); 1881 fi; 1882 1883 # other simple group 1884 if IsSimpleGroup(G) then 1885 return StructureDescriptionForFiniteSimpleGroups(G); 1886 fi; 1887 1888 # direct product decomposition 1889 Gs := DirectFactorsOfGroup( G ); 1890 if Length(Gs) > 1 then 1891 1892 # decompose the factors 1893 Hs := List(Gs,StructureDescription); 1894 1895 # construct 1896 cyclics := Filtered(Gs,IsCyclic); 1897 if cyclics <> [] then 1898 cycsizes := ElementaryDivisorsMat(DiagonalMat(List(cyclics,Size))); 1899 cycsizes := Filtered(cycsizes,n->n<>1); 1900 cycname := SD_cycsaspowers(SD_insertsep(List(cycsizes, SD_cyclic), 1901 " x ",":."), cycsizes); 1902 else cycname := ""; fi; 1903 noncyclics := Difference(Gs,cyclics); 1904 noncycname := SD_insertsep(List(noncyclics,StructureDescription), 1905 " x ",":."); 1906 1907 return SD_insertsep([cycname,noncycname]," x ",":."); 1908 fi; 1909 1910 # semidirect product decomposition 1911 if not nice then 1912 NH := SemidirectDecompositionsOfFiniteGroup( G, "str" ); 1913 if NH <> fail then 1914 H := NH[2]; N := NH[1]; 1915 return SD_insertsep([StructureDescription(N), 1916 StructureDescription(H)]," : ","x:."); 1917 fi; 1918 else 1919 NHs := [ ]; 1920 for NH in SemidirectDecompositions( G ) do 1921 if not IsTrivial( NH[1] ) and not IsTrivial( NH[2] ) then 1922 AddSetIfCanEasilySortElements(NHs, [ NH[1], NH[2] ]); 1923 fi; 1924 od; 1925 if Length(NHs) > 0 then 1926 1927 # prefer abelian H; abelian N; many direct factors in N; phi injective 1928 NHs1 := Filtered(NHs, NH -> IsAbelian(NH[2])); 1929 if Length(NHs1) > 0 then NHs := NHs1; fi; 1930 NHs1 := Filtered(NHs, NH -> IsAbelian(NH[1])); 1931 if Length(NHs1) > 0 then 1932 NHs := NHs1; 1933 len := Maximum( List(NHs, NH -> Length(AbelianInvariants(NH[1]))) ); 1934 NHs := Filtered(NHs, NH -> Length(AbelianInvariants(NH[1])) = len); 1935 fi; 1936 NHs1 := Filtered(NHs, NH -> Length(DirectFactorsOfGroup(NH[1])) > 1); 1937 if Length(NHs1) > 0 then 1938 NHs := NHs1; 1939 len := Maximum(List(NHs,NH -> Length(DirectFactorsOfGroup(NH[1])))); 1940 NHs := Filtered(NHs,NH -> Length(DirectFactorsOfGroup(NH[1]))=len); 1941 fi; 1942 NHs1 := Filtered(NHs, NH -> IsTrivial(Centralizer(NH[2],NH[1]))); 1943 if Length(NHs1) > 0 then NHs := NHs1; fi; 1944 if Length(NHs) > 1 then 1945 1946 # decompose the pairs [N, H] and remove isomorphic copies 1947 NHs1 := []; 1948 NHs1Names := []; 1949 for NH in NHs do 1950 NHname := SD_insertsep([StructureDescription(NH[1]), 1951 StructureDescription(NH[2])], 1952 " : ","x:."); 1953 if not NHname in NHs1Names then 1954 Add(NHs1, NH); 1955 Add(NHs1Names, NHname); 1956 fi; 1957 od; 1958 NHs := NHs1; 1959 1960 if Length(NHs) > 1 then 1961 Info(InfoWarning,2,"Warning! Non-unique semidirect product:"); 1962 Info(InfoWarning,2,List(NHs,NH -> List(NH,StructureDescription))); 1963 fi; 1964 fi; 1965 1966 H := NHs[1][2]; N := NHs[1][1]; 1967 1968 return SD_insertsep([StructureDescription(N:nice), 1969 StructureDescription(H:nice)]," : ","x:."); 1970 fi; 1971 fi; 1972 1973 # non-splitting, non-simple group 1974 if not IsTrivial(Centre(G)) then 1975 cname := SD_insertsep([StructureDescription(Centre(G)), 1976 StructureDescription(G/Centre(G))]," . ","x:."); 1977 fi; 1978 if not IsPerfectGroup(G) then 1979 dname := SD_insertsep([StructureDescription(DerivedSubgroup(G)), 1980 StructureDescription(G/DerivedSubgroup(G))], 1981 " . ","x:."); 1982 fi; 1983 if IsBound(cname) and IsBound(dname) and cname <> dname 1984 then return Concatenation(cname," = ",dname); 1985 elif IsBound(cname) then return cname; 1986 elif IsBound(dname) then return dname; 1987 elif not IsTrivial(FrattiniSubgroup(G)) 1988 then return SD_insertsep([StructureDescription(FrattiniSubgroup(G)), 1989 StructureDescription(G/FrattiniSubgroup(G))], 1990 " . ","x:."); 1991 elif IsPosInt(NrPerfectGroups(Size(G))) 1992 and not Size(G) in [ 86016, 368640, 737280 ] 1993 # this does not happen for Size(G)<10^6 1994 then 1995 id := PerfectIdentification(G); 1996 return Concatenation("PerfectGroup(",String(id[1]),",", 1997 String(id[2]),")"); 1998 else return Concatenation("<a non-simple perfect group of order ", 1999 Size(G)," with trivial centre and trivial Frattini ", 2000 "subgroup, which cannot be written as a direct or ", 2001 "semidirect product of smaller groups>"); 2002 fi; 2003 end ); 2004 2005InstallMethod( StructureDescription, "for abelian groups", 2006 true, [ IsGroup and IsAbelian ], 0, 2007 StructureDescriptionForAbelianGroups ); 2008 2009RedispatchOnCondition( StructureDescription, true, 2010 [ IsGroup ], 2011 [ IsAbelian ], 0); 2012 2013InstallMethod( StructureDescription, "for finite simple groups", 2014 true, [ IsGroup and IsFinite and IsSimpleGroup ], 0, 2015 StructureDescriptionForFiniteSimpleGroups ); 2016 2017InstallMethod( StructureDescription, "for finite groups", 2018 true, [ IsGroup and IsFinite ], 0, 2019 StructureDescriptionForFiniteGroups ); 2020 2021RedispatchOnCondition( StructureDescription, true, 2022 [ IsGroup ], 2023 [ IsFinite ], 0); 2024 2025############################################################################# 2026## 2027#M ViewObj( <G> ) . . . . . . . . for group with known structure description 2028## 2029InstallMethod( ViewObj, 2030 "for groups with known structure description", 2031 true, [ IsGroup and HasStructureDescription ], SUM_FLAGS, 2032 2033 function ( G ) 2034 if HasName(G) then TryNextMethod(); fi; 2035 Print(StructureDescription(G)); 2036 end ); 2037