1#############################################################################
2##
3##  This file is part of GAP, a system for computational discrete algebra.
4##  This file's authors include Lukas Maas, Jack Schmidt.
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 the implementation for Schur covers of symmetric and
12##  alternating groups on Coxeter or standard generators.
13##
14
15#############################################################################
16##
17##  Faithful, irreducible representations of minimal degree of the double
18##  covers of symmetric groups can be constructed inductively using the
19##  methods of https://arxiv.org/abs/0911.3794
20##
21##  The inductive formulation uses a number of helper routines which are
22##  wrapped inside a function call to keep from declaring a large number
23##  of (private) global variables.
24##
25
26Perform( [1], function(x)
27  local S, S1, coeffS2, S2, coeffS3, S3, bmat, spinsteps, SpinDimSym,
28    BasicSpinRepSymPos, BasicSpinRepSymNeg, BasicSpinRepSym,
29    SanityCheckPos, SanityCheckNeg, BasicSpinRepSymTest;
30
31
32##  let 2S+(n) = < t_1, ..., t_(n-1) > subject to the relations
33##    (t_i)^2 = z for 1 <= i <= n-1,
34##    z^2 = 1,
35##    ( t_i*t_(i+1) )^3 = z for 1 <= i <= n-2,
36##    t_i*t_j = z*t_j*t_i for 1 <= i, j <= n-1 with | i - j | > 1.
37##
38##  The following functions allow the construction of basic spin
39##  representations of 2S+(n) over fields of any characteristic.
40
41##  SpinDimSym
42##  IN   integers n >= 4, p >= 0
43##  OUT  the degree of a basic spin repr. of 2S(n) over a field of
44##       characteristic p
45SpinDimSym:= function( n, p )
46    local r;
47    r:= n mod 2;
48    if r = 0 then
49        return 2^((n-2)/2);
50    elif p = 0 then
51        return 2^((n-1)/2);
52    elif r = 1 and n mod p = 0 then
53        return 2^((n-3)/2);
54    else
55        return 2^((n-1)/2);
56    fi;
57end;
58
59##  SanityCheckPos
60##  IN  A record containing the matrices in T, the degree of the symmetric
61##      group n, and the characteristic f the field p
62##  OUT true if the matrices in T are the right size, over the right field, and
63##      satisfy the presentation for 2S(n) given above.  Also checks the
64##      components Sym and Alt if present.
65SanityCheckPos := function( input )
66  local i, j;
67
68    if input.n <> Length( input.T ) + 1 then
69      Print("#I SanityCheckPos: Wrong degree: ",input.n," vs. ",Length(input.T)+1,"\n");
70      return false;
71    fi;
72
73    if input.p <> Characteristic( input.T[1] ) then
74      Print("#I SanityCheckPos: Wrong characteristic: ",input.p," vs. ",Characteristic(input.T[1]),"\n");
75      return false;
76    fi;
77
78    if SpinDimSym( input.n, input.p ) <> Length( input.T[1] ) then
79        Print( "#I SanityCheckPos: Wrong degree: ",SpinDimSym( input.n, input.p )," vs. ",Length( input.T[1] ),"\n" );
80        return false;
81    fi;
82
83    if not ForAll( input.T, mat -> Size(mat) = Size(mat[1]) and Size(mat)=Size(input.T[1])) then
84      Print("#I SanityCheckPos: Matrices not all same size\n");
85      return false;
86    fi;
87
88    for i in [ 1 .. input.n-1 ] do
89        if not IsOne(-input.T[i]^2) then
90            Print( "#I SanityCheckPos: Wrong order for T[",i,"]\n");
91            return false;
92        fi;
93    od;
94    for i in [ 1 .. input.n-2 ] do
95        if not IsOne( -( input.T[i]*input.T[i+1] )^3 ) then
96            Print( "#I SanityCheckPos: Braid relation failed at position ", i, "\n" );
97            return false;
98        fi;
99        for j in [ i+2 .. input.n-1 ] do
100            if not IsOne( - ( input.T[i]*input.T[j] )^2 ) then
101                Print( "#I SanityCheckPos: Commutator relation failed for ( ", i, ", ", j ," )\n" );
102                return false;
103            fi;
104        od;
105    od;
106
107    if IsBound( input.Sym ) then
108      if not input.Sym[1] = Product( Reversed( input.T ) ) then
109        Print( "SanityCheckPos: Wrong Sym[1]\n" );
110        return false;
111      fi;
112
113      if not input.Sym[2] = input.T[1] then
114        Print( "SanityCheckPos: Wrong Sym[2]\n" );
115        return false;
116      fi;
117    fi;
118
119    if IsBound( input.Alt ) then
120      if not input.Alt[1] = Product( Reversed( input.T{[1..2*Int((input.n-1)/2)]} ) ) then
121        Print( "SanityCheckPos: Wrong Alt[1]\n" );
122        return false;
123      fi;
124
125      if not input.Alt[2] = input.T[input.n-1]*input.T[input.n-2] then
126        Print( "SanityCheckPos: Wrong Alt[2]\n" );
127        return false;
128      fi;
129    fi;
130
131    return true;
132end;
133
134##  SanityCheckNeg
135##  IN  A record containing the matrices in T, the degree of the symmetric
136##      group n, and the characteristic f the field p
137##  OUT true if the matrices in T are the right size, over the right field, and
138##      satisfy the presentation for 2S-(n) given above.  Also checks the
139##      components Sym and Alt if present.
140SanityCheckNeg := function( S, p )
141    local d, deg, z, t, i, j;
142
143    d:= Length( S );
144    deg:= Length( S[1] );
145    if SpinDimSym( d+1, p ) <> deg then
146        Print( "#I SanityCheckNeg: wrong degree!\n" );
147        return false;
148    fi;
149    #Print( "#I SanityCheckNeg: degree: ", deg , "\n" );
150    for i in [ 1 .. d ] do
151        if not IsOne( S[i]^2 ) then
152            Print( "#I SanityCheckNeg: order failed at position ", i, "\n" );
153            return false;
154        fi;
155    od;
156    for i in [ 1 .. d-1 ] do
157        if not IsOne( ( S[i]*S[i+1] )^3 ) then
158            Print( "#I SanityCheckNeg: braid relation failed at position ", i, "\n" );
159            return false;
160        fi;
161        for j in [ i+2 .. d ] do
162            if S[i]*S[j] <> -S[j]*S[i] then
163                Print( "#I SanityCheckNeg: commuting relation failed for ( ", i, ", ", j ," )\n" );
164                return false;
165            fi;
166        od;
167    od;
168    #Print( "#I SanityCheckNeg: all relations satisfied\n" );
169    return true;
170end;
171
172##  bmat -- blck matrix maker
173##  IN  the blocks a,b,c,d of the matrix [[a,b],[c,d]]
174##  OUT a normal matrix with the same entries as the corresponding block
175##      matrix.
176bmat := function(a,b,c,d)
177  local mat;
178  mat := DirectSumMat( a, d );
179  if b <> 0 then mat{[1..Length(a)]}{[1+Length(a[1])..Length(mat[1])]} := b; fi;
180  if c <> 0 then mat{[1+Length(a)..Length(mat)]}{[1..Length(a[1])]} := c; fi;
181  return mat;
182end;
183
184##  construction S of Definition 4 / Lemma 5
185##  IN  an input record with n,p,T and optionally Sym and/or Alt,
186##      where n,p satisfy the hypothesis of Def 4 / Lemma 5
187##  OUT the same, but for 2S(n+1)
188S:= function( old )
189  local new, I, z, i;
190
191  #Print("S from ",old.n," to ",new.n,"\n");
192  new := rec( n := old.n+1, p:=old.p, T:=[] );
193
194  for i in [ 1 .. new.n-3 ] do
195    new.T[i] := DirectSumMat( old.T[i], -old.T[i] );
196  od;
197  I := old.T[1]^0;
198  z := 0*old.T[1];
199  new.T[new.n-2] := bmat( old.T[new.n-2], -I, 0, -old.T[new.n-2] );
200  new.T[new.n-1] := bmat( z, I, -I, z );
201
202  if IsBound( old.Sym ) then
203    new.Sym := [];
204    if new.n < 5
205    then new.Sym[1] := Product(Reversed(new.T));
206    else new.Sym[1] := bmat( 0*old.Sym[1], (-1)^new.n*old.Sym[1], -old.Sym[1], (-1)^new.n*old.T[new.n-2]*old.Sym[1] );
207    fi;
208    new.Sym[2] := new.T[1];
209  fi;
210
211  if IsBound( old.Alt ) then
212    new.Alt := [];
213    if IsOddInt(new.n)
214    then new.Alt[1] := new.Sym[1];
215    else new.Alt[1] := -new.T[new.n-1]*new.Sym[1];
216    fi;
217    new.Alt[2] := new.T[new.n-1]*new.T[new.n-2];
218  fi;
219
220  Assert( 1, SanityCheckPos( new ) );
221  return new;
222end;
223
224##  construction S1 of Lemma 7
225##  IN  an input record with n,p,T and optionally Sym and/or Alt,
226##      where n,p satisfy the hypothesis of Lemma 7
227##  OUT the same, but for 2S(n+1)
228S1:= function( old )
229  local new, J;
230
231  #Print("S1 from ",old.n," to ",new.n,"\n");
232  new := rec( n := old.n + 1, p := old.p, T := ShallowCopy( old.T ) );
233
234  J := Sum( [1..new.n-2], k -> k*old.T[k] );
235  if new.p = 2 and 2 = new.n mod 4 then
236    new.T[new.n-1] := J + J^0;
237  else
238    new.T[new.n-1] := J;
239  fi;
240
241  if IsBound( old.Sym ) then
242    new.Sym := [];
243    new.Sym[1] := new.T[new.n-1]*old.Sym[1];
244    new.Sym[2] := old.Sym[2];
245  fi;
246
247  if IsBound( old.Alt ) then
248    new.Alt := [];
249    if IsOddInt(new.n)
250    then new.Alt[1] := new.Sym[1];
251    else new.Alt[1] := old.Alt[1];
252    fi;
253    new.Alt[2] := new.T[new.n-1]*new.T[new.n-2];
254  fi;
255
256  Assert( 1, SanityCheckPos( new ) );
257  return new;
258end;
259
260## return alpha ( = alpha^+ ) and beta as in Lemma 10
261## here n(n-1)(n-2) must not be divisible by p
262coeffS2:= function( n, p )
263    local one, a, b, c, alpha;
264    if p = 0 then
265        c:= n-2;
266        alpha:= (n-1)^-1*( 1 + Sqrt( -n*c^-1 ) );
267    else
268        one:= Z( p )^0;
269        c:= (n-2) mod p;
270        a:= -n*c^-1 mod p;
271        a:= LogFFE( a*one, Z(p^2) ) / 2;
272        b:= (n-1)^-1 mod p;
273        alpha:= b*(one+Z(p^2)^a);
274    fi;
275    return rec( alpha:= alpha, beta:= alpha*c );
276end;
277
278##  construction S2 of Lemma 10
279##  IN  an input record with n,p,T and optionally Sym and/or Alt,
280##      where n,p satisfy the hypothesis of Lemma 10
281##  OUT the same, but for 2S(n+2)
282S2:= function( old )
283  local mid, new, coeffs, a, b, J, I;
284
285  #Print("S2 from ",old.n," to ",old.n+2," via S\n");
286
287  mid := S( old );
288
289  new := rec( n := mid.n + 1, p := mid.p, T := ShallowCopy( mid.T ) );
290
291  coeffs:= coeffS2( new.n, new.p );
292  a := coeffs.alpha;
293  b := coeffs.beta;
294  J := Sum( [ 1 .. new.n-3 ], k-> k*old.T[k] );
295  I := old.T[1]^0;
296  new.T[new.n-1] := bmat( -a*J, (b-1)*I, b*I, a*J );
297
298  if IsBound( old.Sym ) then
299    new.Sym := [];
300    new.Sym[1] := new.T[new.n-1]*mid.Sym[1];
301    new.Sym[2] := mid.Sym[2];
302  fi;
303
304  if IsBound( old.Alt ) then
305    new.Alt := [];
306    if IsOddInt( new.n )
307    then new.Alt[1] := new.Sym[1];
308    else new.Alt[1] := mid.Sym[1];
309    fi;
310    new.Alt[2] := new.T[new.n-1]*new.T[new.n-2];
311  fi;
312
313  Assert( 1, SanityCheckPos( new ) );
314  return  new;
315end;
316
317##  coeffS3 - a needed coefficient
318##  IN  A prime p, or p = 0
319##  OUT Sqrt(-1) in GF(p^2) or CF(4)
320coeffS3:= function( p )
321  if 0 = p then return E(4);
322  elif 2 = p then return Z(2);
323  elif 1 = p mod 4 then return Z(p)^((p-1)/4);
324  else return Z(p^2)^((p^2-1)/4);
325  fi;
326end;
327
328##  construction S3 of Lemma 11
329##  IN  an input record with n,p,T and optionally Sym and/or Alt,
330##      where n,p satisfy the hypothesis of Lemma 11
331##  OUT the same, but for 2S(n+4)
332S3:= function( old )
333  local mid, new, a, J0, I, J;
334  #Print("S3 from ",old.n," to ",old.n+4," via S,S1,S\n");
335
336  mid := S( S1( S( old ) ) ); # now at n-1
337
338  new := rec( n := mid.n + 1, p := mid.p, T := ShallowCopy( mid.T ) );
339
340  a := coeffS3( old.p );
341  J0:= Sum( [1..new.n-5], k-> k*old.T[k] );
342  I := old.T[1]^0;
343  J := a*bmat(J0, 2*I, 2*I, -J0);
344  new.T[new.n-1] := bmat( J, -J^0, 0, -J );
345
346  if IsBound( old.Sym ) then
347    new.Sym := [];
348    new.Sym[1] := new.T[new.n-1]*mid.Sym[1];
349    new.Sym[2] := mid.Sym[2];
350  fi;
351
352  if IsBound( old.Alt ) then
353    new.Alt := [];
354    if IsOddInt( new.n )
355    then new.Alt[1] := new.Sym[1];
356    else new.Alt[1] := mid.Alt[1];
357    fi;
358    new.Alt[2] := new.T[new.n-1]*new.T[new.n-2];
359  fi;
360
361  Assert( 1, SanityCheckPos( new ) );
362  return new;
363end;
364
365##  spinsteps
366##  IN  the degree n and characteristic p > 2
367##  OUT a list which describes the steps of construction
368spinsteps:= function( n, p )
369  local d, k, kmodp, parity;
370  d:= [];
371  k:= n;
372  while k > 4 do
373    kmodp:= k mod p;
374    parity:= k mod 2;
375    if kmodp > 2 then
376      if parity = 1 then
377        Add( d, 0 );
378        k:= k-1;
379      else
380        Add( d, 2 );
381        k:= k-2;
382      fi;
383    elif kmodp = 0 then
384      Add( d, 1 );
385      k:= k-1;
386    elif kmodp = 1 then
387      Add( d, 0 );
388      k:= k-1;
389    else
390      if parity = 1 then
391        Add( d, 0 );
392        k:= k-1;
393      else
394        Add( d, 3 );
395        k:= k-4;
396      fi;
397    fi;
398  od;
399  return Reversed( d );
400end;
401
402##  construction of a basic spin rep. of 2S+(n) in characteristic p
403BasicSpinRepSymPos := function( n, p )
404    local z, M, k, i, kmodp, steps;
405    if not IsPosInt(n) or not IsInt(p) or n < 4 or not ( p = 0 or IsPrime( p ) ) then
406        return fail;
407    fi;
408    ## get the spin reps for 2S(4)
409    z := coeffS3(p);
410    if p = 0 then
411        M:= rec(
412          n := 2,
413          p := 0,
414          T := [ [ [ z ] ] ],
415          Sym := [~.T[1]],
416          Alt :=[]
417        );
418        M:= S2( M );
419    elif p = 2 then
420        M:= rec(
421          n := 2,
422          p := 2,
423          T := [ [ [ z ] ] ],
424          Sym := [~.T[1]],
425          Alt :=[]
426        );
427        M:= S1( S( M ) );
428    elif p = 3 then
429        M:= rec(
430          n := 3,
431          p := 3,
432          T := [ [ [ z ] ], [ [ z ] ] ],
433          Sym := [ [ [ z^2 ] ], ~.T[1] ],
434          Alt:=[ ~.Sym[1] ]
435        );
436        M:= S( M );
437    else # p>3
438        M:= rec(
439           n := 2,
440           p := p,
441           T := [ [ [ z ] ] ],
442           Sym := [ ~.T[1]],
443           Alt:=[]
444        );
445        M:= S2( M );
446    fi;
447    if n = 4 then return M; fi;
448    if ValueOption("Sym") <> true and ValueOption("Alt")<>true then Unbind(M.Sym); fi;
449    if ValueOption("Alt") <> true then Unbind(M.Alt); fi;
450    if p = 0 then
451        if n mod 2 = 0 then
452            k:= (n-4)/2;
453            for i in [ 1 .. k ] do
454                M:= S2( M );
455            od;
456        else
457            k:= (n-5)/2;
458            for i in [ 1 .. k ] do
459                M:= S2( M );
460            od;
461            # now M is a b.s.r. of 2S( n-1 )
462            M:= S( M );
463        fi;
464    elif p = 2 then
465        k:= 5;
466        while k <= n do
467            if k mod 2 = 1 then
468                M:= S( M );
469            else
470                M:= S1( M );
471            fi;
472            k:= k+1;
473        od;
474    else # p >= 3
475        steps:= spinsteps( n, p );
476        for k in steps do
477            if k = 0 then
478                M := S( M );
479            elif k = 1 then
480                M := S1( M );
481            elif k = 2 then
482                M := S2( M );
483            else
484                M := S3( M );
485            fi;
486        od;
487    fi;
488    Assert( 1, SanityCheckPos( M ) );
489    return M;
490end;
491
492BasicSpinRepSymNeg := function( n, p )
493  local T, S;
494  T := BasicSpinRepSymPos( n, p );
495  S := rec( n := T.n, p := T.p, T := coeffS3( p ) * T.T );
496  if IsBound( T.Sym ) then S.Sym := [ coeffS3( p )^(n-1) * T.Sym[1], S.T[1] ]; fi;
497  if IsBound( T.Alt ) then S.Alt := [ (-1)^Int((n-1)/2)*T.Alt[1], -T.Alt[2] ]; fi;
498  Assert( 1, SanityCheckNeg( S.T, p ) );
499  return S;
500end;
501
502BasicSpinRepSym := function( n, p, sign )
503  if sign in [ 1, '+', "+", 4 ] then return BasicSpinRepSymPos(n,p);
504  elif sign in [ -1, '-', "-", 2 ] then return BasicSpinRepSymNeg(n,p);
505  else Error("<sign> should be +1 or -1");
506  fi;
507end;
508
509##########################################################################
510##
511##  Method Installations
512##
513
514InstallGlobalFunction( BasicSpinRepresentationOfSymmetricGroup,
515function(arg)
516  local n, p, s, mats;
517  if Length(arg) < 1 then Error("Usage: BasicSpinRepresentationOfSymmetricGroup( <n>, <p>, <sign> );"); fi;
518  n := arg[1];
519  if Length(arg) < 2 then p := 3; else p := arg[2]; fi;
520  if Length(arg) < 3 then s := 1; else s := arg[3]; fi;
521  mats := BasicSpinRepSym(n,p,s).T;
522  if p = 2 then return List( mats, mat -> ImmutableMatrix( GF(p), mat ) );
523  elif p > 0 then return List( mats, mat -> ImmutableMatrix( GF(p^2), mat ) ); fi;
524  return mats;
525end );
526
527InstallMethod( SchurCoverOfSymmetricGroup,
528  "Use Lukas Maas's inductive construction of a basic spin rep",
529  [ IsPosInt, IsInt, IsInt ],
530function( n, p, s )
531  local mats, grp;
532
533  if p = 2 then return fail; fi; # need a faithful rep
534
535  if n < 4 then TryNextMethod(); fi;
536
537  mats := BasicSpinRepSym(n,p,s:Sym);
538
539  mats.Z := -mats.T[1]^0;
540
541  grp := Group( mats.Sym );
542
543  Assert( 3, Size( grp ) = 2*Factorial( n ) );
544  SetSize( grp, 2*Factorial(n) );
545
546  Assert( 3, Center( grp ) = Subgroup( grp, [ mats.Z ] ) );
547  SetCenter( grp, SubgroupNC( grp, [ mats.Z ] ) );
548
549  Assert( 3, IsAbelian( Center( grp ) ) );
550  SetIsAbelian( Center( grp ), true );
551
552  Assert( 3, Size( Center( grp ) ) = 2 );
553  SetSize( Center( grp ), 2 );
554
555  Assert( 3, AbelianInvariants( Center( grp ) ) = [ 2 ] );
556  SetAbelianInvariants( Center( grp ), [ 2 ] );
557
558  return grp;
559end );
560
561InstallMethod( DoubleCoverOfAlternatingGroup,
562  "Use Lukas Maas's inductive construction of a basic spin rep",
563  [ IsPosInt, IsInt ],
564function( n, p )
565  local mats, grp;
566
567  if p = 2 then return fail; fi; # need a faithful rep
568
569  mats := BasicSpinRepSym(n,p,1:Alt);
570
571  mats.Z := -mats.T[1]^0;
572
573  grp := Group( mats.Alt );
574
575  Assert( 3, Size( grp ) = Factorial( n ) );
576  SetSize( grp, Factorial(n) );
577
578  Assert( 3, Center( grp ) = Subgroup( grp, [ mats.Z ] ) );
579  SetCenter( grp, SubgroupNC( grp, [ mats.Z ] ) );
580
581  Assert( 3, IsAbelian( Center( grp ) ) );
582  SetIsAbelian( Center( grp ), true );
583
584  Assert( 3, Size( Center( grp ) ) = 2 );
585  SetSize( Center( grp ), 2 );
586
587  Assert( 3, AbelianInvariants( Center( grp ) ) = [ 2 ] );
588  SetAbelianInvariants( Center( grp ), [ 2 ] );
589
590  if n >= 5 then
591    Assert( 3, IsPerfectGroup( grp ) );
592    SetIsPerfectGroup( grp, true );
593  fi;
594
595  return grp;
596end );
597
598BasicSpinRepSymTest := function(n,p)
599  local mats, smtx, grp, sign;
600  for sign in [1,-1] do
601    mats := BasicSpinRepSym(n,p,sign).T;
602    if p > 0 then
603      smtx := GModuleByMats( mats, Field(Flat(mats)) );
604      Assert( 0, SMTX.IsAbsolutelyIrreducible( smtx ) );
605    fi;
606    grp := Group( mats.Sym );
607    if n > 4 or p <> 2 then
608    Assert( 0, Size( grp ) = 2*Factorial(n)/GcdInt(p,2) );
609    Assert( 0, Size( Center( grp ) ) = 2/GcdInt(p,2) );
610    Assert( 0, Size( DerivedSubgroup( grp ) ) = Factorial(n)/GcdInt(p,2) );
611    Assert( 0, IsSubgroup( DerivedSubgroup( grp ), Center( grp ) ) );
612    Assert( 0, AbelianInvariants( grp ) = [ 2 ] );
613    if n > 4 then Assert( 0, IsSimpleGroup( DerivedSubgroup( grp ) / Center( grp ) ) ); fi;
614    fi;
615    grp := Group( mats.Alt );
616    if n > 4 or p <> 2 then
617    Assert( 0, Size( grp ) = Factorial(n)/GcdInt(p,2) );
618    Assert( 0, Size( Center( grp ) ) = 2/GcdInt(p,2) );
619    Assert( 0, Size( DerivedSubgroup( grp ) ) = Factorial(n)/GcdInt(p,2) );
620    if n > 4 then Assert( 0, IsSimpleGroup( DerivedSubgroup( grp ) / Center( grp ) ) ); fi;
621    fi;
622  od;
623  return true;
624end;
625
626end );
627
628
629#############################################################################
630##
631##  Other method installations that do not require direct access to the
632##  inductive procedure.
633##
634
635
636#############################################################################
637##
638##  Convenience routines that supply default values.
639##
640
641InstallOtherMethod( SchurCoverOfSymmetricGroup,
642  "Sign=+1 by default",
643  [ IsPosInt, IsInt ],
644function( n, p )
645  return SchurCoverOfSymmetricGroup( n, p, 1 );
646end );
647
648InstallOtherMethod( SchurCoverOfSymmetricGroup,
649  "P=3, Sign=+1 by default",
650  [ IsPosInt ],
651function( n )
652  return SchurCoverOfSymmetricGroup( n, 3, 1 );
653end );
654
655InstallOtherMethod( DoubleCoverOfAlternatingGroup,
656  "P=3 by default",
657  [ IsPosInt ],
658function( n )
659  return DoubleCoverOfAlternatingGroup( n, 3 );
660end );
661
662#############################################################################
663##
664##  Quickly setup the standard epimorphisms
665##
666
667InstallMethod( EpimorphismSchurCover,
668  "Use library copy of double cover",
669  [ IsNaturalSymmetricGroup ],
670function( sym )
671  local dom, deg, cox, chr, grp, hom, img;
672  dom := MovedPoints( sym );
673  deg := Size( dom );
674  if deg < 4 then return IdentityMapping( sym ); fi;
675  cox := List( [1..deg-1], i -> (dom[i],dom[i+1]) );
676  Assert( 1, ForAll( cox, gen -> gen in sym ) );
677  #chr := First( [3,5,7], p -> 0 = deg mod p );
678  #if chr = fail then chr := 3; fi;
679  chr := 3; # appears to be the best choice regardless of deg
680  grp := SchurCoverOfSymmetricGroup( deg, chr, 1 );
681  img := [ Product( Reversed( cox ) ), cox[1] ];
682  if AssertionLevel() > 2 then
683    hom := GroupHomomorphismByImages( grp, sym, GeneratorsOfGroup( grp ), img );
684    Assert( 3, KernelOfMultiplicativeGeneralMapping( hom ) = Center( grp ) );
685  else
686    dom := RUN_IN_GGMBI; RUN_IN_GGMBI := true;
687    hom := GroupHomomorphismByImagesNC( grp, sym, GeneratorsOfGroup( grp ), img );
688    RUN_IN_GGMBI := dom;
689    SetKernelOfMultiplicativeGeneralMapping( hom, Center( grp ) );
690  fi;
691  return hom;
692end );
693
694InstallMethod( EpimorphismSchurCover,
695  "Use library copy of double cover",
696  [ IsNaturalAlternatingGroup ],
697function( alt )
698  local dom, deg, cox, chr, grp, hom, img;
699  dom := MovedPoints( alt );
700  deg := Size( dom );
701  if deg < 4 then return IdentityMapping( alt ); fi;
702  if deg in [6,7] then TryNextMethod(); fi;
703  cox := List( [1..deg-1], i -> (dom[i],dom[i+1]) );
704  Assert( 1, ForAll( [1..deg-2], i -> cox[i]*cox[i+1] in alt ) );
705  chr := 3;
706  grp := DoubleCoverOfAlternatingGroup( deg, chr );
707  img := [ Product( Reversed( cox{[1..2*Int((deg-1)/2)]} ) ), cox[deg-1]*cox[deg-2] ];
708  if AssertionLevel() > 2 then
709    hom := GroupHomomorphismByImages( grp, alt, GeneratorsOfGroup( grp ), img );
710    Assert( 3, KernelOfMultiplicativeGeneralMapping( hom ) = Center( grp ) );
711  else
712    dom := RUN_IN_GGMBI; RUN_IN_GGMBI := true;
713    hom := GroupHomomorphismByImagesNC( grp, alt, GeneratorsOfGroup( grp ), img );
714    RUN_IN_GGMBI := dom;
715    SetKernelOfMultiplicativeGeneralMapping( hom, Center( grp ) );
716  fi;
717  return hom;
718end );
719
720###########################################################################
721##
722##   Special cases just handled explicitly
723##
724
725InstallMethod( SchurCoverOfSymmetricGroup,
726  "Use explicit matrix reps for degrees 1,2,3",
727  [ IsPosInt, IsInt, IsInt ],
728  1,
729function( n, p, ignored )
730  local R;
731  if p = 0 then R := Integers; else R:=GF(p); fi;
732  if n = 1 then return TrivialSubgroup( GL(1,R) );
733  elif n = 2 and p<>2 then return Group( -One(GL(1,R)) );
734  elif n = 3 and p<>3 then return Group( [ [[0,1],[-1,-1]], [[0,1],[1,0]] ]*One(R) );
735  elif n = 2 and p = 2 then return Group( [[1,1],[0,1]]*One(R) ); # indecomposable, not irreducible
736  elif n = 3 and p = 3 then return Group( [ [[0,1],[-1,-1]], [[0,1],[1,0]] ]*One(R) ); # indecomposable, not irreducible
737  else TryNextMethod();
738  fi;
739end );
740
741InstallMethod( EpimorphismSchurCover,
742  "Use copy of AtlasRep's 6-fold cover",
743  [ IsNaturalAlternatingGroup ],
744  1,
745function( alt )
746  local dom, deg, cox, img, z, gen, grp, cen, hom;
747  dom := MovedPoints( alt );
748  deg := Size( dom );
749  if deg = 6 then
750    z := Z(25);
751    gen := [
752      [ [ z^ 0, z^16, z^22, z^ 8, z^ 8, z^13 ],
753        [ z^ 0, z^22, z^ 0, z^ 7, z^11, z^16 ],
754        [ z^11, z^ 7, z^ 0, z^ 6, z^10, z^ 7 ],
755        [ z^ 2, z^ 0, z^ 3, z* 0, z^18, z^21 ],
756        [ z^21, z^ 9, z^ 2, z^12, z^ 5, z^20 ],
757        [ z   , z^ 5, z^ 2, z^ 4, z^16, z^ 6 ] ],
758      [ [ z^18, z^23, z^ 0, z^ 2, z^23, z^17 ],
759        [ z^ 2, z^10, z^17, z* 0, z^ 0, z^18 ],
760        [ z^17, z^ 4, z^12, z^23, z^22, z^ 4 ],
761        [ z   , z^12, z   , z^18, z^11, z^ 2 ],
762        [ z^21, z^ 4, z^15, z^ 8, z^19, z* 0 ],
763        [ z^ 8, z^ 6, z^14, z^18, z^18, z^ 9 ] ] ];
764    grp := Group( gen );
765
766    Assert( 2, Size( grp ) = 6*5*4*3*2/2 * 6 );
767    SetSize( grp, 6*5*4*3*2/2 * 6 );
768
769    cen := SubgroupNC( grp, [ DiagonalMat( [ z^4, z^4, z^4, z^4, z^4, z^4 ] ) ] );
770
771    Assert( 1, Size( cen ) = 6 );
772    SetSize( cen, 6 );
773
774    Assert( 1, IsAbelian( cen ) );
775    SetIsAbelian( cen, true );
776
777    Assert( 1, AbelianInvariants( cen ) = [ 2, 3 ] );
778    SetAbelianInvariants( cen, [ 2, 3 ] );
779
780    Assert( 2, Center(grp) = cen );
781    SetCenter( grp, cen );
782  elif deg = 7 then
783    z := Z(25);
784    gen := [
785      [ [ z* 0, z^14, z^10, z^19, z^11, z^ 6 ],
786        [ z^19, z^12, z^ 9, z   , z^ 0, z    ],
787        [ z^ 8, z^18, z^10, z^ 2, z^20, z^15 ],
788        [ z^ 2, z^ 0, z^23, z^ 0, z^12, z^ 5 ],
789        [ z^20, z^ 8, z^20, z^23, z^16, z^ 0 ],
790        [ z^10, z^ 2, z^13, z^ 5, z^20, z^11 ] ],
791      [ [ z^ 7, z^ 6, z^10, z^23, z^ 6, z^ 0 ],
792        [ z^14, z^19, z^ 9, z^22, z^ 2, z^ 0 ],
793        [ z^10, z^16, z^17, z^15, z^17, z^14 ],
794        [ z^ 0, z^17, z^10, z^13, z   , z^ 6 ],
795        [ z^13, z^ 9, z^ 2, z^12, z^ 8, z^ 7 ],
796        [ z^ 8, z^ 8, z^16, z^23, z^ 4, z^19 ] ] ];
797
798    grp := Group( gen );
799
800    Assert( 2, Size( grp ) = 7*6*5*4*3*2/2 * 6 );
801    SetSize( grp, 7*6*5*4*3*2/2 * 6 );
802
803    cen := SubgroupNC( grp, [ DiagonalMat( [ z^4, z^4, z^4, z^4, z^4, z^4 ] ) ] );
804
805    Assert( 1, Size( cen ) = 6 );
806    SetSize( cen, 6 );
807
808    Assert( 1, IsAbelian( cen ) );
809    SetIsAbelian( cen, true );
810
811    Assert( 1, AbelianInvariants( cen ) = [ 2, 3 ] );
812    SetAbelianInvariants( cen, [ 2, 3 ] );
813
814    Assert( 2, Center(grp) = cen );
815    SetCenter( grp, cen );
816
817  else TryNextMethod();
818  fi;
819  cox := List( [1..deg-1], i -> (dom[i],dom[i+1]) );
820  img := [ Product( Reversed( cox{[1..2*Int((deg-1)/2)]} ) ), cox[deg-1]*cox[deg-2] ];
821  Assert( 1, ForAll( img, i -> i in alt ) );
822  if AssertionLevel() > 1 then
823    hom := GroupHomomorphismByImages( grp, alt, gen, img );
824    Assert( 2, KernelOfMultiplicativeGeneralMapping( hom ) = Center( grp ) );
825  else
826    hom := GroupHomomorphismByImagesNC( grp, alt, gen, img );
827    SetKernelOfMultiplicativeGeneralMapping( hom, Center( grp ) );
828  fi;
829  return hom;
830end );
831