1#############################################################################
2##
3##  This file is part of GAP, a system for computational discrete algebra.
4##  This file's authors include Thomas Breuer.
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 methods for fields consisting of cyclotomics.
12##
13##  Note that we must distinguish abelian number fields and fields
14##  that consist of cyclotomics.
15##  (The image of the natural embedding of the rational number field
16##  into a field of rational functions is of course an abelian number field
17##  but its elements are not cyclotomics since this would be a property given
18##  by their family.)
19##
20
21
22#############################################################################
23##
24#F  AbelianNumberFieldByReducedGaloisStabilizerInfo( <F>, <N>, <stab> )
25##
26##  The constructor `FieldByGenerators' calls this function.
27##  Since `CyclotomicField' and `AbelianNumberField' generate first the
28##  information about conductor and Galois stabilizer, it is useful for them
29##  to call this function instead of constructing generators and calling
30##  `FieldByGenerators', which would mean to construct <N> and <stab> again.
31##
32InstallGlobalFunction( AbelianNumberFieldByReducedGaloisStabilizerInfo,
33    function( F, N, stab )
34
35    local D, d;
36
37    D:= Objectify( NewType( CollectionsFamily( CyclotomicsFamily ),
38                                IsField
39                            and IsFiniteDimensional
40                            and IsAbelianNumberField
41                            and IsAttributeStoringRep ),
42                   rec() );
43
44    d:= Phi(N) / Length( stab );
45
46    SetIsCyclotomicField( D, Length( stab ) = 1 );
47    SetLeftActingDomain( D, F );
48    SetDegreeOverPrimeField( D, d );
49    SetGaloisStabilizer( D, stab );
50    SetConductor( D, N );
51    SetIsFinite( D, false );
52    SetSize( D, infinity );
53    SetDimension( D, d / DegreeOverPrimeField( F ) );
54    SetPrimeField( D, Rationals );
55    SetIsWholeFamily( D, false );
56
57    return D;
58end );
59
60
61#############################################################################
62##
63#F  CyclotomicField( <n> )  . . . . . . .  create the <n>-th cyclotomic field
64#F  CyclotomicField( <gens> )
65#F  CyclotomicField( <subfield>, <n> )
66#F  CyclotomicField( <subfield>, <gens> )
67##
68InstallGlobalFunction( CyclotomicField, function ( arg )
69
70    local F, subfield, xtension;
71
72    # If necessary split the arguments.
73    if     Length( arg ) = 1
74       and ( ( IsInt( arg[1] ) and 0 < arg[1] ) or IsList( arg[1] ) ) then
75
76      # CF( <n> ) or CF( <gens> )
77      subfield:= Rationals;
78      xtension:= arg[1];
79
80    elif     Length( arg ) = 2
81         and IsField( arg[1] )
82         and ( ( IsInt( arg[2] ) and 0 < arg[2] ) or IsList( arg[2] ) ) then
83
84      # `CF( <subfield>, <n> )' or `CF( <subfield>, <gens> )'
85      subfield:= arg[1];
86      xtension:= arg[2];
87
88    else
89      Error("usage: CF( <n> ) or CF( <subfield>, <gens> )");
90    fi;
91
92    # Replace generators by their conductor.
93    if not IsInt( xtension ) then
94      xtension:= Conductor( xtension );
95    fi;
96    if xtension mod 2 = 0 and xtension mod 4 <> 0 then
97      xtension:= xtension / 2;
98    fi;
99
100    # The subfield is given by `Rationals' denoting the prime field.
101    if subfield = Rationals then
102
103      # The standard field is required.  Look whether it is already stored.
104      # If not, generate it and return it
105      return CYCLOTOMIC_FIELDS( xtension );
106
107    elif IsAbelianNumberField( subfield ) then
108
109      # CF( subfield, N )
110      if xtension mod Conductor( subfield ) <> 0 then
111        Error( "<subfield> is not contained in CF( <xtension> )" );
112      fi;
113
114    else
115      Error( "<subfield> must be `Rationals' or an abelian number field" );
116    fi;
117
118    F:= AbelianNumberFieldByReducedGaloisStabilizerInfo( subfield,
119            xtension, [ 1 ] );
120
121    # Return the field.
122    return F;
123end );
124
125
126#############################################################################
127##
128#F  ReducedStabilizerInfo( <N>, <stabilizer> )
129##
130##  is a record with components `N' and `stabilizer',
131##  which are minimal with the property that they describe the same abelian
132##  number field as the input parameters <N> and <stabilizer>.
133##
134BindGlobal( "ReducedGaloisStabilizerInfo", function( N, stabilizer )
135
136    local d,
137          gens,
138          NN,
139          aut,
140          pos,
141          i,
142          p;
143
144    if N mod 2 = 0 and N mod 4 <> 0 then
145      N:= N / 2;
146    fi;
147    if N <= 2 then
148      return rec( N:= 1, stabilizer:= [ 1 ] );
149    fi;
150
151    stabilizer:= Set( stabilizer );
152    AddSet( stabilizer, 1 );
153
154    # Compute the elements of the group generated by `stabilizer'.
155    for d in stabilizer do
156      UniteSet( stabilizer, List( stabilizer, x -> ( x * d ) mod N ) );
157    od;
158
159    # reduce the pair `( N, stabilizer )' such that afterwards `N'
160    # describes the conductor of the required field;
161
162    gens:= GeneratorsPrimeResidues( N );
163    NN:= 1;
164    if gens.primes[1] = 2 then
165
166      if gens.exponents[1] < 3 then
167        if not gens.generators[1] in stabilizer then
168          NN:= NN * 4;
169        fi;
170
171      else
172
173        # the only case where `gens.generators[i]' is a list;
174        # it contains the generators corresponding to `**' and `*5';
175        # the first one is irrelevant for the conductor,
176        # except if also the other generator is contained.
177        if gens.generators[1][2] in stabilizer then
178          if not gens.generators[1][1] in stabilizer then
179            NN:= NN * 4;
180          fi;
181        else
182          NN:= NN * 4;
183          aut:= gens.generators[1][2];
184          while not aut in stabilizer do
185            aut:= ( aut ^ 2 ) mod N;
186            NN:= NN * 2;
187          od;
188        fi;
189      fi;
190      pos:= 2;
191    else
192      pos:= 1;
193    fi;
194
195    for i in [ pos .. Length( gens.primes ) ] do
196      p:= gens.primes[i];
197      if not gens.generators[i] in stabilizer then
198        NN:= NN * p;
199        aut:= ( gens.generators[i] ^ ( p - 1 ) ) mod N;
200        while not aut in stabilizer do
201          aut:= ( aut ^ p ) mod N;
202          NN:= NN * p;
203        od;
204      fi;
205    od;
206
207    N:= NN;
208    if N <= 2 then
209      stabilizer:= [ 1 ];
210      N:= 1;
211    else
212      stabilizer:= Set( List( stabilizer, x -> x mod N ) );
213    fi;
214
215    return rec( N:= N, stabilizer:= stabilizer );
216end );
217
218
219#############################################################################
220##
221#F  AbelianNumberField( <N>, <stab> ) . . . .  create an abelian number field
222##
223##  fixed field of the group generated by <stab> (prime residues modulo <N>)
224##  in the cyclotomic field with conductor <N>.
225##
226InstallGlobalFunction( AbelianNumberField, function ( N, stabilizer )
227
228    local pos,     # position in a list
229          F;       # the field, result
230
231    # Check the arguments.
232    if not ( IsInt( N ) and 0 < N and IsList( stabilizer ) ) then
233      Error( "<N> must be a positive integer, <stabilizer> a list" );
234    fi;
235
236    # Compute the conductor and the reduced stabilizer.
237    # Thus the Galois stabilizer component of the field will be minimal.
238    stabilizer := ReducedGaloisStabilizerInfo( N, stabilizer );
239    N          := stabilizer.N;
240    stabilizer := stabilizer.stabilizer;
241
242    if stabilizer = [ 1 ] then
243      return CyclotomicField( N );
244    fi;
245
246    # The standard field is required.  Look whether it is already stored.
247    atomic readonly ABELIAN_NUMBER_FIELDS do
248      if IsBound( ABELIAN_NUMBER_FIELDS[1][N] ) then
249          pos:= Position( ABELIAN_NUMBER_FIELDS[1][N], stabilizer );
250          if pos <> fail then
251              return ABELIAN_NUMBER_FIELDS[2][N][ pos ];
252          fi;
253      fi;
254    od;
255
256    # Construct the field.
257    F:= AbelianNumberFieldByReducedGaloisStabilizerInfo( Rationals,
258            N, stabilizer );
259
260    # Store the field.
261    atomic readwrite ABELIAN_NUMBER_FIELDS do
262       if not IsBound( ABELIAN_NUMBER_FIELDS[1][N] ) then
263           ABELIAN_NUMBER_FIELDS[1][N]:= MigrateObj([],ABELIAN_NUMBER_FIELDS);
264           ABELIAN_NUMBER_FIELDS[2][N]:= MigrateObj([],ABELIAN_NUMBER_FIELDS);
265       fi;
266       pos:= Position( ABELIAN_NUMBER_FIELDS[1][N], stabilizer );
267       if pos <> fail then
268           # someone else constructed it at the same time
269           # return their version
270           return ABELIAN_NUMBER_FIELDS[2][N][ pos ];
271       fi;
272       Add( ABELIAN_NUMBER_FIELDS[1][N], MakeImmutable(stabilizer) );
273       Add( ABELIAN_NUMBER_FIELDS[2][N], F );
274    od;
275    # Return the number field.
276    return F;
277end );
278
279
280#############################################################################
281##
282#M  ViewObj( <F> )  . . . . . . . . . . . . . .  view an abelian number field
283##
284InstallMethod( ViewObj,
285    "for abelian number field of cyclotomics",
286    [ IsAbelianNumberField and IsCyclotomicCollection ],
287    function( F )
288    if IsPrimeField( LeftActingDomain( F ) ) then
289      Print( "NF(", Conductor( F ), ",",
290              GaloisStabilizer( F ), ")" );
291    else
292      Print( "AsField( ", LeftActingDomain( F ),
293             ", NF(", Conductor( F ), ",", GaloisStabilizer( F ), ") )" );
294    fi;
295    end );
296
297InstallMethod( ViewObj,
298    "for cyclotomic field of cyclotomics",
299    [ IsCyclotomicField and IsCyclotomicCollection ],
300    function( F )
301    if IsPrimeField( LeftActingDomain( F ) ) then
302      Print( "CF(", Conductor( F ), ")" );
303    else
304      Print( "AsField( ", LeftActingDomain( F ),
305             ", CF(", Conductor( F ), ") )" );
306    fi;
307    end );
308
309
310#############################################################################
311##
312#M  PrintObj( <F> ) . . . . . . . . . . . . . . print an abelian number field
313##
314InstallMethod( PrintObj,
315    "for abelian number field of cyclotomics",
316    [ IsAbelianNumberField and IsCyclotomicCollection ],
317    function( F )
318    if IsPrimeField( LeftActingDomain( F ) ) then
319      Print( "NF(", Conductor( F ), ",",
320              GaloisStabilizer( F ), ")" );
321    else
322      Print( "AsField( ", LeftActingDomain( F ),
323             ", NF(", Conductor( F ), ",", GaloisStabilizer( F ), ") )" );
324    fi;
325    end );
326
327InstallMethod( PrintObj,
328    "for cyclotomic field of cyclotomics",
329    [ IsCyclotomicField and IsCyclotomicCollection ],
330    function( F )
331    if IsPrimeField( LeftActingDomain( F ) ) then
332      Print( "CF(", Conductor( F ), ")" );
333    else
334      Print( "AsField( ", LeftActingDomain( F ),
335             ", CF(", Conductor( F ), ") )" );
336    fi;
337    end );
338
339
340#############################################################################
341##
342#M  String( <F> ) . . . . . . . . . . . . . string of an abelian number field
343##
344InstallMethod( String,
345    "for abelian number field of cyclotomics",
346    [ IsAbelianNumberField and IsCyclotomicCollection ],
347    function( F )
348    if IsPrimeField( LeftActingDomain( F ) ) then
349      return Concatenation( "NF(", String( Conductor( F ) ), ",",
350                            String( GaloisStabilizer( F ) ), ")" );
351    else
352      return Concatenation( "AsField( ", String( LeftActingDomain( F ) ),
353                            ", NF(", String( Conductor( F ) ), ",",
354                            String( GaloisStabilizer( F ) ), ") )" );
355    fi;
356    end );
357
358InstallMethod( String,
359    "for cyclotomic field of cyclotomics",
360    [ IsCyclotomicField and IsCyclotomicCollection ],
361    function( F )
362
363    local n;
364
365    n:= Conductor( F );
366
367    if IsPrimeField( LeftActingDomain( F ) ) then
368      if   n = 1 then
369        return "Rationals";
370      elif n = 4 then
371        return "GaussianRationals";
372      else
373        return Concatenation( "CF(", String( n ), ")" );
374      fi;
375    elif n = 4 then
376      return Concatenation( "AsField( ", String( LeftActingDomain( F ) ),
377                            ", GaussianRationals )" );
378    else
379      return Concatenation( "AsField( ", String( LeftActingDomain( F ) ),
380                            ", CF(", String( n ), ") )" );
381    fi;
382    end );
383
384
385#############################################################################
386##
387#M  \=( <F1>, <F2> )  . . . . . . . . . . comparison of abelian number fields
388#M  \<( <F1>, <F2> )  . . . . . . . . . . comparison of abelian number fields
389##
390##  <F1> is smaller than <F2> if and only if <F1> has a smaller conductor,
391##  or it has the same conductor as <F2> but its Galois stabilizer component
392##  is smaller.
393##
394InstallMethod( \=,
395    "for two abelian number fields",
396    IsIdenticalObj,
397    [ IsAbelianNumberField, IsAbelianNumberField ],
398    function ( F1, F2 )
399    return     Conductor( F1 ) = Conductor( F2 )
400           and GaloisStabilizer( F1 ) = GaloisStabilizer( F2 );
401    end );
402
403InstallMethod( \<,
404    "for two abelian number fields",
405    IsIdenticalObj,
406    [ IsAbelianNumberField, IsAbelianNumberField ],
407    function ( F1, F2 )
408    return    Conductor( F1 ) < Conductor( F2 )
409           or (     Conductor( F1 ) = Conductor( F2 )
410                and GaloisStabilizer( F1 ) < GaloisStabilizer( F2 ) );
411    end );
412
413
414#############################################################################
415##
416#M  \in( <z>, <F> ) . . . .  test if <z> lies in the abelian number field <F>
417##
418##  check whether <z> is a cyclotomic with conductor contained in the
419##  conductor of <F>, and that <z> is fixed by `GaloisStabilizer( <F> )'.
420##
421InstallMethod( \in,
422    "for cyclotomic and abelian number field",
423    IsElmsColls,
424    [ IsCyc, IsAbelianNumberField and IsCyclotomicCollection ],
425    function ( z, F )
426    return     Conductor( F ) mod Conductor( z ) = 0
427           and ForAll( GaloisStabilizer( F ), x -> GaloisCyc( z, x ) = z );
428    end );
429
430InstallMethod( \in,
431    "for cyclotomic and cyclotomic field",
432    IsElmsColls,
433    [ IsCyc, IsCyclotomicField and IsCyclotomicCollection ],
434    function ( z, F )
435    return Conductor( F ) mod Conductor( z ) = 0;
436    end );
437
438
439#############################################################################
440##
441#M  Intersection2( <F>, <G> ) . . . . . intersection of abelian number fields
442##
443InstallMethod( Intersection2,
444    "for two cyclotomic fields of cyclotomics",
445    IsIdenticalObj,
446    [ IsCyclotomicField and IsCyclotomicCollection,
447      IsCyclotomicField and IsCyclotomicCollection ],
448    function ( F, G )
449    return CyclotomicField( GcdInt( Conductor( F ), Conductor( G ) ) );
450    end );
451
452InstallMethod( Intersection2,
453    "for cyclotomic field and abelian number field",
454    IsIdenticalObj,
455    [ IsCyclotomicField and IsCyclotomicCollection,
456      IsAbelianNumberField and IsCyclotomicCollection ],
457    function ( F, G )
458
459    # intersection of cyclotomic field `F = CF(N)' with number field `G';
460    # replace `N' by its g.c.d. with the conductor of `G',
461    # and then take the elements of `GaloisStabilizer( G )' modulo `N'.
462    # (If a reduction is necessary, `NF' will do.)
463
464    F:= Gcd( Conductor( F ), Conductor( G ) );
465    return AbelianNumberField( F, Set( List( GaloisStabilizer( G ),
466                                             x -> x mod F ) ) );
467    end );
468
469InstallMethod( Intersection2,
470    "for abelian number field and cyclotomic field",
471    IsIdenticalObj,
472    [ IsAbelianNumberField and IsCyclotomicCollection,
473      IsCyclotomicField and IsCyclotomicCollection ],
474    function ( G, F )
475
476    # intersection of cyclotomic field `F = CF(N)' with number field `G';
477    # replace `N' by its g.c.d. with the conductor of `G',
478    # and then take the elements of `GaloisStabilizer( G )' modulo `N'.
479    # (If a reduction is necessary, `NF' will do.)
480
481    F:= Gcd( Conductor( F ), Conductor( G ) );
482    return AbelianNumberField( F, Set( List( GaloisStabilizer( G ),
483                                             x -> x mod F ) ) );
484    end );
485
486InstallMethod( Intersection2,
487    "for two abelian number fields",
488    IsIdenticalObj,
489    [ IsAbelianNumberField and IsCyclotomicCollection,
490      IsAbelianNumberField and IsCyclotomicCollection ],
491    function ( F, G )
492
493    local i, j, N, stab, stabF, stabG;
494
495    # first compute `N' where `CF(N)' contains the intersection;
496    # reduce the elements of the stabilizers modulo `N', i.e. intersect
497    # `F' and `G' with `CF(N)';
498    # then compute the corresponding stabilizer, i.e. the product of
499    # stabilizers.
500    N:= GcdInt( Conductor( F ), Conductor( G ) );
501    stabF:= Set( List( GaloisStabilizer( F ), x -> x mod N ) );
502    stabG:= Set( List( GaloisStabilizer( G ), x -> x mod N ) );
503    stab:= [];
504    for i in stabF do
505      for j in stabG do
506        AddSet( stab, ( i * j ) mod N );
507      od;
508    od;
509
510    # (If a reduction is necessary, `NF' will do.)
511    return AbelianNumberField( N, stab );
512    end );
513
514
515#############################################################################
516##
517#M  GeneratorsOfDivisionRing( <F> ) .  field gens. of an abelian number field
518##
519InstallMethod( GeneratorsOfDivisionRing,
520    "for abelian number field of cyclotomics",
521    [ IsAbelianNumberField and IsCyclotomicCollection ],
522    function( F )
523    local e;
524    e:= E( Conductor( F ) );
525    return [ Sum( GaloisStabilizer( F ), y -> GaloisCyc( e, y ) ) ];
526    end );
527
528
529#############################################################################
530##
531#M  Conductor( <F> )  . . . . . . . . .  conductor of an abelian number field
532##
533InstallOtherMethod( Conductor,
534    "for abelian number field of cyclotomics",
535    [ IsAbelianNumberField and IsCyclotomicCollection ],
536    F -> Conductor( GeneratorsOfField( F ) ) );
537
538
539#############################################################################
540##
541#M  Subfields( <F> )  . . . . . . . . .  subfields of an abelian number field
542##
543##  The Galois group of an abelian number field is abelian,
544##  so the subfields are in bijection with the conjugacy classes of subgroups
545##  of the Galois group.
546##
547InstallMethod( Subfields,
548    "for abelian number field of cyclotomics",
549    [ IsAbelianNumberField and IsCyclotomicCollection ],
550    function( F )
551    local n, stab;
552    n:= Conductor( F );
553    stab:= GaloisStabilizer( F );
554    return Set( List( ConjugacyClassesSubgroups( GaloisGroup( F ) ),
555                      x -> AbelianNumberField( n, Union( stab,
556                             List( GeneratorsOfGroup( Representative( x ) ),
557                                   y -> ExponentOfPowering( y ) ) ) ) ) );
558    end );
559
560
561#############################################################################
562##
563#M  PrimeField( <F> ) . . . . . . . . . . . . . . for an abelian number field
564##
565InstallMethod( PrimeField,
566    "for abelian number field of cyclotomics",
567    [ IsAbelianNumberField and IsCyclotomicCollection ],
568    F -> Rationals );
569
570
571#############################################################################
572##
573#M  FieldExtension( <subfield>, <poly> )  . .  extend an abelian number field
574##
575InstallOtherMethod( FieldExtension,
576    "for field of cyclotomics, and univ. polynomial (degree <= 2)",
577#T CollPoly
578    [ IsAbelianNumberField and IsCyclotomicCollection,
579      IsLaurentPolynomial ],
580    function( F, poly )
581
582    local coeffs, root;
583
584    coeffs:= CoefficientsOfLaurentPolynomial( poly );
585    coeffs:= ShiftedCoeffs( coeffs[1], coeffs[2] );
586
587    if not IsSubset( F, coeffs ) then
588      Error( "all coefficients of <poly> must lie in <F>" );
589    elif 3 < Length( coeffs ) then
590      TryNextMethod();
591    elif Length( coeffs ) <= 1 then
592      Error( "<poly> must have degree at least 1" );
593    elif Length( coeffs ) = 2 then
594
595      # `poly' is a linear polynomial.
596      root:= - coeffs[1] / coeffs[2];
597      F:= AsField( F, F );
598
599    else
600
601      # `poly' has degree 2.
602      # The roots of `a*x^2 + b*x + c' are
603      # $\frac{ -b \pm \sqrt{ b^2 - 4ac } }{2a}$.
604      root:= coeffs[2]^2 - 4 * coeffs[1] * coeffs[3];
605      if not IsRat( root ) then
606        TryNextMethod();
607      fi;
608      root:= ( ER( root ) - coeffs[2] ) / ( 2 * coeffs[3] );
609      F:= AsField( F, FieldByGenerators(
610                       Concatenation( GeneratorsOfField( F ), [ root ] ) ) );
611
612    fi;
613
614    # Store the defining polynomial, and a root of it in the extension field.
615    SetDefiningPolynomial( F, poly );
616    SetRootOfDefiningPolynomial( F, root );
617
618    return F;
619    end );
620
621
622#############################################################################
623##
624#M  Conjugates( <L>, <K>, <z> )
625##
626InstallMethod( Conjugates,
627    "for two abelian number fields of cyclotomics, and cyclotomic",
628    IsCollsXElms,
629    [ IsAbelianNumberField and IsCyclotomicCollection,
630      IsAbelianNumberField and IsCyclotomicCollection, IsCyc ],
631    function( L, K, z )
632
633    local N, gal, gens, conj, pnt;
634
635    N:= Conductor( L );
636
637    # automorphisms of the conductor
638    gal:= PrimeResidues( N );
639
640    if not IsPrimeField( K ) then
641
642      # take only the subgroup of `gal' that fixes the subfield pointwise
643      gens:= GeneratorsOfField( K );
644      gal:= Filtered( gal,
645                      x -> ForAll( gens, y -> GaloisCyc( y, x ) = y ) );
646    fi;
647
648    # get representatives of cosets of the Galois stabilizer
649    conj:= [];
650    gens:= GaloisStabilizer( L );
651    while gal <> [] do
652      pnt:= gal[1];
653      Add( conj, GaloisCyc( z, pnt ) );
654      SubtractSet( gal, List( gens, x -> ( x * pnt ) mod N ) );
655    od;
656
657    return conj;
658    end );
659
660InstallMethod( Conjugates,
661    "for cycl. field of cyclotomics, ab. number field, and cyclotomic",
662    IsCollsXElms,
663    [ IsCyclotomicField and IsCyclotomicCollection,
664      IsAbelianNumberField and IsCyclotomicCollection, IsCyc ],
665    function( L, K, z )
666
667    local conj, Kgens, i;
668
669    if not z in L then
670      Error( "<z> must lie in <L>" );
671    fi;
672
673    if IsPrimeField( K ) then
674      conj:= List( PrimeResidues( Conductor( L ) ),
675                   i -> GaloisCyc( z, i ) );
676    else
677      conj:= [];
678      Kgens:= GeneratorsOfField( K );
679      for i in PrimeResidues( Conductor( L ) ) do
680        if ForAll( Kgens, x -> GaloisCyc( x, i ) = x ) then
681          Add( conj, GaloisCyc( z, i ) );
682        fi;
683      od;
684    fi;
685
686    return conj;
687    end );
688
689
690#############################################################################
691##
692#M  Norm( <L>, <K>, <z> )
693##
694InstallMethod( Norm,
695    "for two abelian number fields of cyclotomics, and cyclotomic",
696    IsCollsXElms,
697    [ IsAbelianNumberField and IsCyclotomicCollection,
698      IsAbelianNumberField and IsCyclotomicCollection, IsCyc ],
699    function( L, K, z )
700    local N, gal, gens, result, pnt;
701
702    N:= Conductor( L );
703
704    # automorphisms of the conductor
705    gal:= PrimeResidues( N );
706
707    if not IsPrimeField( K ) then
708
709      # take only the subgroup of `gal' that fixes the subfield pointwise
710      gens:= GeneratorsOfField( K );
711      gal:= Filtered( gal,
712                      x -> ForAll( gens, y -> GaloisCyc( y, x ) = y ) );
713    fi;
714
715    # get representatives of cosets of `GaloisStabilizer( L )'
716    result:= 1;
717    gens:= GaloisStabilizer( L );
718    while gal <> [] do
719      pnt:= gal[1];
720      result:= result * GaloisCyc( z, pnt );
721      SubtractSet( gal, List( gens, x -> ( x * pnt ) mod N ) );
722    od;
723
724    return result;
725    end );
726
727InstallMethod( Norm,
728    "for cycl. field of cyclotomics, ab. number field, and cyclotomic",
729    IsCollsXElms,
730    [ IsCyclotomicField and IsCyclotomicCollection,
731      IsAbelianNumberField and IsCyclotomicCollection, IsCyc ],
732    function( L, K, z )
733
734    local i, result, Kgens;
735
736    result:= 1;
737    if IsPrimeField( K ) then
738      for i in PrimeResidues( Conductor( L ) ) do
739        result:= result * GaloisCyc( z, i );
740      od;
741    else
742      Kgens:= GeneratorsOfField( K );
743      for i in PrimeResidues( Conductor( L ) ) do
744        if ForAll( Kgens, x -> GaloisCyc( x, i ) = x ) then
745          result:= result * GaloisCyc( z, i );
746        fi;
747      od;
748    fi;
749
750    return result;
751    end );
752
753
754#############################################################################
755##
756#M  Trace( <L>, K>, <z> )
757##
758InstallMethod( Trace,
759    "for two abelian number fields of cyclotomics, and cyclotomic",
760    IsCollsXElms,
761    [ IsAbelianNumberField and IsCyclotomicCollection,
762      IsAbelianNumberField and IsCyclotomicCollection, IsCyc ],
763    function( L, K, z )
764    local N, gal, gens, result, pnt;
765
766    N:= Conductor( L );
767
768    # automorphisms of the conductor
769    gens:= GeneratorsOfField( K );
770    gal:= PrimeResidues( N );
771
772    if not IsPrimeField( K ) then
773
774      # take only the subgroup of `gal' that fixes the subfield pointwise
775      gal:= Filtered( gal,
776                      x -> ForAll( gens, y -> GaloisCyc( y, x ) = y ) );
777    fi;
778
779    # get representatives of cosets of `GaloisStabilizer( L )'
780    result:= 0;
781    gens:= GaloisStabilizer( L );
782    while gal <> [] do
783      pnt:= gal[1];
784      result:= result + GaloisCyc( z, pnt );
785      SubtractSet( gal, List( gens, x -> ( x * pnt ) mod N ) );
786    od;
787
788    return result;
789    end );
790
791InstallMethod( Trace,
792    "for cycl. field of cyclotomics, ab. number field, and cyclotomic",
793    IsCollsXElms,
794    [ IsCyclotomicField and IsCyclotomicCollection,
795      IsAbelianNumberField and IsCyclotomicCollection, IsCyc ],
796    function( L, K, z )
797    local i, result, Kgens;
798    result:= 0;
799    if IsPrimeField( K ) then
800      for i in PrimeResidues( Conductor( L ) ) do
801        result:= result + GaloisCyc( z, i );
802      od;
803    else
804      Kgens:= GeneratorsOfField( K );
805      for i in PrimeResidues( Conductor( L ) ) do
806        if ForAll( Kgens, x -> GaloisCyc( x, i ) = x ) then
807          result:= result + GaloisCyc( z, i );
808        fi;
809      od;
810    fi;
811
812    return result;
813    end );
814
815
816#############################################################################
817##
818#F  ZumbroichBase( <n>, <m> )
819##
820##  returns the set of exponents `e' for which `E(n)^e' belongs to the
821##  (generalized) Zumbroich base of the cyclotomic field $Q_n$,
822##  viewed as vector space over $Q_m$.
823##
824##  *Note* that for $n \equiv 2 \bmod 4$ we have
825##  `ZumbroichBase( <n>, 1 ) = 2 * ZumbroichBase( <n>/2, 1 )' but
826##  `List( ZumbroichBase(  <n>, 1  ), x -> E(  <n>  )^x ) =
827##   List( ZumbroichBase( <n>/2, 1 ), x -> E( <n>/2 )^x )'.
828##
829InstallGlobalFunction( ZumbroichBase, function( n, m )
830
831    local nn, base, basefactor, factsn, exponsn, factsm, exponsm, primes,
832          p, pos, i, k;
833
834    if not n mod m = 0 then
835      Error( "<m> must be a divisor of <n>" );
836    fi;
837
838    factsn:= Factors(Integers, n );
839    primes:= Set( factsn );
840    exponsn:= List( primes, x -> 0 );   # Product(List( [1..Length(primes)],
841                                        #         x->primes[i]^exponsn[i]))=n
842    p:= factsn[1];
843    pos:= 1;
844    for i in factsn do
845      if i > p then
846        p:= i;
847        pos:= pos + 1;
848      fi;
849      exponsn[ pos ]:= exponsn[ pos ] + 1;
850    od;
851
852    factsm:= Factors(Integers, m );
853    exponsm:= List( primes, x -> 0 );    # Product(List( [1..Length(primes)],
854                                         #         x->primes[i]^exponsm[i]))=m
855    if m <> 1 then
856      p:= factsm[1];
857      pos:= Position( primes, p );
858      for i in factsm do
859        if i > p then
860          p:= i;
861          pos:= Position( primes, p );
862        fi;
863        exponsm[ pos ]:= exponsm[ pos ] + 1;
864      od;
865    fi;
866
867    base:= [ 0 ];
868    if n = 1 then
869      return base;
870    fi;
871
872    if primes[1] = 2 then
873
874      # special case: $J_{k,2} = \{ 0, 1 \}$
875      if exponsm[1] = 0 then exponsm[1]:= 1; fi;    # $J_{0,2} = \{ 0 \}$
876
877      nn:= n / 2^( exponsm[1] + 1 );
878
879      for k in [ exponsm[1] .. exponsn[1] - 1 ] do
880        Append( base, base + nn );
881        nn:= nn / 2;
882      od;
883      pos:= 2;
884    else
885      pos:= 1;
886    fi;
887
888    for i in [ pos .. Length( primes ) ] do
889
890      if m mod primes[i] <> 0 then
891        basefactor:= [ 1 .. primes[i] - 1 ] * ( n / primes[i] );
892        base:= Concatenation( List( base, x -> x + basefactor ) );
893        exponsm[i]:= 1;
894      fi;
895
896      basefactor:= [ - ( primes[i] - 1 ) / 2 .. ( primes[i] - 1 ) / 2 ]
897                     * n / primes[i]^exponsm[i];
898
899      for k in [ exponsm[i] .. exponsn[i] - 1 ] do
900        basefactor:= basefactor / primes[i];
901        base:= Concatenation( List( base, x -> x + basefactor ) );
902      od;
903    od;
904    return Set( List( base, x -> x mod n ) );
905end );
906
907
908#############################################################################
909##
910#F  LenstraBase( <n>, <stabilizer>, <super>, <m> )
911##
912##  returns a list of lists of integers; each list indexing the exponents of
913##  an orbit of a subgroup of <stabilizer> on <n>-th roots of unity.
914##
915##  <super> is a list representing a supergroup of <stabilizer> which
916##  shall act consistently with the action of <stabilizer>, i.e., each orbit
917##  of <supergroup> is a union of orbits of <stabilizer>.
918##
919##  ( Shall there be a test if this is possible ? )
920##
921##  <m> is a positive integer.  The basis described by the returned list is
922##  an integral basis over the cyclotomic field $\Q_m$.
923##
924##  *Note* that the elements are in general not sets, since the first element
925##  is always an element of `ZumbroichBase( <n>, <m> )';
926##  this property is used by `NF' and `Coefficients'.
927##
928##  *Note* that <stabilizer> must not contain the stabilizer of a proper
929##  cyclotomic subfield of the <n>-th cyclotomic field.
930##
931##  We proceed as follows.
932##
933##  Let $n'$ be the biggest divisor of $n$ coprime to $m$.
934##  First choose an integral basis $B$ for the extension $\Q_{n'} / \Q$
935##  (equivalently, for $\Q_{n} / \Q_{n/n'}$).
936##  For each element of $B$ choose an integral basis for $\Q_{n/n'} / \Q_m$,
937##  namely a transversal of $E_m$ in $E_{n/n'}$ where $E_n$ denotes the
938##  group of $n$-th roots of unity.
939##
940##  The products of elements in these bases form an integral $\Q_m$-basis
941##  of $\Q_n$.
942##  Now we choose the bases in such a way that ...
943##
944InstallGlobalFunction( LenstraBase, function( n, stabilizer, supergroup, m )
945
946    local i,
947          k,
948          factors,           # factors of `n'
949          primes,            # set of prime divisors of `n'
950          coprimes,          # set of prime divisors of `n' coprime to `m'
951          nprime,            # biggest divisor of `n' coprime to `m'
952          NN,                # squarefree part of `n'
953          zumb,              # exponents of roots in the basis of `CF(n)'
954          N2,                # 2-part of `n'
955          No,                # odd part of `n'
956          transversal,       # roots in basis of `CF(n/nprime) / CF(m)',
957                             # written as `n'-th roots
958          orbits,
959          pnt,
960          orb,
961          d,
962          ppnt,
963          ord,
964          a,
965          neworbits,
966          rep,
967          super,
968          H1;
969
970    # We may assume that either `m' is odd or $4$ divides `m'.
971    if m mod 4 = 2 then
972      m:= m / 2;
973    fi;
974
975    factors  := Factors(Integers, n );
976    primes   := Set( factors );
977    coprimes := Filtered( primes, x -> m mod x <> 0 );
978    nprime   := Product( Filtered( factors, x -> m mod x <> 0 ) );
979
980    NN:= Product( coprimes );
981    zumb:= List( ZumbroichBase( nprime, 1 ), x -> x * ( n / nprime ) );
982    transversal := List( ZumbroichBase( n / nprime, m ), x -> x * nprime );
983    stabilizer:= Set( stabilizer );
984    orbits:= [];
985
986    if nprime = NN then
987
988      # $n'$ is squarefree.
989      # We have a normal basis, `stabilizer' acts on `zumb',
990      # we do not consider equivalence classes since they are all trivial,
991      # and `supergroup' is obsolete since `zumb' describes a normal basis.
992
993      # *Note* that if $n'$ is even then `zumb' does not consist of
994      # at least `NN'-th roots!
995
996      while 0 < Length( zumb ) do
997
998        # Compute the orbit of `stabilizer' of a point in `zumb'.
999        pnt:= zumb[1];
1000
1001        # For each root in `transversal', compute the orbit of `n'-th roots
1002        # under `stabilizer'.
1003        neworbits:= List( transversal,
1004                          root -> List( stabilizer,
1005                                        x -> ( root + pnt ) * x mod n ) );
1006        SubtractSet( zumb, neworbits[1] );
1007        Append( orbits, neworbits );
1008
1009      od;
1010
1011    else
1012
1013      # Let $d(i)$ be the largest squarefree number whose square divides the
1014      # order of $e_{n'}^i$, that is $n' / \gcd( n', i )$.
1015      # Define an equivalence relation on the set $S$ of at least `NN'-th
1016      # roots of unity.
1017      # $i$ and $j$ are equivalent iff $n'$ divides $( i - j ) d(i)$.  The
1018      # equivalence class $(i)$ of $i$ is
1019      # $\{ i + k n' / d(i) ; 0 \leq k \< d(i) \}$.
1020
1021      # For the case that `NN' is even, replace those roots in $S$ with order
1022      # not divisible by 4 by their negatives.
1023      # (Equivalently\: Replace *all* elements in $S$ by their negatives.)
1024
1025      # If 8 does not divide $n'$ and $n' \not= 4$, `zumb' is a subset of $S$,
1026      # the intersection of $(i)$ with `zumb' is of order $\varphi( d(i) )$,
1027      # it is a basis for the $Z$--submodule spanned by $(i)$.
1028      # Furthermore, the minimality of `n' yields that `stabilizer' acts fixed
1029      # point freely on the set of equivalence classes.
1030
1031      # More exactly, fixed points occur exactly if there is an element `s' in
1032      # `stabilizer' which is congruent $-1$ modulo `N2' and congruent $+1$
1033      # modulo `No'.
1034
1035      # The base is constructed as follows\:
1036      #
1037      # Until all classes are touched:
1038      # 1. Take a point `pnt' (in `zumb').
1039      # 2. Choose a maximal linear independent set `pnts' in the equivalence
1040      #    class of `pnt' (the intersection of the class with `zumb').
1041      # 3. Take the `stabilizer'--orbits of `pnts' as base elements;
1042      #    remove the touched equivalence classes.
1043      # 4. For the representatives `rep' in `supergroup'\:
1044      #    If `rep' maps `pnt' to an equivalence class that was not yet
1045      #    touched, take the `stabilizer'--orbits of the images of `pnts'
1046      #    under `rep' as base elements;
1047      #    remove the touched equivalence classes.
1048
1049      # Compute nontriv. representatives of `supergroup' over `stabilizer'.
1050      super:= Difference( supergroup, stabilizer );
1051      supergroup:= [];
1052      while 0 < Length( super ) do
1053        pnt:= super[1];
1054        Add( supergroup, pnt );
1055        SubtractSet( super, List( stabilizer, x -> ( x * pnt ) mod n ) );
1056      od;
1057
1058      # Compute 2-part and odd part of $n'$.
1059      N2 := 1;
1060      No := nprime;
1061      while No mod 2 = 0 do
1062        N2:= N2 * 2;
1063        No:= No / 2;
1064      od;
1065
1066      # Compute the subgroup `H1' of `stabilizer' that acts fixed point
1067      # freely on the set of equivalence classes,
1068      # and the element `a' that (if exists) fixes some classes pointwise.
1069      H1 := [];
1070      a  := 0;
1071      for k in stabilizer do
1072        if k mod 4 = 1 then
1073          Add( H1, k );
1074        elif ( k -1 ) mod No = 0
1075             and ( ( k + 1 ) mod N2 = 0 or ( k + 1 - N2/2 ) mod N2 = 0 ) then
1076          a:= k;
1077        fi;
1078      od;
1079      if a = 0 then
1080        H1:= stabilizer;
1081      fi;
1082
1083      while 0 < Length( zumb ) do
1084
1085        neworbits:= [];
1086        pnt:= zumb[1];
1087        d:= 1;
1088        ord:= n / GcdInt( n, pnt );
1089        for i in coprimes do
1090          if ord mod i^2 = 0 then d:= d * i; fi;
1091        od;
1092
1093        if ( a = 0 ) or ( ord mod 8 = 0 ) then
1094
1095          # No `H1'-orbit can be fixed by `a'.
1096
1097          for k in [ 0 .. d-1 ] do
1098
1099            # Loop over the equivalence class of `pnt',
1100            # consider only the points in `zumb'.
1101
1102            ppnt:= pnt + k * n / d;
1103            if ppnt in zumb then
1104
1105              orb:= List( stabilizer, x -> ( ppnt * x ) mod n );
1106              Append( neworbits,
1107                      List( transversal,
1108                            root -> List( stabilizer,
1109                                       x -> ( root + ppnt ) * x mod n ) ) );
1110
1111            fi;
1112          od;
1113
1114        elif ord mod 4 = 0 then
1115
1116          # `a' maps each point in the orbit of `H1' to its inverse,
1117          # we ignore all these points.
1118          orb:= List( stabilizer, x -> ( pnt * x ) mod n );
1119
1120        else
1121
1122          # The orbit of `H1' is pointwise fixed by `a'.
1123          for k in [ 0 .. d-1 ] do
1124            ppnt:= pnt + k * n / d;
1125            if ppnt in zumb then
1126
1127              orb:= List( H1, x -> ( ppnt * x ) mod n );
1128              Append( neworbits,
1129                      List( transversal,
1130                            root -> List( H1,
1131                                       x -> ( root + ppnt ) * x mod n ) ) );
1132
1133            fi;
1134          od;
1135
1136        fi;
1137
1138        # Remove the equivalence classes of all new points from `zumb'.
1139        for pnt in orb do
1140          SubtractSet( zumb, List( [ 0 .. d-1 ],
1141                                   k -> ( pnt + k * n / d ) mod n ) );
1142        od;
1143
1144        Append( orbits, neworbits );
1145
1146        # use `supergroup'\:
1147        # Is there a point in `zumb' that is not equivalent to
1148        # `( pnt * rep ) mod nprime' ?
1149        # (Note that the factor group `supergroup / stabilizer' acts on the
1150        # set of unions of orbits with equivalent elements.)
1151
1152        for rep in supergroup do
1153
1154          # is there an `x' in `zumb' that is equivalent to `pnt * rep' ?
1155          if ForAny( zumb, x -> ( ( x - pnt * rep ) * d ) mod n = 0 ) then
1156            Append( orbits, List( neworbits,
1157                              x -> List( x, y -> (y*rep) mod n ) ) );
1158            for ppnt in orbits[ Length( orbits ) ] do
1159              SubtractSet( zumb, List( [ 0..d-1 ],
1160                              k -> ( ppnt + k * n / d ) mod n ) );
1161            od;
1162          fi;
1163        od;
1164
1165      od;
1166    fi;
1167
1168    # Return the list of orbits.
1169    return orbits;
1170end );
1171
1172
1173#############################################################################
1174##
1175#R  IsCanonicalBasisAbelianNumberFieldRep( <B> )
1176##
1177##  The canonical basis of a number field is defined to be a Lenstra basis
1178##  in the case that the subfield is `Rationals'.
1179#T extend this to the case where the subfield is a cyclotomic field!
1180##  In all other cases a normal basis is chosen.
1181##
1182DeclareRepresentation( "IsCanonicalBasisAbelianNumberFieldRep",
1183    IsAttributeStoringRep,
1184    [ "coeffslist", "coeffsmat", "lenstrabase", "conductor" ] );
1185
1186
1187#############################################################################
1188##
1189#R  IsCanonicalBasisCyclotomicFieldRep( <B> )
1190##
1191##  The canonical basis of a field extension $F / K$ for a cyclotomic field
1192##  $F$ is the Zumbroich basis if $K$ is a cyclotomic field.
1193##  Otherwise it is a normal basis.
1194##
1195DeclareRepresentation( "IsCanonicalBasisCyclotomicFieldRep",
1196    IsCanonicalBasisAbelianNumberFieldRep,
1197    [ "zumbroichbase" ] );
1198
1199
1200#############################################################################
1201##
1202#M  CanonicalBasis( <F> )
1203##
1204##  The canonical basis of a number field is defined to be a Lenstra basis
1205##  in the case that the subfield is a cyclotomic field.
1206##
1207##  In all other cases a normal basis is chosen.
1208##
1209InstallMethod( CanonicalBasis,
1210    "for abelian number field of cyclotomics",
1211    [ IsAbelianNumberField and IsCyclotomicCollection ],
1212    function ( F )
1213
1214    local N,             # conductor of `F'
1215          k,
1216          lenst,
1217          i,
1218          B,
1219          BB,
1220          normalbase,
1221          subbase,
1222          m,
1223          j,
1224          C,
1225          coeffsmat,
1226          val,
1227          l;
1228
1229    # Make the basis object.
1230    B:= Objectify( NewType( FamilyObj( F ),
1231                                IsFiniteBasisDefault
1232                            and IsCanonicalBasis
1233                            and IsCanonicalBasisAbelianNumberFieldRep ),
1234                   rec() );
1235    SetUnderlyingLeftModule( B, F );
1236
1237    if IsCyclotomicField( LeftActingDomain( F ) ) then
1238
1239      # Compute the standard Lenstra basis and the `coeffslist' component.
1240      # If `GaloisStabilizer( F )' acts fixed point freely on the
1241      # equivalence classes we must change from the Zumbroich basis to a
1242      # `GaloisStabilizer( F )'-normal basis,
1243      # and afterwards choose coefficients with respect to that basis.
1244      # In the case of fixed points, only the subgroup `H1' of index 2 in
1245      # `GaloisStabilizer( F )' acts fixed point freely;
1246      # we change to a `H1'-normal basis, and afterwards choose coefficients.
1247
1248      # For this basis <B> we want a component `coeffslist' such that
1249      # in the special case of a field over the rationals we have
1250      # `CoeffsCyc( z, N ){ <B>!.coeffslist } = Coefficients( <B>, z )'.
1251
1252      N:= Conductor( F );
1253      lenst:= LenstraBase( N, GaloisStabilizer( F ), GaloisStabilizer( F ),
1254                           Conductor( LeftActingDomain( F ) ) );
1255
1256      # Fill in additional components.
1257      SetBasisVectors( B, List( lenst,
1258                                x -> Sum( List( x, y -> E(N)^y ) ) ) );
1259      B!.coeffslist  := MakeImmutable(List( lenst, x -> x[1] + 1 ));
1260      B!.lenstrabase := MakeImmutable(lenst);
1261      B!.conductor   := N;
1262#T better compute basis vectors only if necessary
1263#T (in the case of a normal basis the vectors are of course known ...)
1264      SetIsIntegralBasis( B, true );
1265
1266    else
1267
1268      # A basis of an extension of a number field is a normal basis.
1269#T handle extensions of cycl. fields specially!!
1270      # Coefficients can be computed using a tensor form basis for that
1271      # the base change relative to the Lenstra basis (relative to the
1272      # rationals) is computed.
1273
1274      # Let $(v_1, \ldots, v_m)$ denote a basis of `subfield' and
1275      #     $(w_1, \ldots, w_k)$ denote a basis of `F';
1276      # Define $u_{i+m(j-1)} = v_i w_j$.  Then $(u_l; 1\leq l\leq mk)$
1277      # is a basis of `F' over the rationals.
1278      # First change from the Lenstra basis to this basis; the matrix is `C'.
1279
1280      normalbase:= NormalBase( F );
1281      subbase:= BasisVectors( CanonicalBasis( LeftActingDomain( F ) ) );
1282      BB:= CanonicalBasis( AbelianNumberField( Conductor( F ),
1283                                               GaloisStabilizer( F ) ) );
1284      m:= Length( subbase );
1285      k:= Length( normalbase );
1286      N:= Conductor( normalbase );
1287      C:= [];
1288      for j in normalbase do
1289
1290        # Compute the Lenstra basis coefficients.
1291        for i in subbase do
1292          Add( C, Coefficients( BB, i*j ) );
1293        od;
1294
1295      od;
1296      C:= C^(-1);
1297
1298      # Let $(c_1, \ldots, c_{mk})$ denote the coefficients with respect
1299      # to the new base.  To achieve `<coeffs> \* normalbase = <z>' we have
1300      # to take $\sum_{i=1}^m c_{i+m(j-1)} v_i$ as $j$--th coefficient:
1301
1302      coeffsmat:= [];
1303      for i in [ 1 .. Length( C ) ] do     # for all rows
1304        coeffsmat[i]:= [];
1305        for j in [ 1 .. k ] do
1306          val:= 0;
1307          for l in [ 1 .. m ] do
1308            val:= val + C[i][ m*(j-1)+l ] * subbase[l];
1309          od;
1310          coeffsmat[i][j]:= val;
1311        od;
1312      od;
1313
1314      # Multiplication of a Lenstra basis coefficient vector with
1315      # `coeffsmat' means first changing to the base of products
1316      # $v_i w_j$ and then summation over the parts of the $v_i$.
1317
1318      SetIsNormalBasis( B, true );
1319      SetBasisVectors( B, normalbase );
1320      B!.coeffslist := BB!.coeffslist;
1321      B!.coeffsmat  := coeffsmat;
1322      B!.conductor  := N;
1323
1324    fi;
1325
1326    # Return the canonical basis.
1327    return B;
1328    end );
1329
1330
1331#############################################################################
1332##
1333#M  Basis( <F> )
1334##
1335InstallMethod( Basis,
1336    "for abelian number field of cyclotomics (delegate to `CanonicalBasis')",
1337    [ IsAbelianNumberField and IsCyclotomicCollection ],
1338    CANONICAL_BASIS_FLAGS,
1339    CanonicalBasis );
1340
1341
1342#############################################################################
1343##
1344#M  Coefficients( <B>, <z> )  . . . . .  for canon. basis of ab. number field
1345##
1346InstallMethod( Coefficients,
1347    "for canonical basis of abelian number field, and cyclotomic",
1348    IsCollsElms,
1349    [ IsBasis and IsCanonicalBasis and IsCanonicalBasisAbelianNumberFieldRep,
1350      IsCyc ],
1351    function ( B, z )
1352    local V,
1353          F,
1354          coeffs,
1355          n,
1356          m,
1357          zumb,
1358          NN,
1359          Em;
1360
1361    if not z in UnderlyingLeftModule( B ) then
1362      return fail;
1363    fi;
1364
1365    V:= UnderlyingLeftModule( B );
1366    F:= LeftActingDomain( V );
1367
1368    # The information about the standard Lenstra basis coefficients
1369    # is stored in `B!.coeffslist'.
1370    if   IsPrimeField( F ) then
1371
1372      # Take the relevant sublist, this suffices for extensions
1373      # of the rationals.
1374      coeffs:= CoeffsCyc( z, B!.conductor ){ B!.coeffslist };
1375
1376    elif IsCyclotomicField( F ) then
1377
1378      # `B' is an integral basis of an extension of a cyclotomic field $\Q_m$,
1379      # the coefficient of the root $\zeta$ is
1380      # $\sum_{\eta\in\B_m} a_{\eta\zeta} \eta$.
1381
1382      coeffs:= CoeffsCyc( z, B!.conductor );
1383      n:= Conductor( V );
1384      m:= Conductor( F );
1385      zumb:= CanonicalBasis( F )!.zumbroichbase;
1386      NN:= n/m;
1387      Em:= E(m);
1388      coeffs:= List( B!.coeffslist,
1389                j->Sum( zumb, k->coeffs[ ((k*NN+j-1) mod n )+1 ]*Em^k ) );
1390
1391    fi;
1392
1393    if IsBound( B!.coeffsmat ) then
1394
1395      # Compute the coefficients with respect to the field extension.
1396      coeffs:= CoeffsCyc( z, B!.conductor ){ B!.coeffslist } * B!.coeffsmat;
1397
1398    fi;
1399
1400    # Return the coefficients list.
1401    return coeffs;
1402    end );
1403
1404
1405#############################################################################
1406##
1407#M  FieldByGenerators( <cycscoll> )
1408#M  FieldByGenerators( <F>, <cycscoll> )
1409##
1410InstallOtherMethod( FieldByGenerators,
1411    "for collection of cyclotomics",
1412    [ IsCyclotomicCollection ],
1413    function( gens )
1414
1415    local N, stab;
1416
1417    N:= Conductor( gens );
1418
1419    # Handle trivial cases.
1420    if   N = 1 then
1421      return Rationals;
1422    elif N = 4 then
1423      return GaussianRationals;
1424    fi;
1425
1426    # Compute the reduced stabilizer info.
1427    stab:= Filtered( PrimeResidues( N ),
1428                     x -> ForAll( gens,
1429                                  gen -> GaloisCyc( gen, x ) = gen ) );
1430
1431    # Construct and return the field.
1432    return AbelianNumberFieldByReducedGaloisStabilizerInfo( Rationals, N,
1433               stab );
1434    end );
1435
1436InstallMethod( FieldByGenerators,
1437    "for field and collection, both collections of cyclotomics",
1438    IsIdenticalObj,
1439    [ IsField and IsCyclotomicCollection, IsCyclotomicCollection ],
1440    function( F, gens )
1441
1442    local N, stab;
1443
1444    N:= Conductor( gens );
1445
1446    if F = Rationals then
1447
1448      # Handle trivial cases.
1449      if   N = 1 then
1450        return Rationals;
1451      elif N = 4 then
1452        return GaussianRationals;
1453      fi;
1454
1455    else
1456      N:= Lcm( N, Conductor( F ) );
1457      gens:= Concatenation( gens, GeneratorsOfField( F ) );
1458    fi;
1459
1460    # Compute the reduced stabilizer info.
1461    stab:= Filtered( PrimeResidues( N ),
1462                     x -> ForAll( gens,
1463                                  gen -> GaloisCyc( gen, x ) = gen ) );
1464
1465    # Construct and return the field.
1466    return AbelianNumberFieldByReducedGaloisStabilizerInfo( F, N, stab );
1467    end );
1468
1469
1470#############################################################################
1471##
1472#M  DefaultFieldByGenerators( <cycscoll> )
1473##
1474InstallMethod( DefaultFieldByGenerators,
1475    "for collection of cyclotomics",
1476    [ IsCyclotomicCollection ],
1477    gens -> CyclotomicField( Conductor( gens ) ) );
1478
1479
1480#############################################################################
1481##
1482#M  CanonicalBasis( <F> )
1483##
1484##  The canonical basis of a field extension $F / K$ for a cyclotomic field
1485##  $F$ is the Zumbroich basis if $K$ is a cyclotomic field.
1486##  Otherwise it is the first normal basis.
1487##
1488InstallMethod( CanonicalBasis,
1489    "for cyclotomic field of cyclotomics",
1490    [ IsCyclotomicField and IsCyclotomicCollection ],
1491    function( F )
1492
1493    local n,
1494          m,
1495          B,
1496          subfield,
1497          zumb,
1498          subvectors,
1499          vectors,
1500          i, j, k, l,
1501          C,
1502          coeffsmat,
1503          val;
1504
1505    n:= Conductor( F );
1506
1507    B:= Objectify( NewType( FamilyObj( F ),
1508                                IsFiniteBasisDefault
1509                            and IsCanonicalBasis
1510                            and IsCanonicalBasisCyclotomicFieldRep ),
1511                   rec() );
1512    SetUnderlyingLeftModule( B, F );
1513    B!.conductor:= n;
1514
1515    subfield:= LeftActingDomain( F );
1516
1517    if IsCyclotomicField( subfield ) then
1518
1519      SetIsIntegralBasis( B, true );
1520
1521      # Construct the Zumbroich basis.
1522      B!.zumbroichbase := MakeImmutable(ZumbroichBase( n, Conductor( subfield ) ));
1523
1524    else
1525
1526      # Compute a normal basis.
1527
1528      # Let $(v_1, \ldots, v_m)$ denote `Basis( F.field ).vectors' and
1529      #     $(w_1, \ldots, w_k)$ denote `vectors'.
1530      # Define $u_{i+m(j-1)} = v_i w_j$.  Then $(u_l; 1\leq l\leq mk)$
1531      # is a $Q$-basis of `F'.  First change from the Zumbroich basis to
1532      # this basis; the matrix is `C'\:
1533
1534      zumb       := ZumbroichBase( n, 1 ) + 1;
1535      subvectors := BasisVectors( Basis( subfield ) );
1536      vectors    := NormalBase( F );
1537      m:= Length( subvectors );
1538      k:= Length( vectors );
1539      C:= [];
1540      for j in vectors do
1541        for i in subvectors do
1542          Add( C, CoeffsCyc( i*j, n ){ zumb } );
1543        od;
1544      od;
1545      C:= C^(-1);
1546
1547      # Let $(c_1, \ldots, c_{mk})$ denote the coefficients with respect
1548      # to the new basis.
1549      # To achieve `<coeffs> \* BasisVectors( <B> ) = <z>' we have
1550      # to take $\sum_{i=1}^m c_{i+m(j-1)} v_i$ as $j$--th coefficient\:
1551
1552      coeffsmat:= [];
1553      for i in [ 1 .. Length( C ) ] do     # for all rows
1554        coeffsmat[i]:= [];
1555        for j in [ 1 .. k ] do
1556          val:= 0;
1557          for l in [ 1 .. m ] do
1558            val:= val + C[i][ m*(j-1)+l ] * subvectors[l];
1559          od;
1560          coeffsmat[i][j]:= val;
1561        od;
1562      od;
1563
1564      SetBasisVectors( B, vectors );
1565      SetIsNormalBasis( B, true );
1566
1567      B!.zumbroichbase := MakeImmutable(zumb - 1);
1568      B!.coeffsmat     := MakeImmutable(coeffsmat);
1569
1570    fi;
1571
1572    # Return the basis.
1573    return B;
1574    end );
1575
1576
1577#############################################################################
1578##
1579#M  BasisVectors( <B> )
1580##
1581InstallMethod( BasisVectors,
1582    "for canon. basis of cyclotomic field of cyclotomics",
1583    [ IsBasis and IsCanonicalBasis and IsCanonicalBasisCyclotomicFieldRep ],
1584    function( B )
1585    local e;
1586    # Basis vectors are bound if the subfield is not a cycl. field.
1587#T ??
1588    e:= E( Conductor( UnderlyingLeftModule( B ) ) );
1589    return List( B!.zumbroichbase, x -> e^x );
1590    end );
1591
1592
1593#############################################################################
1594##
1595#M  Coefficients( <B>, <z> )  . . . . . . . . for canon. basis of cycl. field
1596##
1597InstallMethod( Coefficients,
1598    "for canonical basis of cyclotomic field, and cyclotomic",
1599    IsCollsElms,
1600    [ IsBasis and IsCanonicalBasis and IsCanonicalBasisCyclotomicFieldRep,
1601      IsCyc ],
1602    function( B, z )
1603    local N,
1604          coeffs,
1605          F,
1606          m,
1607          zumb,
1608          NN,
1609          Em;
1610
1611    F:= UnderlyingLeftModule( B );
1612    if not z in F then return fail; fi;
1613
1614    N:= B!.conductor;
1615
1616    # Get the Zumbroich basis representation of <z> in `N'-th roots.
1617    coeffs:= CoeffsCyc( z, N );
1618    if coeffs = fail then return fail; fi;
1619
1620    F:= LeftActingDomain( F );
1621
1622    if   IsPrimeField( F ) then
1623
1624      # Get the Zumbroich basis coefficients (basis $B_{n,1}$)
1625      coeffs:= coeffs{ B!.zumbroichbase + 1 };
1626
1627    elif IsCyclotomicField( F ) then
1628
1629      # Get the Zumbroich basis coefficients (basis $B_{n,m}$) directly.
1630      m:= Conductor( F );
1631      zumb:= CanonicalBasis( F )!.zumbroichbase;
1632      NN:= N/m;
1633      Em:= E(m);
1634      coeffs:= List( B!.zumbroichbase,
1635                     j->Sum( zumb, k->coeffs[ ((k*NN+j) mod N )+1 ]*Em^k ) );
1636
1637    else
1638
1639      # The subfield is not a cyclotomic field.
1640      # The necessary information is stored in `B!.coeffsmat'.
1641      coeffs:= coeffs{ B!.zumbroichbase + 1 } * B!.coeffsmat;
1642
1643    fi;
1644
1645    # Return the list of coefficients.
1646    return coeffs;
1647    end );
1648
1649
1650#############################################################################
1651##
1652#V  Cyclotomics . . . . . . . . . . . . . . . . . .  field of all cyclotomics
1653##
1654InstallValue( Cyclotomics, Objectify( NewType(
1655    CollectionsFamily( CyclotomicsFamily ),
1656    IsField and IsAttributeStoringRep ),
1657    rec() ) );
1658SetName( Cyclotomics, "Cyclotomics" );
1659SetLeftActingDomain( Cyclotomics, Rationals );
1660SetIsFiniteDimensional( Cyclotomics, false );
1661SetIsFinite( Cyclotomics, false );
1662SetIsWholeFamily( Cyclotomics, true );
1663SetDegreeOverPrimeField( Cyclotomics, infinity );
1664SetDimension( Cyclotomics, infinity );
1665SetRepresentative(Cyclotomics, 0);
1666
1667
1668#############################################################################
1669##
1670##  Automorphisms of abelian number fields
1671##
1672
1673
1674#############################################################################
1675##
1676#R  IsANFAutomorphismRep( <obj> )
1677##
1678DeclareRepresentation( "IsANFAutomorphismRep",
1679    IsAttributeStoringRep, [ "galois" ] );
1680
1681
1682#############################################################################
1683##
1684#P  IsANFAutomorphism( <obj> )
1685##
1686DeclareSynonym( "IsANFAutomorphism", IsANFAutomorphismRep
1687    and IsFieldHomomorphism
1688    and IsMapping
1689    and IsBijective );
1690
1691
1692#############################################################################
1693##
1694#M  ExponentOfPowering( <map> )
1695##
1696InstallMethod( ExponentOfPowering,
1697    "for an ANFAutomorphism",
1698    [ IsMapping and IsANFAutomorphismRep ],
1699    map -> map!.galois );
1700
1701InstallMethod( ExponentOfPowering,
1702    "for an identity mapping",
1703    [ IsMapping and IsOne ],
1704    map -> 1 );
1705
1706InstallMethod( ExponentOfPowering,
1707    "for a mapping (check whether it is the identity mapping)",
1708    [ IsMapping ],
1709    function( map )
1710    if IsOne( map ) then
1711      return 1;
1712    else
1713      TryNextMethod();
1714    fi;
1715    end );
1716
1717
1718#############################################################################
1719##
1720#F  ANFAutomorphism( <F>, <k> )  . .  automorphism of an abelian number field
1721##
1722InstallGlobalFunction( ANFAutomorphism, function ( F, k )
1723
1724    local galois, aut;
1725
1726    # check the arguments
1727    if not ( IsAbelianNumberField(F) and IsCyclotomicCollection(F) ) then
1728      Error("<F> must be an abelian number field consisting of cyclotomics");
1729    elif not IsRat( k ) then
1730      Error( "<k> must be an integer" );
1731    fi;
1732    if not IsInt( k ) then
1733#T this is a hack ...
1734      k:= k mod Conductor( F );
1735    fi;
1736    if Gcd( Conductor( F ), k ) <> 1 then
1737      Error( "<k> must be coprime to the conductor of <F>" );
1738    fi;
1739
1740    # Let $F / K$ be a field extension where $Q_n$ is the conductor of $F$;
1741    # let $S(F)$ be the group of those prime residues mod $n$ that fix $F$
1742    # pointwise.  The Galois group of $F / K$ is in natural correspondence
1743    # to $S(K) / S(F)$.  Thus each automorphism of $F / K$ corresponds to
1744    # a coset $c$ of $S(K)$, and it acts on $F$ like each element of $c$.
1745    # The automorphism `ANFAutomorphism( F/K, k )' maps $x\in F / K$ to
1746    # `GaloisCyc( <x>, k )'.
1747
1748    # Choose the smallest representative ...
1749    galois:= Set(List(GaloisStabilizer( F ), x->x*k mod Conductor( F )))[1];
1750    if galois = 1 then
1751      return IdentityMapping( F );
1752    fi;
1753
1754    # make the mapping
1755    aut:= Objectify( TypeOfDefaultGeneralMapping( F, F,
1756                             IsSPGeneralMapping
1757                         and IsANFAutomorphism ),
1758                     rec() );
1759
1760    aut!.galois:= galois;
1761
1762    return aut;
1763end );
1764
1765
1766#############################################################################
1767##
1768#M  \=( <aut1>, <aut2> )  . . . .  for automorphisms of abelian number fields
1769#M  \=( <id>, <aut> ) . . . . . .  for automorphisms of abelian number fields
1770#M  \=( <aut>, <id> ) . . . . . .  for automorphisms of abelian number fields
1771##
1772InstallMethod( \=,
1773    "for two ANF automorphisms",
1774    IsIdenticalObj,
1775    [ IsFieldHomomorphism and IsANFAutomorphismRep,
1776      IsFieldHomomorphism and IsANFAutomorphismRep ],
1777    function ( aut1, aut2 )
1778    return     Source( aut1 ) = Source( aut2 )
1779           and aut1!.galois = aut2!.galois;
1780    end );
1781
1782InstallMethod( \=,
1783    "for identity mapping and ANF automorphism",
1784    IsIdenticalObj,
1785    [ IsMapping and IsOne,
1786      IsFieldHomomorphism and IsANFAutomorphismRep ],
1787    function ( id, aut )
1788    return     Source( id ) = Source( aut )
1789           and aut!.galois = 1;
1790    end );
1791
1792InstallMethod( \=,
1793    "for ANF automorphism and identity mapping",
1794    IsIdenticalObj,
1795    [ IsFieldHomomorphism and IsANFAutomorphismRep,
1796      IsMapping and IsOne ],
1797    function ( aut, id )
1798    return     Source( id ) = Source( aut )
1799           and aut!.galois = 1;
1800    end );
1801
1802
1803#############################################################################
1804##
1805#M  \<( <aut1>, <aut2> )  . . . .  for automorphisms of abelian number fields
1806##
1807InstallMethod( \<,
1808    "for two ANF automorphisms",
1809    IsIdenticalObj,
1810    [ IsFieldHomomorphism and IsANFAutomorphismRep,
1811      IsFieldHomomorphism and IsANFAutomorphismRep ],
1812    function ( aut1, aut2 )
1813    return    Source( aut1 ) < Source( aut2 )
1814           or (     Source( aut1 ) = Source( aut2 )
1815                and aut1!.galois < aut2!.galois );
1816    end );
1817
1818InstallMethod( \<,
1819    "for identity mapping and ANF automorphism",
1820    IsIdenticalObj,
1821    [ IsMapping and IsOne,
1822      IsFieldHomomorphism and IsANFAutomorphismRep ],
1823    function ( id, aut )
1824    return    Source( id ) < Source( aut )
1825           or (     Source( id ) = Source( aut )
1826                and 1 < aut!.galois );
1827    end );
1828
1829InstallMethod( \<,
1830    "for ANF automorphism and identity mapping",
1831    IsIdenticalObj,
1832    [ IsFieldHomomorphism and IsANFAutomorphismRep,
1833      IsMapping and IsOne ],
1834    function ( aut, id )
1835    return Source( aut ) < Source( id );
1836    end );
1837
1838
1839#############################################################################
1840##
1841#M  ImageElm( <aut>, <cyc> )  . .  for automorphisms of abelian number fields
1842##
1843InstallMethod( ImageElm,
1844    "for ANF automorphism and scalar",
1845    FamSourceEqFamElm,
1846    [ IsFieldHomomorphism and IsANFAutomorphismRep, IsCyc ],
1847    function ( aut, elm )
1848    return GaloisCyc( elm, aut!.galois );
1849    end );
1850
1851
1852#############################################################################
1853##
1854#M  ImagesElm( <aut>, <cyc> ) . .  for automorphisms of abelian number fields
1855##
1856InstallMethod( ImagesElm,
1857    "for ANF automorphism and scalar",
1858    FamSourceEqFamElm,
1859    [ IsFieldHomomorphism and IsANFAutomorphismRep, IsScalar ],
1860    function ( aut, elm )
1861    return [ GaloisCyc( elm, aut!.galois ) ];
1862    end );
1863
1864
1865#############################################################################
1866##
1867#M  ImagesSet( <aut>, <field> ) .  for automorphisms of abelian number fields
1868##
1869InstallMethod( ImagesSet,
1870    "for ANF automorphism and field",
1871    CollFamSourceEqFamElms,
1872    [ IsFieldHomomorphism and IsANFAutomorphismRep, IsField ],
1873    function ( aut, F )
1874    return F;
1875    end );
1876
1877
1878#############################################################################
1879##
1880#M  ImagesRepresentative( <aut>, <cyc> )  . . for autom. of ab. number fields
1881##
1882InstallMethod( ImagesRepresentative,
1883    "for ANF automorphism and scalar",
1884    FamSourceEqFamElm,
1885    [ IsFieldHomomorphism and IsANFAutomorphismRep, IsScalar ],
1886    function ( aut, elm )
1887    return GaloisCyc( elm, aut!.galois );
1888    end );
1889
1890
1891#############################################################################
1892##
1893#M  PreImageElm( <aut>, <cyc> ) . . . . . . . for autom. of ab. number fields
1894##
1895InstallMethod( PreImageElm,
1896    "for ANF automorphism and scalar",
1897    FamRangeEqFamElm,
1898    [ IsFieldHomomorphism and IsBijective and IsANFAutomorphismRep,
1899      IsScalar ],
1900    function ( aut, elm )
1901    return GaloisCyc( elm, ( 1 / aut!.galois )
1902                           mod Conductor( Range( aut ) ) );
1903    end );
1904
1905
1906#############################################################################
1907##
1908#M  PreImagesElm( <aut>, <cyc> )  . . . . . . for autom. of ab. number fields
1909##
1910InstallMethod( PreImagesElm,
1911    "for ANF automorphism and scalar",
1912    FamRangeEqFamElm,
1913    [ IsFieldHomomorphism and IsANFAutomorphismRep, IsScalar ],
1914    function ( aut, elm )
1915    return [ GaloisCyc( elm, ( 1 / aut!.galois )
1916                             mod Conductor( Range( aut ) ) ) ];
1917    end );
1918
1919
1920#############################################################################
1921##
1922#M  PreImagesSet( <aut>, <field> )  . . . . . for autom. of ab. number fields
1923##
1924InstallMethod( PreImagesSet,
1925    "for ANF automorphism and scalar",
1926    CollFamRangeEqFamElms,
1927    [ IsFieldHomomorphism and IsANFAutomorphismRep, IsField ],
1928    function ( aut, F )
1929    return F;
1930    end );
1931
1932
1933#############################################################################
1934##
1935#M  PreImagesRepresentative( <aut>, <cyc> ) . for autom. of ab. number fields
1936##
1937InstallMethod( PreImagesRepresentative,
1938    "for ANF automorphism and scalar",
1939    FamRangeEqFamElm,
1940    [ IsFieldHomomorphism and IsANFAutomorphismRep, IsScalar ],
1941    function ( aut, elm )
1942    return GaloisCyc( elm, ( 1 / aut!.galois )
1943                           mod Conductor( Range( aut ) ) );
1944    end );
1945
1946
1947#############################################################################
1948##
1949#M  CompositionMapping2( <aut2>, <aut1> ) . . for autom. of ab. number fields
1950##
1951InstallMethod( CompositionMapping2,
1952    "for two ANF automorphisms",
1953    FamSource1EqFamRange2,
1954    [ IsFieldHomomorphism and IsANFAutomorphismRep,
1955      IsFieldHomomorphism and IsANFAutomorphismRep ],
1956    function ( aut1, aut2 )
1957    return ANFAutomorphism( Source( aut1 ), aut1!.galois * aut2!.galois );
1958    end );
1959
1960
1961#############################################################################
1962##
1963#M  InverseGeneralMapping( <aut> )  . . . . . for autom. of ab. number fields
1964##
1965InstallOtherMethod( InverseGeneralMapping,
1966    "for ANF automorphism",
1967    [ IsFieldHomomorphism and IsANFAutomorphismRep ],
1968    aut -> ANFAutomorphism( Source( aut ), 1 / aut!.galois ) );
1969
1970
1971#############################################################################
1972##
1973#M  \^( <aut>, <n> )  . . . . . . . . . . . . for autom. of ab. number fields
1974##
1975InstallMethod( \^,
1976    "for ANF automorphism and integer",
1977    [ IsFieldHomomorphism and IsANFAutomorphismRep, IsInt ],
1978    function ( aut, i )
1979    return ANFAutomorphism( Source( aut ), aut!.galois^i );
1980    end );
1981
1982
1983#############################################################################
1984##
1985#M  PrintObj( <aut> ) . . . . . . . . . . . . for autom. of ab. number fields
1986##
1987InstallMethod( PrintObj,
1988    "for ANF automorphism",
1989    [ IsFieldHomomorphism and IsANFAutomorphismRep ],
1990    function ( aut )
1991    Print( "ANFAutomorphism( ", Source( aut ), ", ", aut!.galois, " )" );
1992    end );
1993
1994
1995#############################################################################
1996##
1997#M  GaloisGroup( <F> )  . . . . . . . Galois group of an abelian number field
1998##
1999##  The required group is a factor group of the Galois group $G$
2000##  of the enveloping cyclotomic field.
2001##  So the group $U$ generated by the actions of the generators of $G$ on <F>
2002##  is the Galois group of <F>, viewed as field over the rationals.
2003##
2004##  If <F> is a field over a proper extension of the rationals then we take
2005##  the pointwise stabilizer of the subfield in $U$.
2006##
2007InstallMethod( GaloisGroup,
2008    "for abelian number field ",
2009    [ IsAbelianNumberField ],
2010    function( F )
2011    local group;
2012
2013    group:= GroupByGenerators( List( Flat(
2014                    GeneratorsPrimeResidues( Conductor( F ) ).generators ),
2015                        x -> ANFAutomorphism( F, x ) ),
2016                IdentityMapping( F ) );
2017
2018    if not IsPrimeField( LeftActingDomain( F ) ) then
2019      group:= Stabilizer( group,
2020                  GeneratorsOfField( LeftActingDomain( F ) ), OnTuples );
2021    fi;
2022
2023    return group;
2024end );
2025
2026
2027InstallMethod( Representative, [IsCyclotomicField], f->0);
2028