1#############################################################################
2##
3#W  rcwamap.gi                GAP4 Package `RCWA'                 Stefan Kohl
4##
5##  This file contains implementations of methods and functions for computing
6##  with rcwa mappings of
7##
8##    - the ring Z of the integers, of
9##    - the ring Z^2, of
10##    - the semilocalizations Z_(pi) of the ring of integers, and of
11##    - the polynomial rings GF(q)[x] in one variable over a finite field.
12##
13##  See the definitions given in the file rcwamap.gd.
14##
15#############################################################################
16
17#############################################################################
18##
19#F  RCWAInfo . . . . . . . . . . . . . . . . . . set info level of `InfoRCWA'
20##
21InstallGlobalFunction( RCWAInfo,
22                       function ( n ) SetInfoLevel( InfoRCWA, n ); end );
23
24#############################################################################
25##
26#S  Implications between the categories of rcwa mappings. ///////////////////
27##
28#############################################################################
29
30InstallTrueMethod( IsMapping,     IsRcwaMapping );
31InstallTrueMethod( IsRcwaMapping, IsRcwaMappingOfZOrZ_pi );
32InstallTrueMethod( IsRcwaMappingOfZOrZ_pi, IsRcwaMappingOfZ );
33InstallTrueMethod( IsRcwaMappingOfZOrZ_pi, IsRcwaMappingOfZ_pi );
34InstallTrueMethod( IsRcwaMapping, IsRcwaMappingOfZxZ );
35InstallTrueMethod( IsRcwaMapping, IsRcwaMappingOfGFqx );
36
37#############################################################################
38##
39#S  Shorthands for commonly used filters. ///////////////////////////////////
40##
41#############################################################################
42
43BindGlobal( "IsRcwaMappingInStandardRep",
44             IsRcwaMapping and IsRcwaMappingStandardRep );
45BindGlobal( "IsRcwaMappingOfZInStandardRep",
46             IsRcwaMappingOfZ and IsRcwaMappingStandardRep );
47BindGlobal( "IsRcwaMappingOfZ_piInStandardRep",
48             IsRcwaMappingOfZ_pi and IsRcwaMappingStandardRep );
49BindGlobal( "IsRcwaMappingOfZOrZ_piInStandardRep",
50             IsRcwaMappingOfZOrZ_pi and IsRcwaMappingStandardRep );
51BindGlobal( "IsRcwaMappingOfZxZInStandardRep",
52             IsRcwaMappingOfZxZ and IsRcwaMappingStandardRep );
53BindGlobal( "IsRcwaMappingOfGFqxInStandardRep",
54             IsRcwaMappingOfGFqx and IsRcwaMappingStandardRep );
55
56BindGlobal( "IsRcwaMappingInSparseRep",
57             IsRcwaMapping and IsRcwaMappingSparseRep );
58BindGlobal( "IsRcwaMappingOfZInSparseRep",
59             IsRcwaMappingOfZ and IsRcwaMappingSparseRep );
60BindGlobal( "IsRcwaMappingOfZ_piInSparseRep",                 # unused so far
61             IsRcwaMappingOfZ_pi and IsRcwaMappingSparseRep );
62BindGlobal( "IsRcwaMappingOfZOrZ_piInSparseRep",
63             IsRcwaMappingOfZOrZ_pi and IsRcwaMappingSparseRep );
64BindGlobal( "IsRcwaMappingOfZxZInSparseRep",                  # unused so far
65             IsRcwaMappingOfZxZ and IsRcwaMappingSparseRep );
66BindGlobal( "IsRcwaMappingOfGFqxInSparseRep",                 # unused so far
67             IsRcwaMappingOfGFqx and IsRcwaMappingSparseRep );
68
69#############################################################################
70##
71#S  The families of rcwa mappings. //////////////////////////////////////////
72##
73#############################################################################
74
75#############################################################################
76##
77#V  RcwaMappingsOfZFamily . . . . . . .  the family of all rcwa mappings of Z
78##
79BindGlobal( "RcwaMappingsOfZFamily",
80            NewFamily( "RcwaMappingsFamily( Integers )",
81                       IsRcwaMappingOfZ,
82                       CanEasilySortElements, CanEasilySortElements ) );
83SetFamilySource( RcwaMappingsOfZFamily, FamilyObj( 1 ) );
84SetFamilyRange ( RcwaMappingsOfZFamily, FamilyObj( 1 ) );
85SetUnderlyingRing( RcwaMappingsOfZFamily, Integers );
86
87#############################################################################
88##
89#V  RcwaMappingsOfZxZFamily . . . . .  the family of all rcwa mappings of Z^2
90##
91BindGlobal( "RcwaMappingsOfZxZFamily",
92            NewFamily( "RcwaMappingsFamily( Integers^2 )",
93                       IsRcwaMappingOfZxZ,
94                       CanEasilySortElements, CanEasilySortElements ) );
95SetFamilySource( RcwaMappingsOfZxZFamily, FamilyObj( [ 1, 1 ] ) );
96SetFamilyRange ( RcwaMappingsOfZxZFamily, FamilyObj( [ 1, 1 ] ) );
97SetUnderlyingLeftModule( RcwaMappingsOfZxZFamily, Integers^2 );
98
99## Internal variables storing the rcwa mapping families used in the
100## current GAP session.
101
102BindGlobal( "Z_PI_RCWAMAPPING_FAMILIES", [] );
103BindGlobal( "GFQX_RCWAMAPPING_FAMILIES", [] );
104
105#############################################################################
106##
107#F  RcwaMappingsOfZ_piFamily( <R> )
108##
109##  Returns the family of all rcwa mappings of a given semilocalization <R>
110##  of the ring of integers.
111##
112InstallGlobalFunction( RcwaMappingsOfZ_piFamily,
113
114  function ( R )
115
116    local  fam, name;
117
118    if   not IsZ_pi( R )
119    then Error("usage: RcwaMappingsOfZ_piFamily( <R> )\n",
120               "where <R> = Z_pi( <pi> ) for a set of primes <pi>.\n");
121    fi;
122    fam := First( Z_PI_RCWAMAPPING_FAMILIES,
123                  fam -> UnderlyingRing( fam ) = R );
124    if fam <> fail then return fam; fi;
125    name := Concatenation( "RcwaMappingsFamily( ",
126                           String( R ), " )" );
127    fam := NewFamily( name, IsRcwaMappingOfZ_pi,
128                      CanEasilySortElements, CanEasilySortElements );
129    SetUnderlyingRing( fam, R );
130    SetFamilySource( fam, FamilyObj( 1 ) );
131    SetFamilyRange ( fam, FamilyObj( 1 ) );
132    MakeReadWriteGlobal( "Z_PI_RCWAMAPPING_FAMILIES" );
133    Add( Z_PI_RCWAMAPPING_FAMILIES, fam );
134    MakeReadOnlyGlobal( "Z_PI_RCWAMAPPING_FAMILIES" );
135
136    return fam;
137  end );
138
139#############################################################################
140##
141#F  RcwaMappingsOfGFqxFamily( <R> )
142##
143##  Returns the family of all rcwa mappings of a given polynomial ring <R>
144##  in one variable over a finite field.
145##
146InstallGlobalFunction( RcwaMappingsOfGFqxFamily,
147
148  function ( R )
149
150    local  fam, x;
151
152    if   not (     IsUnivariatePolynomialRing( R )
153               and IsFiniteFieldPolynomialRing( R ) )
154    then Error("usage: RcwaMappingsOfGFqxFamily( <R> ) for a ",
155               "univariate polynomial ring <R> over a finite field.\n");
156    fi;
157    x := IndeterminatesOfPolynomialRing( R )[ 1 ];
158    fam := First( GFQX_RCWAMAPPING_FAMILIES,
159                  fam -> IsIdenticalObj( UnderlyingRing( fam ), R ) );
160    if fam <> fail then return fam; fi;
161    fam := NewFamily( Concatenation( "RcwaMappingsFamily( ",
162                                      String( R ), " )" ),
163                      IsRcwaMappingOfGFqx,
164                      CanEasilySortElements, CanEasilySortElements );
165    SetUnderlyingIndeterminate( fam, x );
166    SetUnderlyingRing( fam, R );
167    SetFamilySource( fam, FamilyObj( x ) );
168    SetFamilyRange ( fam, FamilyObj( x ) );
169    MakeReadWriteGlobal( "GFQX_RCWAMAPPING_FAMILIES" );
170    Add( GFQX_RCWAMAPPING_FAMILIES, fam );
171    MakeReadOnlyGlobal( "GFQX_RCWAMAPPING_FAMILIES" );
172
173    return fam;
174  end );
175
176#############################################################################
177##
178#F  RcwaMappingsFamily( <R> ) . . . family of rcwa mappings over the ring <R>
179##
180InstallGlobalFunction( RcwaMappingsFamily,
181
182  function ( R )
183
184    if   IsIntegers( R ) then return RcwaMappingsOfZFamily;
185    elif IsZxZ( R )      then return RcwaMappingsOfZxZFamily;
186    elif IsZ_pi( R )     then return RcwaMappingsOfZ_piFamily( R );
187    elif IsUnivariatePolynomialRing( R ) and IsFiniteFieldPolynomialRing( R )
188    then return RcwaMappingsOfGFqxFamily( R );
189    else Error("Sorry, rcwa mappings over ",R," are not yet implemented.\n");
190    fi;
191  end );
192
193#############################################################################
194##
195#F  RcwaMappingsType( <R> ) . . . . . . . . . .  filter: rcwa mappings of <R>
196##
197InstallGlobalFunction( RcwaMappingsType,
198
199  function ( R )
200    if   IsIntegers( R ) then return IsRcwaMappingOfZ;
201    elif IsZxZ( R )      then return IsRcwaMappingOfZxZ;
202    elif IsZ_pi( R )     then return IsRcwaMappingOfZ_pi;
203    elif IsUnivariatePolynomialRing( R ) and IsFiniteFieldPolynomialRing( R )
204    then return IsRcwaMappingOfGFqx;
205    else return fail; fi;
206  end );
207
208#############################################################################
209##
210#S  The methods for the general-purpose constructor for rcwa mappings. //////
211##
212#############################################################################
213
214#############################################################################
215##
216#F  RCWAMAPPING_COMPRESS_COEFFICIENT_LIST( <coeffs> ) . . . . . . . . utility
217##
218##  This function takes care of that equal coefficient triples are always
219##  also identical, in order to save memory.
220##
221BindGlobal( "RCWAMAPPING_COMPRESS_COEFFICIENT_LIST",
222
223  function ( coeffs )
224
225    local  cset, i;
226
227    cset := Set(coeffs);
228    for i in [1..Length(coeffs)] do
229      coeffs[i] := cset[PositionSorted(cset,coeffs[i])];
230    od;
231  end );
232
233#############################################################################
234##
235#M  RcwaMapping( <R>, <modulus>, <coeffs> ) . . . .  method (a) in the manual
236##
237InstallMethod( RcwaMapping,
238               "rcwa mapping by ring, modulus and coefficients (RCWA)",
239               ReturnTrue, [ IsRing, IsRingElement, IsList ], 0,
240
241  function ( R, modulus, coeffs )
242
243    if not modulus in R then TryNextMethod(); fi;
244    if   IsIntegers(R) or IsZ_pi(R)
245    then return RcwaMapping(R,coeffs);
246    elif IsPolynomialRing(R)
247    then return RcwaMapping(Size(LeftActingDomain(R)),modulus,coeffs);
248    else TryNextMethod(); fi;
249  end );
250
251#############################################################################
252##
253#M  RcwaMapping( <R>, <modulus>, <coeffs> ) . . . .  method (a) in the manual
254##
255InstallMethod( RcwaMapping,
256               "rcwa mapping by ring = Z^2, modulus and coefficients (RCWA)",
257               ReturnTrue, [ IsRowModule, IsMatrix, IsList ], 0,
258
259  function ( R, modulus, coeffs )
260
261    local  residues, errormessage, i;
262
263    errormessage := Concatenation("construction of an rcwa mapping of Z^2:",
264                                  "\nmathematically incorrect arguments.\n");
265
266    if   not IsZxZ(R) or DimensionsMat(modulus) <> [2,2]
267      or not ForAll(Flat(modulus),IsInt) or DeterminantMat(modulus) = 0
268      or Length(coeffs) <> DeterminantMat(modulus)
269      or not ForAll(coeffs,IsList)
270      or not Set(List(coeffs,Length)) in [[2],[3]]
271      or not (    Length(coeffs[1])=2
272              and ForAll( coeffs, c ->    c[1] in R and IsList(c[2])
273                                      and Length(c[2])=3
274                                      and IsMatrix(c[2][1])
275                                      and ForAll(Flat(c[2][1]),IsInt)
276                                      and c[2][2] in R
277                                      and IsInt(c[2][3]) and c[2][3] <> 0 )
278            or    Length(coeffs[1])=3
279              and ForAll( coeffs, c ->    IsMatrix(c[1])
280                                      and ForAll(Flat(c[1]),IsInt)
281                                      and c[2] in R
282                                      and IsInt(c[3]) and c[3] <> 0 ) )
283    then Error(errormessage); return fail; fi;
284
285    modulus  := HermiteNormalFormIntegerMat(modulus);
286    residues := AllResidues(R,modulus);
287
288    if Length(coeffs[1]) = 2 then
289      for i in [1..Length(coeffs)] do
290        coeffs[i][1] := coeffs[i][1] mod modulus;
291      od;
292      Sort( coeffs, function ( c1, c2 ) return c1[1] < c2[1]; end );
293      if   List(coeffs,c->c[1]) <> residues
294      then Error(errormessage); return fail; fi;
295      coeffs := List(coeffs,c->c[2]);
296    fi;
297
298    if   not ForAll( [1..Length(residues)],
299                     i ->   ( residues[i]*coeffs[i][1] + coeffs[i][2] )
300                          mod coeffs[i][3] = [ 0, 0 ] )
301    then Error(errormessage); return fail; fi;
302
303    return RcwaMappingNC(R,modulus,coeffs);
304  end );
305
306#############################################################################
307##
308#M  RcwaMappingNC( <R>, <modulus>, <coeffs> ) . . NC-method (a) in the manual
309##
310InstallMethod( RcwaMappingNC,
311               "rcwa mapping by ring, modulus and coefficients (RCWA)",
312               ReturnTrue, [ IsRing, IsRingElement, IsList ], 0,
313
314  function ( R, modulus, coeffs )
315
316    if not modulus in R then TryNextMethod(); fi;
317    if   IsIntegers(R) or IsZ_pi(R)
318    then return RcwaMappingNC(R,coeffs);
319    elif IsPolynomialRing(R)
320    then return RcwaMappingNC(Size(LeftActingDomain(R)),modulus,coeffs);
321    else TryNextMethod(); fi;
322  end );
323
324#############################################################################
325##
326#M  RcwaMappingNC( <R>, <modulus>, <coeffs> ) . . NC-method (a) in the manual
327##
328InstallMethod( RcwaMappingNC,
329               "rcwa mapping by ring = Z^2, modulus and coefficients (RCWA)",
330               ReturnTrue, [ IsRowModule, IsMatrix, IsList ], 0,
331
332  function ( R, modulus, coeffs )
333
334    local  ReduceRcwaMappingOfZxZ, result;
335
336    ReduceRcwaMappingOfZxZ := function ( f )
337
338      local  m, c, d, divs, res, resRed, mRed, cRed,
339             nraffs, identres, pos, i;
340
341      m := f!.modulus; c := f!.coeffs;
342      for i in [1..Length(c)] do
343        c[i] := c[i]/Gcd(Flat(c[i]));
344        if c[i][3] < 0 then c[i] := -c[i]; fi;
345      od;
346      nraffs := Length(Set(c));
347      res    := AllResidues(R,m);
348      divs   := Superlattices(m);
349      mRed := m; cRed := c;
350      for d in divs do
351        if DeterminantMat(d) < nraffs then continue; fi;
352        resRed   := List(res,r->r mod d);
353        identres := EquivalenceClasses([1..Length(c)],i->resRed[i]);
354        if ForAll(identres,res->Length(Set(c{res}))=1) then
355          mRed   := d;
356          pos    := List(identres,cl->cl[1]);
357          resRed := res{pos};
358          cRed   := Permuted(c{pos},SortingPerm(resRed));
359          break;
360        fi;
361      od;
362      RCWAMAPPING_COMPRESS_COEFFICIENT_LIST(cRed);
363      f!.modulus := Immutable(mRed);
364      f!.coeffs  := Immutable(cRed);
365    end;
366
367    if not IsZxZ( R ) then TryNextMethod( ); fi;
368
369    modulus := HermiteNormalFormIntegerMat( modulus );
370
371    result := Objectify( NewType( RcwaMappingsOfZxZFamily,
372                                  IsRcwaMappingOfZxZInStandardRep ),
373                         rec( modulus := modulus,
374                              coeffs  := coeffs ) );
375    SetSource( result, R );
376    SetRange ( result, R );
377
378    ReduceRcwaMappingOfZxZ( result );
379
380    return result;
381  end );
382
383#############################################################################
384##
385#M  RcwaMapping( <R>, <coeffs> ) . . . . . . . . . . method (b) in the manual
386##
387InstallMethod( RcwaMapping,
388               "rcwa mapping by ring and coefficients (RCWA)",
389               ReturnTrue, [ IsRing, IsList ], 0,
390
391  function ( R, coeffs )
392
393    if   IsIntegers(R)
394    then return RcwaMapping(coeffs);
395    elif IsZ_pi(R)
396    then return RcwaMapping(NoninvertiblePrimes(R),coeffs);
397    else TryNextMethod(); fi;
398  end );
399
400#############################################################################
401##
402#M  RcwaMappingNC( <R>, <coeffs> ) . . . . . . .  NC-method (b) in the manual
403##
404InstallMethod( RcwaMappingNC,
405               "rcwa mapping by ring and coefficients (RCWA)",
406               ReturnTrue, [ IsRing, IsList ], 0,
407
408  function ( R, coeffs )
409
410    if   IsIntegers(R)
411    then return RcwaMappingNC(coeffs);
412    elif IsZ_pi(R)
413    then return RcwaMappingNC(NoninvertiblePrimes(R),coeffs);
414    else TryNextMethod(); fi;
415  end );
416
417#############################################################################
418##
419#M  RcwaMapping( <coeffs> ) . . . . . . . . . . . .  method (c) in the manual
420##
421InstallMethod( RcwaMapping,
422               "rcwa mapping of Z by coefficients (RCWA)",
423               true, [ IsList ], 10,
424
425  function ( coeffs )
426
427    local  quiet;
428
429    if not IsList( coeffs[1] ) or not IsInt( coeffs[1][1] )
430      or Length( coeffs[1] ) = 5
431    then TryNextMethod( ); fi;
432    quiet := ValueOption("BeQuiet") = true;
433    if not (     ForAll(Flat(coeffs),IsInt)
434             and ForAll(coeffs, IsList)
435             and ForAll(coeffs, c -> Length(c) = 3)
436             and ForAll([0..Length(coeffs) - 1],
437                        n -> coeffs[n + 1][3] <> 0 and
438                             (n * coeffs[n + 1][1] + coeffs[n + 1][2])
439                             mod coeffs[n + 1][3] = 0 and
440                             (  (n + Length(coeffs)) * coeffs[n + 1][1]
441                              +  coeffs[n + 1][2])
442                             mod coeffs[n + 1][3] = 0))
443    then if quiet then return fail; fi;
444         Error("the coefficients ",coeffs," do not define a proper ",
445               "rcwa mapping of Z.\n");
446    fi;
447    return RcwaMappingNC( coeffs );
448  end );
449
450#############################################################################
451##
452#M  RcwaMappingNC( <coeffs> ) . . . . . . . . . . NC-method (c) in the manual
453##
454InstallMethod( RcwaMappingNC,
455               "rcwa mapping of Z by coefficients (RCWA)",
456               true, [ IsList ], 10,
457
458  function ( coeffs )
459
460    local  ReduceRcwaMappingOfZ, Result;
461
462    ReduceRcwaMappingOfZ := function ( f )
463
464      local  c, m, fact, p, cRed, cRedBuf, n;
465
466      c := f!.coeffs; m := f!.modulus;
467      for n in [1..Length(c)] do
468        c[n] := c[n]/Gcd(c[n]);
469        if c[n][3] < 0 then c[n] := -c[n]; fi;
470      od;
471      fact := Set(FactorsInt(m)); cRed := c;
472      for p in fact do
473        repeat
474          cRedBuf := StructuralCopy(cRed);
475          cRed := List([1..p], i -> cRedBuf{[(i - 1) * m/p + 1 .. i * m/p]});
476          if   Length(Set(cRed)) = 1
477          then cRed := cRed[1]; m := m/p; else cRed := cRedBuf; fi;
478        until cRed = cRedBuf or m mod p <> 0;
479      od;
480      RCWAMAPPING_COMPRESS_COEFFICIENT_LIST(cRed);
481      f!.coeffs  := Immutable(cRed);
482      f!.modulus := Length(cRed);
483    end;
484
485    if not IsList( coeffs[1] ) or not IsInt( coeffs[1][1] )
486      or Length( coeffs[1] ) = 5
487    then TryNextMethod( ); fi;
488    Result := Objectify( NewType(    RcwaMappingsOfZFamily,
489                                     IsRcwaMappingOfZInStandardRep ),
490                         rec( coeffs  := coeffs,
491                              modulus := Length(coeffs) ) );
492    SetSource(Result, Integers);
493    SetRange (Result, Integers);
494    ReduceRcwaMappingOfZ(Result);
495    return Result;
496  end );
497
498#############################################################################
499##
500#M  RcwaMapping( <perm>, <range> ) . . . . . . . . . method (d) in the manual
501##
502InstallMethod( RcwaMapping,
503               "rcwa mapping of Z by permutation and range (RCWA)",
504               true, [ IsPerm, IsRange ], 0,
505
506  function ( perm, range )
507
508    local  quiet;
509
510    quiet := ValueOption("BeQuiet") = true;
511    if   Permutation(perm,range) = fail
512    then if quiet then return fail; fi;
513         Error("the permutation ",perm," does not act on the range ",
514               range,".\n");
515    fi;
516    return RcwaMappingNC( perm, range );
517  end );
518
519#############################################################################
520##
521#M  RcwaMappingNC( <perm>, <range> ) . . . . . .  NC-method (d) in the manual
522##
523InstallMethod( RcwaMappingNC,
524               "rcwa mapping of Z by permutation and range (RCWA)",
525               true, [ IsPerm, IsRange ], 0,
526
527  function ( perm, range )
528
529    local  result, coeffs, min, max, m, n, r;
530
531    min := Minimum(range); max := Maximum(range);
532    m := max - min + 1; coeffs := [];
533    for n in [min..max] do
534      r := n mod m + 1;
535      coeffs[r] := [1, n^perm - n, 1];
536    od;
537    result := RcwaMappingNC( coeffs );
538    SetIsBijective(result,true);
539    SetIsTame(result,true); SetIsIntegral(result,true);
540    SetOrder(result,Order(RestrictedPerm(perm,range)));
541    return result;
542  end );
543
544#############################################################################
545##
546#M  RcwaMapping( <modulus>, <values> ) . . . . . . . method (e) in the manual
547##
548InstallMethod( RcwaMapping,
549               "rcwa mapping of Z by modulus and values (RCWA)",
550               true, [ IsInt, IsList ], 0,
551
552  function ( modulus, values )
553
554    local  f, coeffs, pts, r, quiet;
555
556    quiet := ValueOption("BeQuiet") = true;
557    coeffs := [];
558    for r in [1..modulus] do
559      pts := Filtered(values, pt -> pt[1] mod modulus = r - 1);
560      if   Length(pts) < 2
561      then if quiet then return fail; fi;
562           Error("the mapping is not given at at least 2 points <n> ",
563                 "with <n> mod ",modulus," = ",r - 1,".\n");
564      fi;
565    od;
566    f := RcwaMappingNC( modulus, values );
567    if Mod(f) mod Div(f) <> 0 or not ForAll(values,t -> t[1]^f = t[2])
568    then if quiet then return fail; fi;
569         Error("the values ",values," do not define a proper ",
570               "rcwa mapping of Z.\n");
571    fi;
572    return f;
573  end );
574
575#############################################################################
576##
577#M  RcwaMappingNC( <modulus>, <values> ) . . . .  NC-method (e) in the manual
578##
579InstallMethod( RcwaMappingNC,
580               "rcwa mapping of Z by modulus and values (RCWA)",
581               true, [ IsInt, IsList ], 0,
582
583  function ( modulus, values )
584
585    local  coeffs, pts, r;
586
587    coeffs := [];
588    for r in [1..modulus] do
589      pts := Filtered(values, pt -> pt[1] mod modulus = r - 1);
590      coeffs[r] := [  pts[1][2] - pts[2][2],
591                      pts[1][2] * (pts[1][1] - pts[2][1])
592                    - pts[1][1] * (pts[1][2] - pts[2][2]),
593                      pts[1][1] - pts[2][1]];
594    od;
595    return RcwaMappingNC( coeffs );
596  end );
597
598#############################################################################
599##
600#M  RcwaMapping( <pi>, <coeffs> ) . . . . . . . . .  method (f) in the manual
601##
602InstallMethod( RcwaMapping,
603               "rcwa mapping by noninvertible primes and coeff's (RCWA)",
604               true, [ IsObject, IsList ], 0,
605
606  function ( pi, coeffs )
607
608    local  R, quiet;
609
610    quiet := ValueOption("BeQuiet") = true;
611    if IsInt(pi) then pi := [pi]; fi; R := Z_pi(pi);
612    if not (     IsList(pi) and ForAll(pi,IsInt)
613             and IsSubset(Union(pi,[1]),Set(Factors(Length(coeffs))))
614             and ForAll(Flat(coeffs), x -> IsRat(x) and Intersection(pi,
615                                        Set(Factors(DenominatorRat(x))))=[])
616             and ForAll(coeffs, IsList)
617             and ForAll(coeffs, c -> Length(c) = 3)
618             and ForAll([0..Length(coeffs) - 1],
619                        n -> coeffs[n + 1][3] <> 0 and
620                             NumeratorRat(n * coeffs[n + 1][1]
621                                            + coeffs[n + 1][2])
622                             mod StandardAssociate(R,coeffs[n + 1][3]) = 0
623                         and NumeratorRat(  (n + Length(coeffs))
624                                           * coeffs[n + 1][1]
625                                           + coeffs[n + 1][2])
626                             mod StandardAssociate(R,coeffs[n + 1][3]) = 0))
627    then if quiet then return fail; fi;
628         Error("the coefficients ",coeffs," do not define a proper ",
629               "rcwa mapping of Z_(",pi,").\n");
630    fi;
631    return RcwaMappingNC(pi,coeffs);
632  end );
633
634#############################################################################
635##
636#M  RcwaMappingNC( <pi>, <coeffs> ) . . . . . . . NC-method (f) in the manual
637##
638InstallMethod( RcwaMappingNC,
639               "rcwa mapping by noninvertible primes and coeff's (RCWA)",
640               true, [ IsObject, IsList ], 0,
641
642  function ( pi, coeffs )
643
644    local  ReduceRcwaMappingOfZ_pi, f, R, fam;
645
646    ReduceRcwaMappingOfZ_pi := function ( f )
647
648      local  c, m, pi, d_pi, d_piprime, divs, d, cRed, n, i;
649
650      c := f!.coeffs; m := f!.modulus;
651      pi := NoninvertiblePrimes(Source(f));
652      for n in [1..Length(c)] do
653        if c[n][3] < 0 then c[n] := -c[n]; fi;
654        d_pi := Gcd(Product(Filtered(Factors(Gcd(NumeratorRat(c[n][1]),
655                                                 NumeratorRat(c[n][2]))),
656                                     p -> p in pi or p = 0)),
657                    NumeratorRat(c[n][3]));
658        d_piprime := c[n][3]/Product(Filtered(Factors(NumeratorRat(c[n][3])),
659                                              p -> p in pi));
660        c[n] := c[n] / (d_pi * d_piprime);
661      od;
662      divs := DivisorsInt(m); i := 1;
663      repeat
664        d := divs[i]; i := i + 1;
665        cRed := List([1..m/d], i -> c{[(i - 1) * d + 1 .. i * d]});
666      until Length(Set(cRed)) = 1;
667      cRed := cRed[1];
668      RCWAMAPPING_COMPRESS_COEFFICIENT_LIST(cRed);
669      f!.coeffs  := Immutable(cRed);
670      f!.modulus := Length(cRed);
671    end;
672
673    if IsInt(pi) then pi := [pi]; fi;
674    if   not IsList(pi) or not ForAll(pi,IsInt) or not ForAll(coeffs,IsList)
675    then TryNextMethod(); fi;
676    R := Z_pi(pi); fam := RcwaMappingsFamily( R );
677    f := Objectify( NewType( fam, IsRcwaMappingOfZ_piInStandardRep ),
678                    rec( coeffs  := coeffs,
679                         modulus := Length(coeffs) ) );
680    SetSource(f,R); SetRange(f,R);
681    ReduceRcwaMappingOfZ_pi(f);
682    return f;
683  end );
684
685#############################################################################
686##
687#M  RcwaMapping( <q>, <modulus>, <coeffs> ) . . . .  method (g) in the manual
688##
689InstallMethod( RcwaMapping,
690               Concatenation("rcwa mapping by finite field size, ",
691                             "modulus and coefficients (RCWA)"),
692               true, [ IsInt, IsPolynomial, IsList ], 0,
693
694  function ( q, modulus, coeffs )
695
696    local  d, x, P, p, quiet;
697
698    quiet := ValueOption("BeQuiet") = true;
699    if not (    IsPosInt(q) and IsPrimePowerInt(q)
700            and ForAll(coeffs, IsList)
701            and ForAll(coeffs, c -> Length(c) = 3)
702            and ForAll(Flat(coeffs), IsPolynomial)
703            and Length(Set(List(Flat(coeffs),
704                                IndeterminateNumberOfLaurentPolynomial)))=1)
705    then if quiet then return fail; fi;
706         Error("see RCWA manual for information on how to construct\n",
707               "an rcwa mapping of a polynomial ring.\n");
708    fi;
709    d := DegreeOfLaurentPolynomial(modulus);
710    x := IndeterminateOfLaurentPolynomial(coeffs[1][1]);
711    P := AllGFqPolynomialsModDegree(q,d,x);
712    if not ForAll([1..Length(P)],
713                  i -> IsZero(   (coeffs[i][1]*P[i] + coeffs[i][2])
714                              mod coeffs[i][3]))
715    then Error("the coefficients ",coeffs," do not define a proper ",
716               "rcwa mapping.\n");
717    fi;
718    return RcwaMappingNC( q, modulus, coeffs );
719  end );
720
721#############################################################################
722##
723#M  RcwaMappingNC( <q>, <modulus>, <coeffs> ) . . NC-method (g) in the manual
724##
725InstallMethod( RcwaMappingNC,
726               Concatenation("rcwa mapping by finite field size, ",
727                             "modulus and coefficients (RCWA)"),
728               true, [ IsInt, IsPolynomial, IsList ], 0,
729
730  function ( q, modulus, coeffs )
731
732    local  ReduceRcwaMappingOfGFqx, f, R, fam, ind;
733
734    ReduceRcwaMappingOfGFqx := function ( f )
735
736      local  c, m, F, q, x, deg, r, fact, d, degd,
737             sigma, csorted, numresred, numresd, mred, rred,
738             n, l, i;
739
740      c := f!.coeffs; m := f!.modulus;
741      for n in [1..Length(c)] do
742        d := Gcd(c[n]);
743        c[n] := c[n]/(d * LeadingCoefficient(c[n][3]));
744      od;
745      deg := DegreeOfLaurentPolynomial(m);
746      F := CoefficientsRing(UnderlyingRing(FamilyObj(f)));
747      q := Size(F);
748      x := UnderlyingIndeterminate(FamilyObj(f));
749      r := AllGFqPolynomialsModDegree(q,deg,x);
750      fact := Difference(Factors(m),[One(m)]);
751      for d in fact do
752        degd := DegreeOfLaurentPolynomial(d);
753        repeat
754          numresd := q^degd; numresred := q^(deg-degd);
755          mred  := m/d;
756          rred  := List(r, P -> P mod mred);
757          sigma := SortingPerm(rred);
758          csorted := Permuted(c,sigma);
759          if ForAll([1..numresred],
760                    i->Length(Set(csorted{[(i-1)*numresd+1..i*numresd]}))=1)
761          then m   := mred;
762               deg := deg - degd;
763               r := AllGFqPolynomialsModDegree(q,deg,x);
764               c := csorted{[1, 1 + numresd .. 1 + (numresred-1) * numresd]};
765          fi;
766        until m <> mred or not IsZero(m mod d);
767      od;
768      RCWAMAPPING_COMPRESS_COEFFICIENT_LIST(c);
769      f!.coeffs  := Immutable(c);
770      f!.modulus := m;
771    end;
772
773    ind := IndeterminateNumberOfLaurentPolynomial( coeffs[1][1] );
774    R   := PolynomialRing( GF( q ), [ ind ] );
775    fam := RcwaMappingsFamily( R );
776    f   := Objectify( NewType( fam, IsRcwaMappingOfGFqxInStandardRep ),
777                      rec( coeffs  := coeffs,
778                           modulus := modulus ) );
779    SetUnderlyingField( f, CoefficientsRing( R ) );
780    SetSource( f, R ); SetRange( f, R );
781    ReduceRcwaMappingOfGFqx( f );
782    return f;
783  end );
784
785#############################################################################
786##
787#M  RcwaMapping( <P1>, <P2> ) . . . . . . . . . . .  method (h) in the manual
788##
789InstallMethod( RcwaMapping,
790               "rcwa mapping by two class partitions (RCWA)",
791               true, [ IsList, IsList ], 0,
792
793  function ( P1, P2 )
794
795    local  result;
796
797    if not (     ForAll(Concatenation(P1,P2),IsResidueClass)
798             and Length(P1) = Length(P2)
799             and Sum(List(P1,Density)) = 1
800             and Union(P1) = UnderlyingRing(FamilyObj(P1[1])))
801    then TryNextMethod(); fi;
802    result := RcwaMappingNC(P1,P2);
803    IsBijective(result);
804    return result;
805  end );
806
807#############################################################################
808##
809#M  RcwaMappingNC( <P1>, <P2> ) . . . . . . . . . NC-method (h) in the manual
810##
811InstallMethod( RcwaMappingNC,
812               "rcwa mapping by two class partitions (RCWA)",
813               true, [ IsList, IsList ], 0,
814
815  function ( P1, P2 )
816
817    local  R, coeffs, m, res, r1, m1, r2, m2, i, j;
818
819    if not IsResidueClassUnion(P1[1]) then TryNextMethod(); fi;
820    R := UnderlyingRing(FamilyObj(P1[1]));
821    m := Lcm(R,List(P1,Modulus)); res := AllResidues(R,m);
822    coeffs := List(res,r->[1,0,1]*One(R));
823    for i in [1..Length(P1)] do
824      r1 := Residue(P1[i]); m1 := Modulus(P1[i]);
825      r2 := Residue(P2[i]); m2 := Modulus(P2[i]);
826      for j in Filtered([1..Length(res)],j->res[j] mod m1 = r1) do
827        coeffs[j] := [m2,m1*r2-m2*r1,m1];
828      od;
829    od;
830    return RcwaMappingNC(R,m,coeffs);
831  end );
832
833#############################################################################
834##
835#M  RcwaMappingNC( <P1>, <P2> ) . . . .  NC-method (h) in the manual, for Z^2
836##
837InstallMethod( RcwaMappingNC,
838               "rcwa mapping by two class partitions of Z^2 (RCWA)",
839               true, [ IsList, IsList ], 0,
840
841  function ( P1, P2 )
842
843    local  R, coeffs, m, res, affectedpos, t, r1, m1, r2, m2, i, j;
844
845    if not IsResidueClassUnionOfZxZ(P1[1]) then TryNextMethod(); fi;
846    R := UnderlyingRing(FamilyObj(P1[1]));
847    m := Lcm(List(P1,Modulus)); res := AllResidues(R,m);
848    coeffs := List(res,r->[[[1,0],[0,1]],[0,0],1]);
849    for i in [1..Length(P1)] do
850      r1 := Residue(P1[i]); m1 := Modulus(P1[i]);
851      r2 := Residue(P2[i]); m2 := Modulus(P2[i]);
852      affectedpos := Filtered([1..Length(res)],j->res[j] mod m1 = r1);
853      t := [m1^-1*m2,r2-r1*m1^-1*m2,1];
854      t := t * Lcm(List(Flat(t),DenominatorRat));
855      for i in affectedpos do coeffs[i] := t; od;
856    od;
857    return RcwaMappingNC(R,m,coeffs);
858  end );
859
860#############################################################################
861##
862#M  RcwaMapping( <cycles> ) . . . . . . . . . . . .  method (i) in the manual
863##
864InstallMethod( RcwaMapping,
865               "rcwa mapping by class cycles (RCWA)", true, [ IsList ], 0,
866
867  function ( cycles )
868
869    local  CheckClassCycles, R;
870
871    CheckClassCycles := function ( R, cycles )
872
873      if not (    ForAll(cycles,IsList)
874              and ForAll(Flat(cycles),S->IsResidueClass(S)
875              and IsSubset(R,S)))
876         or  ForAny(Combinations(Flat(cycles),2),s->Intersection(s) <> [])
877      then Error("there is no rcwa mapping of ",R," having the class ",
878                 "cycles ",cycles,".\n");
879      fi;
880    end;
881
882    if   not IsList(cycles[1]) or not IsResidueClass(cycles[1][1])
883    then TryNextMethod(); fi;
884    R := cycles[1][1];
885    if   not (IsRing(R) or IsZxZ(R))
886    then R := UnderlyingRing(FamilyObj(R)); fi;
887    CheckClassCycles(R,cycles);
888    return RcwaMappingNC(cycles);
889  end );
890
891#############################################################################
892##
893#M  RcwaMapping( <cycles> ) .  method (i), variation for rc. with fixed rep's
894##
895InstallMethod( RcwaMapping,
896               "rcwa mapping by class cycles (fixed rep's) (RCWA)",
897               true, [ IsList ], 0,
898
899  function ( cycles )
900
901    local  CheckClassCycles, R;
902
903    CheckClassCycles := function ( R, cycles )
904
905      if not (    ForAll(cycles,IsList)
906              and ForAll(Flat(cycles),S->IsResidueClassWithFixedRep(S)
907              and UnderlyingRing(FamilyObj(S)) = R))
908         or  ForAny(Combinations(Flat(cycles),2),
909                    s->Intersection(List([s[1],s[2]],
910                                    AsOrdinaryUnionOfResidueClasses)) <> [])
911      then Error("there is no rcwa mapping of ",R," having the class ",
912                 "cycles ",cycles,".\n");
913      fi;
914    end;
915
916    if   not IsList(cycles[1])
917      or not IsResidueClassWithFixedRepresentative(cycles[1][1])
918    then TryNextMethod(); fi;
919    R := UnderlyingRing(FamilyObj(cycles[1][1]));
920    CheckClassCycles(R,cycles);
921    return RcwaMappingNC(cycles);
922  end );
923
924#############################################################################
925##
926#M  RcwaMappingNC( <cycles> ) . . . . . . . . . . NC-method (i) in the manual
927##
928InstallMethod( RcwaMappingNC,
929               "rcwa mapping by class cycles (RCWA)", true, [ IsList ], 0,
930
931  function ( cycles )
932
933    local  result, R, coeffs, m, res, cyc, pre, im, affectedpos,
934           r1, r2, m1, m2, pos, i;
935
936    if    not IsResidueClass(cycles[1][1])
937      and not IsResidueClassWithFixedRepresentative(cycles[1][1])
938    then TryNextMethod(); fi;
939
940    R := cycles[1][1];
941    if   not (IsRing(R) or IsZxZ(R))
942    then R := UnderlyingRing(FamilyObj(R)); fi;
943    if not IsIntegers(R) and not IsZ_pi(R)
944      and not (     IsUnivariatePolynomialRing(R)
945                and IsFiniteFieldPolynomialRing(R))
946    then TryNextMethod(); fi;
947
948    m      := Lcm(List(Union(cycles),Modulus));
949    res    := AllResidues(R,m);
950    coeffs := List(res,r->[1,0,1]*One(R));
951    for cyc in cycles do
952      if Length(cyc) <= 1 then continue; fi;
953      for pos in [1..Length(cyc)] do
954        pre := cyc[pos]; im := cyc[pos mod Length(cyc) + 1];
955        r1 := Residue(pre); m1 := Modulus(pre);
956        r2 := Residue(im);  m2 := Modulus(im);
957        affectedpos := Filtered([1..Length(res)],
958                                i->res[i] mod m1 = r1 mod m1);
959        for i in affectedpos do coeffs[i] := [m2,m1*r2-m2*r1,m1]; od;
960      od;
961    od;
962    if   IsIntegers(R)
963    then result := RcwaMappingNC(coeffs);
964    elif IsZ_pi(R)
965    then result := RcwaMappingNC(R,coeffs);
966    elif IsPolynomialRing(R)
967    then result := RcwaMappingNC(R,Lcm(List(Flat(cycles),Modulus)),coeffs);
968    fi;
969    Assert(4,Order(result)=Lcm(List(cycles,Length)));
970    SetIsBijective(result,true); SetIsTame(result,true);
971    SetOrder(result,Lcm(List(cycles,Length)));
972    return result;
973  end );
974
975#############################################################################
976##
977#M  RcwaMappingNC( <cycles> ) . . . . .  NC-method (i) in the manual, for Z^2
978##
979InstallMethod( RcwaMappingNC,
980               "rcwa mapping of Z^2 by class cycles (RCWA)", true,
981               [ IsList ], 0,
982
983  function ( cycles )
984
985    local  result, R, coeffs, m, res, cyc, pre, im, affectedpos, t,
986           r1, r2, m1, m2, pos, i;
987
988    if   not IsResidueClass(cycles[1][1])
989      or not IsResidueClassUnionOfZxZ(cycles[1][1])
990    then TryNextMethod(); fi;
991
992    R      := UnderlyingRing(FamilyObj(cycles[1][1]));
993    m      := Lcm(List(Union(cycles),Modulus));
994    res    := AllResidues(R,m);
995    coeffs := List(res,r->[[[1,0],[0,1]],[0,0],1]);
996    for cyc in cycles do
997      if Length(cyc) <= 1 then continue; fi;
998      for pos in [1..Length(cyc)] do
999        pre := cyc[pos]; im := cyc[pos mod Length(cyc) + 1];
1000        r1 := Residue(pre); m1 := Modulus(pre);
1001        r2 := Residue(im);  m2 := Modulus(im);
1002        affectedpos := Filtered([1..Length(res)],i->res[i] mod m1 = r1);
1003        t := [m1^-1*m2,r2-r1*m1^-1*m2,1];
1004        t := t * Lcm(List(Flat(t),DenominatorRat));
1005        for i in affectedpos do coeffs[i] := t; od;
1006      od;
1007    od;
1008    result := RcwaMapping(R,m,coeffs);
1009    Assert(4,Order(result)=Lcm(List(cycles,Length)));
1010    SetIsBijective(result,true); SetIsTame(result,true);
1011    SetOrder(result,Lcm(List(cycles,Length)));
1012    return result;
1013  end );
1014
1015#############################################################################
1016##
1017#M  RcwaMapping( <expression> ) . . . . . . . . . .  method (j) in the manual
1018##
1019InstallMethod( RcwaMapping,
1020               "rcwa mapping of Z by expression, given as a string (RCWA)",
1021               true, [ IsString ], 0,
1022
1023  function ( expression )
1024    if   IsSubset( "0123456789-,()crs^*/", expression )
1025    then return RcwaMappingNC( expression );
1026    else TryNextMethod(); fi;
1027  end );
1028
1029#############################################################################
1030##
1031#M  RcwaMappingNC( <expression> ) . . . . . . . . NC-method (j) in the manual
1032##
1033InstallMethod( RcwaMappingNC,
1034               "rcwa mapping of Z by expression, given as a string (RCWA)",
1035               true, [ IsString ], 0,
1036
1037  function ( expression )
1038
1039    local  ValueExpression, ValueElementaryExpression;
1040
1041    ValueElementaryExpression := function ( exp )
1042
1043      local  ints, cycle, i;
1044
1045      if IsSubset("0123456789-",exp) then return Int(exp); fi;
1046      ints := List(Filtered(SplitString(exp,"()[],crs"),
1047                            s->s<>""),Int);
1048      if   Length(ints) = 2 then
1049        if   's' in exp
1050        then return ClassShift(ints);
1051        else return ClassReflection(ints); fi;
1052      elif Length(ints) = 4 then
1053        return ClassTransposition(ints);
1054      elif Length(ints) mod 2 = 0 then
1055        cycle := [];
1056        for i in [1,3..Length(ints)-1] do
1057          Add(cycle,ResidueClass(ints[i],ints[i+1]));
1058        od;
1059        return RcwaMapping([cycle]);
1060      else Error("unknown type of rcwa permutation\n"); fi;
1061    end;
1062
1063    ValueExpression := function ( exp )
1064
1065      local  brackets, parts, part, operators,
1066             values, valuesexp, value, i, j;
1067
1068      if   IsSubset("0123456789-,()crs",exp)
1069      then return ValueElementaryExpression(exp); fi;
1070
1071      brackets := 0; parts := []; operators := []; part := "";
1072      for i in [1..Length(exp)] do
1073        Add(part,exp[i]);
1074        if   exp[i] = '(' then
1075          brackets := brackets + 1;
1076        elif exp[i] = ')' then
1077          brackets := brackets - 1;
1078          if brackets = 0 then
1079            Add(parts,part); part := "";
1080          fi;
1081        elif brackets = 0 and exp[i] in "*/^" then
1082          Add(operators,exp[i]);
1083          Add(parts,part{[1..Length(part)-1]}); part := "";
1084        fi;
1085      od;
1086      Add(parts,part);
1087      parts := Filtered(parts,part->Intersection(part,"0123456789")<>"");
1088      for i in [1..Length(parts)] do
1089        if   parts[i][1] = '('
1090        then parts[i] := parts[i]{[2..Length(parts[i])-1]}; fi;
1091      od;
1092      values    := List(parts,ValueExpression);
1093      valuesexp := ShallowCopy(values);
1094      for i in [1..Length(operators)] do
1095        if operators[i] = '^' then
1096          valuesexp[i] := valuesexp[i]^valuesexp[i+1];
1097          valuesexp[i+1] := fail;
1098        fi;
1099      od;
1100      valuesexp := Filtered(valuesexp,val->val<>fail);
1101
1102      operators := Filtered(operators,op->op<>'^');
1103      value     := valuesexp[1];
1104      for i in [1..Length(operators)] do
1105        if   operators[i] = '*'
1106        then value := value * valuesexp[i+1];
1107        elif operators[i] = '/'
1108        then value := value / valuesexp[i+1];
1109        else Error("RcwaMapping: unknown operator: ",operators[i],"\n"); fi;
1110      od;
1111
1112      return value;
1113    end;
1114
1115    return ValueExpression( BlankFreeString( expression ) );
1116  end );
1117
1118#############################################################################
1119##
1120#M  RcwaMapping( <R>, <f>, <g> ) . rcwa mapping of Z^2 by two rcwa map's of Z
1121#M  RcwaMapping( <f>, <g> )
1122##
1123InstallMethod( RcwaMapping,
1124               "rcwa mapping of Z^2 by two rcwa mappings of Z (RCWA)",
1125               ReturnTrue,
1126               [ IsRowModule, IsRcwaMappingOfZ, IsRcwaMappingOfZ ], 0,
1127               function ( R, f, g )
1128                 if IsZxZ(R) then return RcwaMappingNC(f,g);
1129                             else TryNextMethod(); fi;
1130               end );
1131
1132InstallMethod( RcwaMapping,
1133               "rcwa mapping of Z^2 by two rcwa mappings of Z (RCWA)",
1134               IsIdenticalObj, [ IsRcwaMappingOfZ, IsRcwaMappingOfZ ], 0,
1135               function ( f, g ) return RcwaMappingNC(f,g); end );
1136
1137#############################################################################
1138##
1139#M  RcwaMappingNC( <f>, <g> ) . rcwa mapping of Z^2 by two rcwa mappings of Z
1140##
1141InstallMethod( RcwaMappingNC,
1142               "rcwa mapping of Z^2 by two rcwa mappings of Z (RCWA)",
1143               IsIdenticalObj, [ IsRcwaMappingOfZ, IsRcwaMappingOfZ ], 0,
1144
1145  function ( f, g )
1146
1147    local  result, m, mf, mg, c, cf, cg, res, r, t, d, d1, d2;
1148
1149    mf := Modulus(f);      mg  := Modulus(g);
1150    m  := [[mf,0],[0,mg]]; res := AllResidues(Integers^2,m);
1151    cf := Coefficients(f); cg  := Coefficients(g); c := [];
1152    for r in res do
1153      t := [cf[r[1]+1],cg[r[2]+1]];
1154      d := Lcm(t[1][3],t[2][3]); d1 := d/t[1][3]; d2 := d/t[2][3];
1155      Add(c,[[[t[1][1]*d1,0],[0,t[2][1]*d2]],[t[1][2]*d1,t[2][2]*d2],d]);
1156    od;
1157    result := RcwaMapping(Integers^2,m,c);
1158    if   ForAny([f,g],HasIsClassTransposition)
1159    then IsClassTransposition(result); fi;
1160    return result;
1161  end );
1162
1163#############################################################################
1164##
1165#M  RcwaMapping( <coeffs> ) . . .  rcwa mapping of Z in sparse representation
1166##
1167InstallMethod( RcwaMapping,
1168               "rcwa mapping of Z in sparse representation (RCWA)",
1169               true, [ IsList ], 5,
1170
1171  function ( coeffs )
1172
1173    local  result;
1174
1175    if   not ForAll(coeffs,c->IsList(c) and Length(c)=5 and ForAll(c,IsInt))
1176    then TryNextMethod(); fi;
1177
1178    if ForAny(coeffs,c->c[2] = 0 or c[5] = 0) then
1179      Error("zero in modulus or denominator not allowed.\n");
1180      return fail;
1181    fi;
1182    if ForAny(Combinations([1..Length(coeffs)],2),
1183              i->(    coeffs[i[1]][1]-coeffs[i[2]][1])
1184                  mod Gcd(coeffs[i[1]][2],coeffs[i[2]][2]) = 0)
1185    then Error("rcwa mapping must be single-valued.\n"); return fail; fi;
1186    if   Sum(List(coeffs,c->1/c[2])) <> 1
1187    then Error("rcwa mapping must be total.\n"); return fail; fi;
1188
1189    return RcwaMappingNC(coeffs);
1190  end );
1191
1192#############################################################################
1193##
1194#M  RcwaMappingNC( <coeffs> ) . .  rcwa mapping of Z in sparse representation
1195##
1196InstallMethod( RcwaMappingNC,
1197               "rcwa mapping of Z in sparse representation (RCWA)",
1198               true, [ IsList ], 5,
1199
1200  function ( coeffs )
1201
1202    local  result, modulus, coeffset, coeffcoll, cls, redcls, cl,
1203           d, d_red, res, res_d, div, m, r, r_red, range, i, j;
1204
1205    if   not ForAll(coeffs,c->IsList(c) and Length(c)=5)
1206      or not ForAll(coeffs[1],IsInt)
1207    then TryNextMethod(); fi;
1208
1209    for i in [1..Length(coeffs)] do
1210      coeffs[i][2] := AbsInt(coeffs[i][2]);
1211      coeffs[i][1] := coeffs[i][1] mod coeffs[i][2];
1212      coeffs[i]{[3..5]} := coeffs[i]{[3..5]}/Gcd(coeffs[i]{[3..5]});
1213      if coeffs[i][5] < 0 then coeffs[i]{[3..5]} := -coeffs[i]{[3..5]}; fi;
1214    od;
1215
1216    coeffset  := Set(List(coeffs,c->c{[3..5]}));
1217    coeffcoll := List(coeffset,c->[]);
1218    for i in [1..Length(coeffs)] do
1219      j := PositionSorted(coeffset,coeffs[i]{[3..5]});
1220      Add(coeffcoll[j],coeffs[i]);
1221    od;
1222
1223    coeffs := [];
1224    for i in [1..Length(coeffset)] do
1225
1226      cls := Set(coeffcoll[i],c->c{[1,2]});
1227      if Length(cls) > 1 then
1228
1229        d_red  := Gcd(DifferencesList(List(cls,cl->cl[1])));
1230        d_red  := Gcd(d_red,Gcd(List(cls,cl->cl[2])));
1231        r_red  := cls[1][1] mod d_red;
1232        redcls := List(cls,cl->[(cl[1]-r_red)/d_red,cl[2]/d_red]);
1233        m      := Lcm(List(redcls,cl->cl[2]));
1234        res    := [];
1235        for cl in redcls do
1236          for j in [0..m/cl[2]-1] do Add(res,cl[1]+j*cl[2]); od;
1237        od;
1238        res := Set(res);
1239
1240        redcls := [];
1241        div    := DivisorsInt(m);
1242        for d in div do
1243          res_d := Set(res mod d);
1244          for r in res_d do
1245            range := List([0..m/d-1],j->r+j*d);
1246            if IsSubset(res,range) then
1247              Add(redcls,[r,d]);
1248              res := Difference(res,range);
1249              if res = [] then break; fi;
1250              if Length(res) < m/d then break; fi;
1251            fi;
1252          od;
1253          if res = [] then break; fi;
1254        od;
1255
1256        cls := List(redcls,cl->[cl[1]*d_red+r_red,cl[2]*d_red]);
1257
1258      # Erroneous reduction process: doesn't cope with cases like
1259      # 0(3) U 1(6) U 5(6) = 1(2) U 0(6).
1260      #
1261      # repeat
1262      #   oldcls := ShallowCopy(cls);
1263      #   mods   := Set(List(oldcls,cl->cl[2]));
1264      #   for m in mods do
1265      #     resm := List(Set(Filtered(cls,cl->cl[2]=m)),c->c[1]);
1266      #     if Length(resm) >= 2 then
1267      #       for p in Set(Factors(m)) do
1268      #         for r in Filtered(resm,res->res<m/p) do
1269      #           # range := [r,r+m/p..m-m/p+r]; -> range restriction (<2^28)
1270      #           range := List([0..p-1],j->j*(m/p)+r);
1271      #           if IsSubset(resm,range) then
1272      #             cls := Difference(cls,List(range,r->[r,m]));
1273      #             Add(cls,[r,m/p]);
1274      #           fi;
1275      #         od;
1276      #       od;
1277      #     fi;
1278      #   od;
1279      #   cls := Set(cls);
1280      # until cls = oldcls;
1281
1282      fi;
1283
1284      for cl in cls do
1285        Add(coeffs,Concatenation(cl,coeffset[i]));
1286      od;
1287
1288    od;
1289
1290    # SortParallel(List(coeffs,c->[c[2],c[1]]),coeffs); -> problematic?
1291    coeffs  := Set(coeffs);
1292    modulus := Lcm(List(coeffs,c->c[2]));
1293
1294    MakeImmutable(coeffs);
1295    result := Objectify( NewType( RcwaMappingsOfZFamily,
1296                                  IsRcwaMappingOfZ
1297                              and IsRcwaMappingSparseRep ),
1298                         rec( modulus := modulus,
1299                              coeffs  := coeffs ) );
1300    SetSource(result,Integers);
1301    SetRange (result,Integers);
1302
1303    return result;
1304  end );
1305
1306#############################################################################
1307##
1308#S  Conversion of rcwa mappings between standard and sparse representation. /
1309##
1310#############################################################################
1311
1312#############################################################################
1313##
1314#M  SparseRepresentation( <f> )
1315##
1316InstallMethod( SparseRepresentation,
1317               "for rcwa mappings in standard representation (RCWA)",
1318               true, [ IsRcwaMappingInStandardRep ], 0,
1319
1320  function ( f )
1321
1322    local  result, attrs, attrsetters, props, propsetters, attr, prop,
1323           R, coeffs, src, sparse, cl, r, m, c, i;
1324
1325    R      := Source(f);
1326    coeffs := f!.coeffs;
1327
1328    src := Set(Flat(List(LargestSourcesOfAffineMappings(f),
1329                         AsUnionOfFewClasses)));
1330    sparse := [];
1331    for cl in src do
1332      r := Residue(cl); m := Modulus(cl);
1333      if   IsRcwaMappingOfZ(f)
1334      then c := coeffs[r+1];
1335      else c := coeffs[Position(AllResidues(R,m),r)]; fi;
1336      Add(sparse,[r,m,c[1],c[2],c[3]]);
1337    od;
1338
1339    sparse := Set(sparse);
1340
1341    result := Objectify(NewType(FamilyObj(f),RcwaMappingsType(R)
1342                                         and IsRcwaMappingSparseRep),
1343                        rec(modulus := f!.modulus, coeffs := sparse));
1344
1345    # Copy over representation-independent attributes and properties.
1346
1347    attrs := List( Intersection( RCWA_REP_INDEPENDENT_ATTRIBUTES,
1348                                 KnownAttributesOfObject( f ) ),
1349                   ValueGlobal );
1350    attrsetters := List(attrs,Setter);
1351    for i in [1..Length(attrs)] do attrsetters[i](result,attrs[i](f)); od;
1352
1353    props := List( Intersection( RCWA_REP_INDEPENDENT_PROPERTIES,
1354                                 KnownPropertiesOfObject( f ) ),
1355                   ValueGlobal );
1356    propsetters := List(props,Setter);
1357    for i in [1..Length(props)] do propsetters[i](result,props[i](f)); od;
1358
1359    return result;
1360  end );
1361
1362#############################################################################
1363##
1364#M  SparseRepresentation( <f> )
1365##
1366InstallMethod( SparseRepresentation,
1367               "for rcwa mappings in sparse representation (RCWA)",
1368               true, [ IsRcwaMappingInSparseRep ], 0, f -> f );
1369
1370#############################################################################
1371##
1372#M  StandardRepresentation( <f> )
1373##
1374InstallMethod( StandardRepresentation,
1375               "for rcwa mappings in sparse representation (RCWA)",
1376               true, [ IsRcwaMappingInSparseRep ], 0,
1377
1378  function ( f )
1379
1380    local  result, attrs, attrsetters, props, propsetters,
1381           R, modulus, coeffs, src, sparse, res, cl, r, m, c, n, i;
1382
1383    R       := Source(f);
1384    modulus := f!.modulus;
1385    sparse  := f!.coeffs;
1386
1387    if IsRing(R) then
1388      coeffs := List([1..NumberOfResidues(R,modulus)],r->[1,0,1]*One(R));
1389    elif IsZxZ(R) then
1390      coeffs := List([1..NumberOfResidues(R,modulus)],
1391                     r->[[[1,0],[0,1]],[0,0],1]);
1392    fi;
1393
1394    res := AllResidues(R,modulus);
1395    for i in [1..Length(sparse)] do
1396      c := sparse[i];
1397      r := c[1]; m := c[2];
1398      if IsRcwaMappingOfZ(f) then
1399        for n in [r+1,r+m+1..modulus-m+r+1] do
1400          coeffs[n] := c{[3..5]};
1401        od;
1402      else
1403        for n in PositionsProperty(res,el->el mod m = r) do
1404          coeffs[n] := c{[3..5]};
1405        od;
1406      fi;
1407    od;
1408
1409    result := Objectify(NewType(FamilyObj(f),RcwaMappingsType(R)
1410                                         and IsRcwaMappingStandardRep),
1411                        rec( modulus := modulus, coeffs := coeffs ));
1412
1413    # Copy over representation-independent attributes and properties.
1414
1415    attrs := List( Intersection( RCWA_REP_INDEPENDENT_ATTRIBUTES,
1416                                 KnownAttributesOfObject( f ) ),
1417                   ValueGlobal );
1418    attrsetters := List(attrs,Setter);
1419    for i in [1..Length(attrs)] do attrsetters[i](result,attrs[i](f)); od;
1420
1421    props := List( Intersection( RCWA_REP_INDEPENDENT_PROPERTIES,
1422                                 KnownPropertiesOfObject( f ) ),
1423                   ValueGlobal );
1424    propsetters := List(props,Setter);
1425    for i in [1..Length(props)] do propsetters[i](result,props[i](f)); od;
1426
1427    return result;
1428  end );
1429
1430#############################################################################
1431##
1432#M  StandardRepresentation( <f> )
1433##
1434InstallMethod( StandardRepresentation,
1435               "for rcwa mappings in standard representation (RCWA)",
1436               true, [ IsRcwaMappingInStandardRep ], 0, f -> f );
1437
1438#############################################################################
1439##
1440#S  ExtRepOfObj / ObjByExtRep for rcwa mappings. ////////////////////////////
1441##
1442#############################################################################
1443
1444#############################################################################
1445##
1446#M  ExtRepOfObj( <f> ) . . . . . . . . . . . . . . . . . .  for rcwa mappings
1447##
1448InstallMethod( ExtRepOfObj,
1449               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
1450               f -> [ Modulus( f ), ShallowCopy( Coefficients( f ) ) ] );
1451
1452#############################################################################
1453##
1454#M  ExtRepOfObj( <ct> ) . . . . . . . . . . . . . .  for class transpositions
1455##
1456InstallMethod( ExtRepOfObj,
1457               "for class transpositions (RCWA)", true,
1458               [ IsRcwaMapping and IsClassTransposition ], 0,
1459
1460  function ( ct )
1461
1462    local  cls;
1463
1464    cls := TransposedClasses(ct);
1465    return [ Residue(cls[1]), Mod(cls[1]), Residue(cls[2]), Mod(cls[2]) ];
1466  end );
1467
1468#############################################################################
1469##
1470#M  ObjByExtRep( <fam>, <l> ) . rcwa mapping, by list [ <modulus>, <coeffs> ]
1471##
1472InstallMethod( ObjByExtRep,
1473               "rcwa mapping, by list [ <modulus>, <coefficients> ] (RCWA)",
1474               ReturnTrue, [ IsFamily, IsList ], 0,
1475
1476  function ( fam, l )
1477
1478    local  R;
1479
1480    if not HasUnderlyingRing(fam) or Length(l) <> 2 then TryNextMethod(); fi;
1481    R := UnderlyingRing(fam);
1482    if fam <> RcwaMappingsFamily(R) then TryNextMethod(); fi;
1483    if   ForAll(l[2],c->IsList(c) and Length(c)=5)
1484    then return RcwaMappingNC(l[2]); # sparse rep., of Z
1485    else return RcwaMappingNC(R,l[1],l[2]); fi;
1486  end );
1487
1488#############################################################################
1489##
1490#S  Creating new rcwa mappings from rcwa mappings of the same ring. /////////
1491##
1492#############################################################################
1493
1494#############################################################################
1495##
1496#M  PiecewiseMapping( <sources>, <maps> ) . . . . . .  for rcwa mappings of Z
1497##
1498InstallMethod( PiecewiseMapping,
1499               "for rcwa mappings of Z (RCWA)",
1500               ReturnTrue, [ IsList, IsList ], 0,
1501
1502  function ( sources, maps )
1503
1504    local  map, coeffs, f, c, S, t, cls, cl, i;
1505
1506    if  Length(sources) <> Length(maps)
1507      or not ForAll(sources,IsListOrCollection)
1508      or not ForAll(maps,IsMapping)
1509    then return fail; fi;
1510    if   not ForAll(sources,IsResidueClassUnionOfZ)
1511      or not ForAll(maps,IsRcwaMappingOfZ)
1512      or not IsIntegers(Union(sources))
1513      or Sum(List(sources,Density)) <> 1
1514    then TryNextMethod(); fi;
1515
1516    maps := List(maps,SparseRep);
1517    coeffs := [];
1518    for i in [1..Length(maps)] do
1519      S := sources[i];
1520      f := maps[i];
1521      c := Coefficients(f);
1522      for t in c do
1523        cls := AsUnionOfFewClasses(Intersection(ResidueClass(t[1],t[2]),S));
1524        for cl in cls do
1525          Add(coeffs,[Residue(cl),Modulus(cl),t[3],t[4],t[5]]);
1526        od;
1527      od;
1528    od;
1529    map := RcwaMapping(coeffs);
1530    return SparseRep(map);
1531  end );
1532
1533#############################################################################
1534##
1535#S  Creating new rcwa mappings from rcwa mappings of different rings. ///////
1536##
1537#############################################################################
1538
1539#############################################################################
1540##
1541#F  LocalizedRcwaMapping( <f>, <p> )
1542##
1543InstallGlobalFunction( LocalizedRcwaMapping,
1544
1545  function ( f, p )
1546    if   not IsRcwaMappingOfZ(f) or not IsInt(p) or not IsPrimeInt(p)
1547    then Error("usage: see ?LocalizedRcwaMapping( f, p )\n"); fi;
1548    return SemilocalizedRcwaMapping( f, [ p ] );
1549  end );
1550
1551#############################################################################
1552##
1553#F  SemilocalizedRcwaMapping( <f>, <pi> )
1554##
1555InstallGlobalFunction( SemilocalizedRcwaMapping,
1556
1557  function ( f, pi )
1558    if    IsRcwaMappingOfZ(f) and IsList(pi) and ForAll(pi,IsPosInt)
1559      and ForAll(pi,IsPrimeInt) and IsSubset(pi,Factors(Modulus(f)))
1560    then return RcwaMapping(Z_pi(pi),ShallowCopy(Coefficients(f)));
1561    else Error("usage: see ?SemilocalizedRcwaMapping( f, pi )\n"); fi;
1562  end );
1563
1564#############################################################################
1565##
1566#M  ProjectionsToCoordinates( <f> ) . . . . . . . .  for rcwa mappings of Z^2
1567##
1568InstallMethod( ProjectionsToCoordinates,
1569               "rcwa mapping of Z^2 to two rcwa mappings of Z (RCWA)", true,
1570               [ IsRcwaMappingOfZxZ ], 0,
1571
1572  function ( f )
1573
1574    local  m, mf, mg, c, cf, cg, res, t, t1, t2, r, i;
1575
1576    m := Modulus(f); c := Coefficients(f);
1577    res := AllResidues(Integers^2,m);
1578    mf := m[1][1]; mg := m[2][2]; cf := []; cg := [];
1579    for i in [1..Length(res)] do
1580      t := c[i]; r := res[i];
1581      t1 := [t[1][1][1],t[2][1],t[3]]; t1 := t1/Gcd(t1);
1582      t2 := [t[1][2][2],t[2][2],t[3]]; t2 := t2/Gcd(t2);
1583      if   not IsBound(cf[r[1]+1]) then cf[r[1]+1] := t1;
1584      elif cf[r[1]+1] <> t1        then return fail; fi;
1585      if   not IsBound(cg[r[2]+1]) then cg[r[2]+1] := t2;
1586      elif cg[r[2]+1] <> t2        then return fail; fi;
1587      if t[1][1][2] <> 0 or t[1][2][1] <> 0 then return fail; fi;
1588    od;
1589    return [ RcwaMapping(cf), RcwaMapping(cg) ];
1590  end );
1591
1592#############################################################################
1593##
1594#M  Projection( <f>, <coord> ) .  proj. of an rcwa mapping of Z^2 to 1 coord.
1595##
1596InstallOtherMethod( Projection,
1597                    "for rcwa mappings of Z^2 (RCWA)",
1598                    ReturnTrue, [ IsRcwaMappingOfZxZ, IsPosInt ], 0,
1599
1600  function ( f, coord )
1601
1602    local  m, c, c_proj, proj;
1603
1604    if not coord in [1,2] then return fail; fi; # there are only 2 coord's
1605
1606    proj := ProjectionsToCoordinates(f); # maybe even both projections exist
1607    if proj <> fail then return proj[coord]; fi;
1608
1609    m := Modulus(f); # check whether the choice of the affine partial mapping
1610                     # depends only on the specified coordinate:
1611    if   m[1][2] <> 0 or m[2][1] <> 0 or m[3-coord][3-coord] <> 1
1612    then return fail; fi;
1613
1614    c := Coefficients(f); # check for dependency on other coordinate:
1615    if not ForAll(c,t->t[1][3-coord][coord]=0) then return fail; fi;
1616
1617    # build coefficient list in one dimension:
1618    c_proj := List(c,t->[t[1][coord][coord],t[2][coord],t[3]]);
1619
1620    return RcwaMapping(c_proj);
1621  end );
1622
1623#############################################################################
1624##
1625#S  The automorphism switching action on negative and nonnegative integers. /
1626##
1627#############################################################################
1628
1629#############################################################################
1630##
1631#M  Mirrored( <f> ) . . . . . . . . . for rcwa mappings of Z in standard rep.
1632#M  Mirrored( <f> ) . . . . . . . . . for rcwa mappings of Z in sparse rep.
1633##
1634InstallOtherMethod( Mirrored,
1635                    "for rcwa mappings of Z in standard rep. (RCWA)",
1636                    true, [ IsRcwaMappingOfZInStandardRep ], 0,
1637                    f -> f^RcwaMapping( [ [ -1, -1, 1 ] ] ) );
1638InstallOtherMethod( Mirrored,
1639                    "for rcwa mappings of Z in sparse rep. (RCWA)",
1640                    true, [ IsRcwaMappingOfZInSparseRep ], 0,
1641                    f -> f^RcwaMapping( [ [ 0, 1, -1, -1, 1 ] ] ) );
1642
1643#############################################################################
1644##
1645#S  Constructors and basic methods for special types of rcwa permutations. //
1646##
1647#############################################################################
1648
1649#############################################################################
1650##
1651#F  ClassShift( <R>, <r>, <m> ) . . . . . . . . . . . . . class shift nu_r(m)
1652#F  ClassShift( <r>, <m> )  . . . . . . . . . . . . . . . . . . . . .  (dito)
1653#F  ClassShift( <R>, <cl> ) . . . . . .  class shift nu_r(m), where cl = r(m)
1654#F  ClassShift( <cl> )  . . . . . . . . . . . . . . . . . . . . . . .  (dito)
1655#F  ClassShift( <R> ) . . . . . . . . . . . . .  class shift nu_R: n -> n + 1
1656##
1657##  (Enclosing the argument list in list brackets is permitted.)
1658##
1659InstallGlobalFunction( ClassShift,
1660
1661  function ( arg )
1662
1663    local  result, R, coeff, idcoeff, res, pos, r, m, latex;
1664
1665    if Length(arg) = 1 and IsList(arg[1]) then arg := arg[1]; fi;
1666
1667    if   IsZxZ(arg[1])
1668      or IsRowVector(arg[1]) and Length(arg[1]) = 2 and ForAll(arg[1],IsInt)
1669      or IsResidueClassOfZxZ(arg[1])
1670    then return CallFuncList(ClassShiftOfZxZ,arg); fi;
1671
1672    if not Length(arg) in [1..3]
1673      or     Length(arg) = 1 and not IsResidueClass(arg[1])
1674      or     Length(arg) = 2
1675         and not (   ForAll(arg,IsRingElement)
1676                  or     IsRing(arg[1])
1677                     and IsResidueClass(arg[2])
1678                     and arg[1] = UnderlyingRing(FamilyObj(arg[2])))
1679      or     Length(arg) = 3
1680         and not (    IsRing(arg[1])
1681                  and IsSubset(arg[1],arg{[2,3]}))
1682    then Error("usage: see ?ClassShift( r, m )\n"); fi;
1683
1684    if IsRing(arg[1]) then R := arg[1]; arg := arg{[2..Length(arg)]}; fi;
1685    if   IsBound(R) and IsEmpty(arg)
1686    then arg := [0,1] * One(R);
1687    elif IsResidueClass(arg[1])
1688    then if not IsBound(R) then R := UnderlyingRing(FamilyObj(arg[1])); fi;
1689         arg := [Residue(arg[1]),Modulus(arg[1])] * One(R);
1690    elif not IsBound(R) then R := DefaultRing(arg[2]); fi;
1691    arg := arg * One(R); # Now we know R, and we have arg = [r,m].
1692
1693    m          := StandardAssociate(R,arg[2]);
1694    r          := arg[1] mod m;
1695    res        := AllResidues(R,m);
1696    idcoeff    := [1,0,1]*One(R);
1697    coeff      := List(res,r->idcoeff);
1698    pos        := PositionSorted(res,r);
1699    coeff[pos] := [1,m,1]*One(R);
1700    result     := RcwaMapping(R,m,coeff);
1701    SetIsClassShift(result,true); SetIsBijective(result,true);
1702    if   Characteristic(R) = 0
1703    then SetOrder(result,infinity);
1704    else SetOrder(result,Characteristic(R)); fi;
1705    SetIsTame(result,true);
1706    SetBaseRoot(result,result); SetPowerOverBaseRoot(result,1);
1707    if IsIntegers(R) then
1708      SetSmallestRoot(result,result); SetPowerOverSmallestRoot(result,1);
1709    fi;
1710    SetFactorizationIntoCSCRCT(result,[result]);
1711
1712    latex := ValueOption("LaTeXString");
1713    if latex = fail then
1714      if IsOne(m) then
1715        SetLaTeXString(result,"\\nu");
1716      else
1717        SetLaTeXString(result,Concatenation("\\nu_{",String(r),"(",
1718                                                     String(m),")}"));
1719      fi;
1720    elif not IsEmpty(latex) then SetLaTeXString(result,latex); fi;
1721
1722    return result;
1723  end );
1724
1725#############################################################################
1726##
1727#F  ClassShiftOfZxZ( <R>, <r>, <m>, <coord> )  class shift nu_r(m),c; c=coord
1728#F  ClassShiftOfZxZ( <r>, <m>, <coord> )  . . . . . . . . . . . . . .  (dito)
1729#F  ClassShiftOfZxZ( <R>, <cl>, <coord> ) . .  class shift nu_r(m),c; cl=r(m)
1730#F  ClassShiftOfZxZ( <cl>, <coord> )  . . . . . . . . . . . . . . . .  (dito)
1731#F  ClassShiftOfZxZ( <R>, <coord> ) . . . . . . . . . . .  class shift nu_R_c
1732##
1733##  This function is called by `ClassShift' if the first argument is either
1734##  Integers^2, a row vector of length 2 with integer entries or a residue
1735##  class of Integers^2. Enclosing the argument list in list brackets is
1736##  permitted.
1737##
1738InstallGlobalFunction( ClassShiftOfZxZ,
1739
1740  function ( arg )
1741
1742    local  result, R, M, r, m, coord, cl, coeff, idcoeff, res, pos, latex;
1743
1744    R := Integers^2; M := FullMatrixAlgebra(Integers,2);
1745
1746    if Length(arg) = 1 and IsList(arg[1]) then arg := arg[1]; fi;
1747
1748    coord := arg[Length(arg)]; arg := arg{[1..Length(arg)-1]};
1749    if IsZxZ(arg[1]) then arg := arg{[2..Length(arg)]}; fi;
1750
1751    if   not coord in [1,2]
1752      or not Length(arg) in [0..2]
1753      or Length(arg) = 1 and not IsResidueClassOfZxZ(arg[1])
1754      or Length(arg) = 2 and not (arg[1] in R and arg[2] in M)
1755    then Error("usage: see ?ClassShift( r, m, coord )\n"); fi;
1756
1757    if   arg = [] then cl := R;
1758    elif Length(arg) = 1 then cl := arg[1];
1759    else cl := ResidueClass(arg[1],arg[2]); fi;
1760
1761    r := Residue(cl); m := Modulus(cl);
1762
1763    res        := AllResidues(R,m);
1764    idcoeff    := [[[1,0],[0,1]],[0,0],1];
1765    coeff      := ListWithIdenticalEntries(Length(res),idcoeff);
1766    pos        := PositionSorted(res,r);
1767    coeff[pos] := [[[1,0],[0,1]],m[coord],1];
1768    result     := RcwaMapping(R,m,coeff);
1769    SetIsClassShift(result,true); SetIsBijective(result,true);
1770    SetOrder(result,infinity);
1771    SetIsTame(result,true);
1772    SetBaseRoot(result,result); SetPowerOverBaseRoot(result,1);
1773    SetFactorizationIntoCSCRCT(result,[result]);
1774
1775    latex := ValueOption("LaTeXString");
1776    if latex = fail then
1777      latex := Concatenation("\\nu_{",ViewString(cl),",",String(coord),"}");
1778      latex := ReplacedString(latex,"Z","\\mathbb{Z}");
1779      SetLaTeXString(result,latex);
1780    elif not IsEmpty(latex) then SetLaTeXString(result,latex); fi;
1781
1782    return result;
1783  end );
1784
1785#############################################################################
1786##
1787#M  IsClassShift( <sigma> ) . . . . . . . . . . . . . . . . for rcwa mappings
1788##
1789InstallMethod( IsClassShift,
1790               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
1791               sigma -> IsResidueClass(Support(sigma))
1792                        and sigma = ClassShift(Support(sigma)) );
1793
1794#############################################################################
1795##
1796#M  IsPowerOfClassShift( <sigma> ) . . . . . . . . . . for rcwa mappings of Z
1797##
1798InstallMethod( IsPowerOfClassShift, "for rcwa mappings of Z (RCWA)", true,
1799               [ IsRcwaMappingOfZ ], 0,
1800
1801  function ( sigma )
1802
1803    local  cl, c;
1804
1805    if not IsClassWiseOrderPreserving(sigma) then return false; fi;
1806    if IsSignPreserving(sigma) then return IsOne(sigma); fi;
1807    if not IsBijective(sigma) then return false; fi;
1808    cl := Support(sigma);
1809    if not IsResidueClass(cl) then return false; fi;
1810    if Mod(sigma) <> Mod(cl) then return false; fi;
1811    return true;
1812  end );
1813
1814#############################################################################
1815##
1816#M  String( <cs> ) . . . . . . . . . . . . . . . . . . . . . for class shifts
1817#M  ViewString( <cs> ) . . . . . . . . . . . . . . . . . . . for class shifts
1818#M  PrintObj( <cs> ) . . . . . . . . . . . . . . . . . . . . for class shifts
1819#M  ViewObj( <cs> )  . . . . . . . . . . . . . . . . . . . . for class shifts
1820##
1821InstallMethod( String, "for class shifts (RCWA)", true,
1822               [ IsRcwaMapping and IsClassShift ], SUM_FLAGS,
1823
1824  function ( cs )
1825
1826    local  str;
1827
1828    if IsRcwaMappingOfZ(cs) then
1829      str := Concatenation(List(["ClassShift(",Residue(Support(cs)),",",
1830                                 Modulus(Support(cs))],String));
1831    else
1832      str := Concatenation(List(["ClassShift(",Source(cs),",",
1833                                 Residue(Support(cs)),",",
1834                                 Modulus(Support(cs))],String));
1835    fi;
1836    if IsRcwaMappingOfZxZ(cs) then
1837      Append(str,",");
1838      Append(str,String(PositionNonZero(Residue(Support(cs))^cs
1839                                       -Residue(Support(cs)))));
1840    fi;
1841    return Concatenation(BlankFreeString(str),")");
1842  end );
1843
1844InstallMethod( ViewString, "for class shifts (RCWA)", true,
1845               [ IsRcwaMapping and IsClassShift ], SUM_FLAGS,
1846
1847  function ( cs )
1848
1849    local  cl, name, suppname;
1850
1851    if   ValueOption("PrintNotation") = true
1852    then return String(cs); fi;
1853    if   ValueOption("AbridgedNotation") = true
1854    then name := "cs"; else name := "ClassShift"; fi;
1855    cl := Support(cs);
1856    if IsRing(cl) or IsZxZ(cl) then suppname := RingToString(cl);
1857                               else suppname := ViewString(cl); fi;
1858    if   IsRing(Source(cs)) then
1859      return Concatenation(List([name,"( ",suppname," )"],String));
1860    elif IsRcwaMappingOfZxZ(cs) then
1861      return Concatenation(List([name,"( ",suppname,", ",
1862                                 PositionNonZero(Residue(Support(cs))^cs
1863                                                -Residue(Support(cs)))," )"],
1864                                String));
1865    else TryNextMethod(); fi;
1866  end );
1867
1868InstallMethod( ViewString, "for powers of class shifts of Z (RCWA)", true,
1869               [ IsRcwaMappingOfZ and IsPowerOfClassShift ], SUM_FLAGS,
1870
1871  function ( cs )
1872    if IsClassShift(cs) then TryNextMethod(); fi;
1873    return Concatenation(ViewString(ClassShift(Support(cs))),"^",
1874                         String(First(List(Coefficients(cs),c->c[2]),
1875                                      b->b<>0)/Modulus(cs)));
1876  end );
1877
1878InstallMethod( PrintObj, "for class shifts (RCWA)", true,
1879               [ IsRcwaMapping and IsClassShift ], SUM_FLAGS+10,
1880               function ( cs ) Print( String( cs ) ); end );
1881
1882InstallMethod( ViewObj, "for class shifts (RCWA)", true,
1883               [ IsRcwaMapping and IsClassShift ], 20,
1884               function ( cs ) Print( ViewString( cs ) ); end );
1885
1886InstallMethod( ViewObj, "for powers of class shifts (RCWA)", true,
1887               [ IsRcwaMapping and IsPowerOfClassShift ], 20,
1888               function ( cs ) Print( ViewString( cs ) ); end );
1889
1890#############################################################################
1891##
1892#F  ClassReflection( <R>, <r>, <m> )  . . . .  class reflection varsigma_r(m)
1893#F  ClassReflection( <r>, <m> ) . . . . . . . . . . . . . . . . . . .  (dito)
1894#F  ClassReflection( <R>, <cl> )  . class reflection varsigma_r(m), cl = r(m)
1895#F  ClassReflection( <cl> ) . . . . . . . . . . . . . . . . . . . . .  (dito)
1896#F  ClassReflection( <R> )  . . . . . .  class reflection varsigma_R: n -> -n
1897##
1898##  (Enclosing the argument list in list brackets is permitted.)
1899##
1900InstallGlobalFunction( ClassReflection,
1901
1902  function ( arg )
1903
1904    local  result, R, coeff, idcoeff, res, pos, r, m, latex;
1905
1906    if Length(arg) = 1 and IsList(arg[1]) then arg := arg[1]; fi;
1907
1908    if   IsZxZ(arg[1])
1909      or IsRowVector(arg[1]) and Length(arg[1]) = 2 and ForAll(arg[1],IsInt)
1910      or IsResidueClassOfZxZ(arg[1])
1911    then
1912      return CallFuncList(ClassRotationOfZxZ,
1913                          Concatenation(arg,[[[-1,0],[0,-1]]]));
1914    fi;
1915
1916    if not Length(arg) in [1..3]
1917      or     Length(arg) = 1 and not IsResidueClass(arg[1])
1918      or     Length(arg) = 2
1919         and not (   ForAll(arg,IsRingElement)
1920                  or     IsRing(arg[1])
1921                     and IsResidueClass(arg[2])
1922                     and arg[1] = UnderlyingRing(FamilyObj(arg[2])))
1923      or     Length(arg) = 3
1924         and not (    IsRing(arg[1])
1925                  and IsSubset(arg[1],arg{[2,3]}))
1926    then Error("usage: see ?ClassReflection( r, m )\n"); fi;
1927
1928    if IsRing(arg[1]) then R := arg[1]; arg := arg{[2..Length(arg)]}; fi;
1929    if   IsBound(R) and IsEmpty(arg)
1930    then arg := [0,1] * One(R);
1931    elif IsResidueClass(arg[1])
1932    then if not IsBound(R) then R := UnderlyingRing(FamilyObj(arg[1])); fi;
1933         arg := [Residue(arg[1]),Modulus(arg[1])] * One(R);
1934    elif not IsBound(R) then R := DefaultRing(arg[2]); fi;
1935    if Characteristic(R) = 2 then return One(RCWA(R)); fi; # Now we know R...
1936    arg := arg * One(R); # ...and we have arg = [r,m].
1937
1938    m          := StandardAssociate(R,arg[2]);
1939    r          := arg[1] mod m;
1940    res        := AllResidues(R,m);
1941    idcoeff    := [1,0,1]*One(R);
1942    coeff      := List(res,r->idcoeff);
1943    pos        := PositionSorted(res,r);
1944    coeff[pos] := [-1,2*r,1]*One(R);
1945    result     := RcwaMapping(R,m,coeff);
1946    SetIsClassReflection(result,true);
1947    SetRotationFactor(result,-1);
1948    SetIsBijective(result,true);
1949    SetOrder(result,2); SetIsTame(result,true);
1950    SetFactorizationIntoCSCRCT(result,[result]);
1951
1952    latex := ValueOption("LaTeXString");
1953    if latex = fail then
1954      if IsOne(m) then
1955        SetLaTeXString(result,"\\varsigma");
1956      else
1957        SetLaTeXString(result,Concatenation("\\varsigma_{",String(r),"(",
1958                                                           String(m),")}"));
1959      fi;
1960    elif not IsEmpty(latex) then SetLaTeXString(result,latex); fi;
1961
1962    return result;
1963  end );
1964
1965#############################################################################
1966##
1967#M  IsClassReflection( <sigma> ) . . . . . . . . . . . . .  for rcwa mappings
1968##
1969InstallMethod( IsClassReflection,
1970               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
1971               sigma -> IsResidueClass(Union(Support(sigma),
1972                                       ExcludedElements(Support(sigma)))) and
1973               sigma = ClassReflection(Union(Support(sigma),
1974                                       ExcludedElements(Support(sigma)))) );
1975
1976#############################################################################
1977##
1978#M  String( <cr> ) . . . . . . . . . . . . . . . . . .  for class reflections
1979#M  ViewString( <cr> ) . . . . . . . . . . . . . . . .  for class reflections
1980##
1981InstallMethod( String, "for class reflections (RCWA)", true,
1982               [ IsRcwaMapping and IsClassReflection ], SUM_FLAGS,
1983
1984  function ( cr )
1985    if IsRcwaMappingOfZ(cr) then
1986      return Concatenation(List(["ClassReflection(",
1987                                 Residue(Support(cr)),",",
1988                                 Modulus(Support(cr)),")"],BlankFreeString));
1989    else
1990      return Concatenation(List(["ClassReflection(",Source(cr),",",
1991                                 Residue(Support(cr)),",",
1992                                 Modulus(Support(cr)),")"],BlankFreeString));
1993    fi;
1994  end );
1995
1996InstallMethod( ViewString, "for class reflections (RCWA)", true,
1997               [ IsRcwaMapping and IsClassReflection ], SUM_FLAGS,
1998
1999  function ( cr )
2000
2001    local  cl, name, suppname;
2002
2003    if   ValueOption("PrintNotation") = true
2004    then return String(cr); fi;
2005    if   ValueOption("AbridgedNotation") = true
2006    then name := "cr"; else name := "ClassReflection"; fi;
2007    cl := Union(Support(cr),ExcludedElements(Support(cr)));
2008    if IsRing(cl) or IsZxZ(cl) then suppname := RingToString(cl);
2009                               else suppname := ViewString(cl); fi;
2010    return Concatenation(List([name,"( ",suppname," )"],String));
2011  end );
2012
2013#############################################################################
2014##
2015#F  ClassRotation( <R>, <r>, <m>, <u> ) . . . . . class rotation rho_(r(m),u)
2016#F  ClassRotation( <r>, <m>, <u> )  . . . . . . . . . . . . . . . . .  (dito)
2017#F  ClassRotation( <R>, <cl>, <u> ) .  class rotation rho_(r(m),u), cl = r(m)
2018#F  ClassRotation( <cl>, <u> )  . . . . . . . . . . . . . . . . . . .  (dito)
2019#F  ClassRotation( <R>, <u> ) . . . . . . . class rotation rho_(R,u): n -> un
2020##
2021##  (Enclosing the argument list in list brackets is permitted.)
2022##
2023InstallGlobalFunction( ClassRotation,
2024
2025  function ( arg )
2026
2027    local  result, R, coeff, idcoeff, res, pos, r, m, u, latex;
2028
2029    if Length(arg) = 1 and IsList(arg[1]) then arg := arg[1]; fi;
2030
2031    if   IsZxZ(arg[1])
2032      or IsRowVector(arg[1]) and Length(arg[1]) = 2 and ForAll(arg[1],IsInt)
2033      or IsResidueClassOfZxZ(arg[1])
2034    then return CallFuncList(ClassRotationOfZxZ,arg); fi;
2035
2036    if not Length(arg) in [2..4]
2037      or     Length(arg) = 2
2038         and not (    IsResidueClass(arg[1])
2039                  and IsCollsElms(FamilyObj(arg[1]),FamilyObj(arg[2])))
2040      or     Length(arg) = 3
2041         and not (   ForAll(arg,IsRingElement)
2042                  or     IsRing(arg[1])
2043                     and IsResidueClass(arg[2])
2044                     and arg[1] = UnderlyingRing(FamilyObj(arg[2]))
2045                     and arg[3] in arg[1])
2046      or     Length(arg) = 4
2047         and not (    IsRing(arg[1])
2048                  and IsSubset(arg[1],arg{[2,3,4]}))
2049    then Error("usage: see ?ClassRotation( r, m, u )\n"); fi;
2050
2051    if IsRing(arg[1]) then R := arg[1]; arg := arg{[2..Length(arg)]}; fi;
2052    if   IsBound(R) and Length(arg) = 1
2053    then arg := [0,1,arg[1]] * One(R);
2054    elif IsResidueClass(arg[1])
2055    then if not IsBound(R) then R := UnderlyingRing(FamilyObj(arg[1])); fi;
2056         arg := [Residue(arg[1]),Modulus(arg[1]),arg[2]] * One(R);
2057    elif not IsBound(R) then R := DefaultRing(arg{[2,3]}); fi;
2058    arg := arg * One(R); # Now we know R, and we have arg = [r,m,u].
2059
2060    m          := StandardAssociate(R,arg[2]);
2061    r          := arg[1] mod m;
2062    u          := arg[3];
2063
2064    if   IsOne( u) then return One(RCWA(R));
2065    elif IsOne(-u) then return ClassReflection(ResidueClass(R,m,r)); fi;
2066
2067    res        := AllResidues(R,m);
2068    idcoeff    := [1,0,1]*One(R);
2069    coeff      := List(res,r->idcoeff);
2070    pos        := PositionSorted(res,r);
2071    coeff[pos] := [u,(1-u)*r,1]*One(R);
2072    result     := RcwaMapping(R,m,coeff);
2073    SetIsClassRotation(result,true);
2074    SetRotationFactor(result,u);
2075    SetIsBijective(result,true);
2076    SetOrder(result,Order(u)); SetIsTame(result,true);
2077    SetBaseRoot(result,result); SetPowerOverBaseRoot(result,1);
2078    SetFactorizationIntoCSCRCT(result,[result]);
2079
2080    latex := ValueOption("LaTeXString");
2081    if latex = fail then
2082      if IsOne(m) then
2083        SetLaTeXString(result,Concatenation("\\rho_{",String(u),"}"));
2084      else
2085        SetLaTeXString(result,Concatenation("\\rho_{",String(r),"(",
2086                                            String(m),"),",String(u),"}"));
2087      fi;
2088    elif not IsEmpty(latex) then SetLaTeXString(result,latex); fi;
2089
2090    return result;
2091  end );
2092
2093#############################################################################
2094##
2095#F  ClassRotationOfZxZ( ... ) . . . . . . . . . . . . . class rotation of Z^2
2096##
2097##  This function is called by `ClassRotation' if the first argument is
2098##  either Integers^2, a row vector of length 2 with integer entries or
2099##  a residue class of Integers^2. For recognized arguments, see there.
2100##
2101InstallGlobalFunction( ClassRotationOfZxZ,
2102
2103  function ( arg )
2104
2105    local  result, R, mats, r, m, u, uimg, M, U, Uimg, cl,
2106           coeff, idcoeff, res, pos, latex;
2107
2108    if Length(arg) = 1 and IsList(arg[1]) then arg := arg[1]; fi;
2109
2110    R := Integers^2; mats := FullMatrixAlgebra(Integers,2);
2111
2112    u := arg[Length(arg)]; arg := arg{[1..Length(arg)-1]};
2113    if IsZxZ(arg[1]) then arg := arg{[2..Length(arg)]}; fi;
2114
2115    if   not u in mats or not DeterminantMat(u) in [-1,1]
2116      or not Length(arg) in [0..2]
2117      or Length(arg) = 1 and not IsResidueClassOfZxZ(arg[1])
2118      or Length(arg) = 2 and not (arg[1] in R and arg[2] in mats)
2119    then Error("usage: see ?ClassRotation( r, m, u )\n"); fi;
2120
2121    if   IsOne(u) then return IdentityRcwaMappingOfZxZ; fi;
2122
2123    if   arg = [] then cl := R;
2124    elif Length(arg) = 1 then cl := arg[1];
2125    else cl := ResidueClass(arg[1],arg[2]); fi;
2126
2127    r := Residue(cl); m := Modulus(cl);
2128
2129    res     := AllResidues(R,m);
2130    idcoeff := [[[1,0],[0,1]],[0,0],1];
2131    coeff   := ListWithIdenticalEntries(Length(res),idcoeff);
2132
2133    M := NullMat(3,3);
2134    M{[1..2]}{[1..2]} := m;
2135    M[3][3] := 1;
2136    M[3]{[1..2]} := r;
2137
2138    U := NullMat(3,3);
2139    U{[1..2]}{[1..2]} := u;
2140    U[3][3] := 1;
2141
2142    Uimg := U^M;
2143    Uimg := Uimg * Lcm(List(Flat(Uimg),DenominatorRat));
2144    uimg := Uimg{[1..2]}{[1..2]};
2145
2146    pos        := PositionSorted(res,r);
2147    coeff[pos] := [uimg,Uimg[3]{[1..2]},Uimg[3][3]];
2148
2149    result     := RcwaMapping(R,m,coeff);
2150
2151    SetIsClassRotation(result,true);
2152    SetRotationFactor(result,u);
2153    if IsOne(-u) then SetIsClassReflection(result,true); fi;
2154    SetIsBijective(result,true);
2155    SetOrder(result,Order(u));
2156    SetIsTame(result,true);
2157    SetBaseRoot(result,result); SetPowerOverBaseRoot(result,1);
2158    SetFactorizationIntoCSCRCT(result,[result]);
2159
2160    latex := ValueOption("LaTeXString");
2161    if latex = fail then
2162      latex := Concatenation("\\rho_{",ViewString(cl),
2163                             ",",BlankFreeString(u),"}");
2164      latex := ReplacedString(latex,"Z","\\mathbb{Z}");
2165      SetLaTeXString(result,latex);
2166    elif not IsEmpty(latex) then SetLaTeXString(result,latex); fi;
2167
2168    return result;
2169  end );
2170
2171#############################################################################
2172##
2173#M  IsClassRotation( <sigma> ) . . . . . . . . . . . . . .  for rcwa mappings
2174##
2175InstallMethod( IsClassRotation,
2176               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
2177
2178  function ( sigma )
2179
2180    local  S, u, c;
2181
2182    S := Union(Support(sigma),ExcludedElements(Support(sigma)));
2183    if not IsResidueClass(S) then return false; fi;
2184    c := First(Coefficients(sigma),c->not IsOne(c[1]));
2185    if c = fail then return false; else u := c[1]; fi;
2186    if sigma = ClassRotation(S,u) then
2187      SetRotationFactor(sigma,u);
2188      return true;
2189    else return false; fi;
2190  end );
2191
2192#############################################################################
2193##
2194#M  IsClassRotation( <sigma> ) . . . . . . . . . . . .  for class reflections
2195##
2196InstallTrueMethod( IsClassRotation, IsClassReflection );
2197
2198#############################################################################
2199##
2200#M  String( <cr> ) . . . . . . . . . . . . . . . . . . .  for class rotations
2201#M  ViewString( <cr> ) . . . . . . . . . . . . . . . . .  for class rotations
2202#M  PrintObj( <cr> ) . . . . . . . . . . . . . . . . . .  for class rotations
2203#M  ViewObj( <cr> )  . . . . . . . . . . . . . . . . . .  for class rotations
2204##
2205InstallMethod( String, "for class rotations (RCWA)", true,
2206               [ IsRcwaMapping and IsClassRotation ], SUM_FLAGS-10,
2207               cr -> Concatenation(List(["ClassRotation(",Source(cr),",",
2208                                         Residue(Support(cr)),",",
2209                                         Modulus(Support(cr)),",",
2210                                         RotationFactor(cr),")"],
2211                                        BlankFreeString)));
2212
2213InstallMethod( ViewString, "for class rotations (RCWA)", true,
2214               [ IsRcwaMapping and IsClassRotation ], SUM_FLAGS-10,
2215
2216  function ( cr )
2217
2218    local  cl, name, suppname;
2219
2220    if   ValueOption("PrintNotation") = true
2221    then return String(cr); fi;
2222    if   ValueOption("AbridgedNotation") = true
2223    then name := "cr"; else name := "ClassRotation"; fi;
2224    cl := Union(Support(cr:BeQuiet),ExcludedElements(Support(cr)));
2225    if IsRing(cl) or IsZxZ(cl) then suppname := RingToString(cl);
2226                               else suppname := ViewString(cl); fi;
2227    return Concatenation(List([name,"( ",suppname,", ",
2228                               RotationFactor(cr)," )"],String));
2229  end );
2230
2231InstallMethod( PrintObj, "for class rotations (RCWA)", true,
2232               [ IsRcwaMapping and IsClassRotation ], SUM_FLAGS+10,
2233               function ( cr ) Print( String( cr ) ); end );
2234
2235InstallMethod( ViewObj, "for class rotations (RCWA)", true,
2236               [ IsRcwaMapping and IsClassRotation ], 20,
2237               function ( cr ) Print( ViewString( cr ) ); end );
2238
2239#############################################################################
2240##
2241#F  ClassTransposition( <R>, <r1>, <m1>, <r2>, <m2> ) . . class transposition
2242#F  ClassTransposition( <r1>, <m1>, <r2>, <m2> )            tau_r1(m1),r2(m2)
2243#F  ClassTransposition( <R>, <cl1>, <cl2> ) . . . dito, cl1=r1(m1) cl2=r2(m2)
2244#F  ClassTransposition( <cl1>, <cl2> )  . . . . . . . . . . . . . . .  (dito)
2245##
2246##  (Enclosing the argument list in list brackets is permitted.)
2247##
2248InstallGlobalFunction( ClassTransposition,
2249
2250  function ( arg )
2251
2252    local  result, is_usual_ct, type, R, r1, m1, r2, m2, cl1, cl2, h, latex;
2253
2254    if Length(arg) = 1 and IsList(arg[1]) then arg := arg[1]; fi;
2255
2256    if   IsZxZ(arg[1])
2257      or IsRowVector(arg[1]) and Length(arg[1]) = 2 and ForAll(arg[1],IsInt)
2258      or IsResidueClassOfZxZ(arg[1])
2259    then return CallFuncList(ClassTranspositionOfZxZ,arg); fi;
2260
2261    if not Length(arg) in [2..5]
2262      or     Length(arg) = 2 and not (ForAll(arg,IsResidueClass)
2263                                   or ForAll(arg,IsResidueClassWithFixedRep))
2264      or     Length(arg) = 3
2265         and not (     IsRing(arg[1]) and ForAll(arg{[2,3]},IsResidueClass)
2266                   and arg[1] = UnderlyingRing(FamilyObj(arg[2]))
2267                   and arg[1] = UnderlyingRing(FamilyObj(arg[3])))
2268      or     Length(arg) = 4 and not ForAll(arg,IsRingElement)
2269      or     Length(arg) = 5 and not (    IsRing(arg[1])
2270                                      and IsSubset(arg[1],arg{[2..5]}))
2271    then Error("usage: see ?ClassTransposition( r1, m1, r2, m2 )\n"); fi;
2272
2273    if IsRing(arg[1]) then R := arg[1]; arg := arg{[2..Length(arg)]}; fi;
2274    if   IsResidueClass(arg[1]) or IsResidueClassWithFixedRep(arg[1])
2275    then if not IsBound(R) then R := UnderlyingRing(FamilyObj(arg[1])); fi;
2276         arg := [Residue(arg[1]),Modulus(arg[1]),
2277                 Residue(arg[2]),Modulus(arg[2])] * One(R);
2278    elif not IsBound(R) then R := DefaultRing(arg{[2,4]}); fi;
2279    arg := arg * One(R); # Now we know R, and we have arg = [r1,m1,r2,m2].
2280
2281    r1 := arg[1]; m1 := arg[2]; r2 := arg[3]; m2 := arg[4];
2282
2283    if IsZero(m1*m2) or IsZero((r1-r2) mod Gcd(R,m1,m2)) then
2284      Error("ClassTransposition: The residue classes must be disjoint.\n");
2285    fi;
2286
2287    is_usual_ct := m1 = StandardAssociate(R,m1)
2288               and m2 = StandardAssociate(R,m2)
2289               and r1 mod m1 = r1 and r2 mod m2 = r2;
2290
2291    if   [m1,r1] > [m2,r2]
2292    then h := r1; r1 := r2; r2 := h; h := m1; m1 := m2; m2 := h; fi;
2293
2294    if is_usual_ct then
2295      cl1 := ResidueClass(R,m1,r1);
2296      cl2 := ResidueClass(R,m2,r2);
2297    else
2298      cl1 := ResidueClassWithFixedRepresentative(R,m1,r1);
2299      cl2 := ResidueClassWithFixedRepresentative(R,m2,r2);
2300    fi;
2301
2302    result := RcwaMapping([[cl1,cl2]]);
2303
2304    if is_usual_ct then SetIsClassTransposition(result,true); fi;
2305    SetIsGeneralizedClassTransposition(result,true);
2306    SetTransposedClasses(result,[cl1,cl2]);
2307
2308    latex := ValueOption("LaTeXString");
2309    if latex = fail then
2310      if IsIntegers(R) and [m1,m2] = [2,2] then
2311        SetLaTeXString(result,"\\tau");
2312      else
2313        SetLaTeXString(result,Concatenation("\\tau_{",
2314                                            String(r1),"(",String(m1),"),",
2315                                            String(r2),"(",String(m2),")}"));
2316      fi;
2317    elif not IsEmpty(latex) then SetLaTeXString(result,latex); fi;
2318
2319    if is_usual_ct then SetFactorizationIntoCSCRCT(result,[result]); fi;
2320
2321    return result;
2322  end );
2323
2324#############################################################################
2325##
2326#F  ClassTranspositionOfZxZ( ... ) . . . . . . . . class transposition of Z^2
2327##
2328##  This function is called by `ClassTransposition' if the first argument
2329##  is either Integers^2, a row vector of length 2 with integer entries
2330##  or a residue class of Integers^2. For recognized arguments, see there.
2331##
2332InstallGlobalFunction( ClassTranspositionOfZxZ,
2333
2334  function ( arg )
2335
2336    local  result, R, M, r1, m1, r2, m2, cl1, cl2, h, latex;
2337
2338    if Length(arg) = 1 and IsList(arg[1]) then arg := arg[1]; fi;
2339
2340    R := Integers^2; M := FullMatrixAlgebra(Integers,2);
2341
2342    if not Length(arg) in [2..5]
2343      or     Length(arg) = 2 and not ForAll(arg,IsResidueClassOfZxZ)
2344      or     Length(arg) = 3 and not (IsZxZ(arg[1])
2345                                  and ForAll(arg{[2,3]},IsResidueClassOfZxZ))
2346      or     Length(arg) = 4 and not ( arg[1] in R and arg[2] in M
2347                                   and arg[3] in R and arg[4] in M )
2348      or     Length(arg) = 5 and not ( IsZxZ(arg[1])
2349                                   and arg[2] in R and arg[3] in M
2350                                   and arg[4] in R and arg[5] in M )
2351    then Error("usage: see ?ClassTransposition( r1, m1, r2, m2 )\n"); fi;
2352
2353    if IsZxZ(arg[1]) then arg := arg{[2..Length(arg)]}; fi;
2354    if IsResidueClass(arg[1]) then
2355      arg := [Residue(arg[1]),Modulus(arg[1]),
2356              Residue(arg[2]),Modulus(arg[2])];
2357    fi; # Now we have arg = [r1,m1,r2,m2].
2358
2359    r1 := arg[1]; m1 := arg[2]; r2 := arg[3]; m2 := arg[4];
2360
2361    if   [m1,r1] > [m2,r2]
2362    then h := r1; r1 := r2; r2 := h; h := m1; m1 := m2; m2 := h; fi;
2363
2364    if DeterminantMat(m1*m2) = 0 then
2365      Error("ClassTransposition:\n",
2366            "The moduli of the residue classes must be invertible.\n");
2367    fi;
2368
2369    cl1 := ResidueClass(R,m1,r1);
2370    cl2 := ResidueClass(R,m2,r2);
2371
2372    if Intersection(cl1,cl2) <> [] then
2373      Error("ClassTransposition: The residue classes must be disjoint.\n");
2374    fi;
2375
2376    result := RcwaMapping([[cl1,cl2]]);
2377
2378    SetIsClassTransposition(result,true);
2379    SetIsGeneralizedClassTransposition(result,true);
2380    SetTransposedClasses(result,[cl1,cl2]);
2381
2382    latex := ValueOption("LaTeXString");
2383    if latex = fail then
2384      latex := Concatenation("\\tau_{",ViewString(cl1),",",
2385                                       ViewString(cl2),"}");
2386      latex := ReplacedString(latex,"Z","\\mathbb{Z}");
2387      SetLaTeXString(result,latex);
2388    elif not IsEmpty(latex) then SetLaTeXString(result,latex); fi;
2389
2390    SetFactorizationIntoCSCRCT(result,[result]);
2391
2392    return result;
2393  end );
2394
2395#############################################################################
2396##
2397#M  IsClassTransposition( <sigma> ) . . . . . . . . . . . . for rcwa mappings
2398##
2399InstallMethod( IsClassTransposition,
2400               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
2401
2402  function ( sigma )
2403
2404    local  cls;
2405
2406    if IsOne(sigma) then return false; fi;
2407    if not IsBijective(sigma) then return false; fi;
2408    if HasOrder(sigma) and Order(sigma) <> 2 then return false; fi;
2409    if   IsRcwaMappingStandardRep(sigma)
2410     and Length(Set(Coefficients(sigma))) > 3
2411    then return false; fi;
2412    if   IsRcwaMappingSparseRep(sigma)
2413     and Length(Set(Coefficients(sigma),c->c{[3..5]})) > 3
2414    then return false; fi;
2415    cls := AsUnionOfFewClasses(Support(sigma));
2416    if Length(cls) = 1 then cls := SplittedClass(cls[1],2); fi;
2417    if Length(cls) > 2 then return false; fi;
2418    if   sigma = ClassTransposition(cls)
2419    then SetTransposedClasses(sigma,cls);
2420         SetIsGeneralizedClassTransposition(sigma,true);
2421         return true;
2422    else return false; fi;
2423  end );
2424
2425#############################################################################
2426##
2427#M  IsClassTransposition( <sigma> ) . . . . . . . .  for rcwa mappings of Z^2
2428##
2429InstallMethod( IsClassTransposition,
2430               "for rcwa mappings of Z^2 (RCWA)", true,
2431               [ IsRcwaMappingOfZxZ ], 0,
2432
2433  function ( sigma )
2434
2435    local  cls, split;
2436
2437    if IsOne(sigma) then return false; fi;
2438    if Length(Set(Coefficients(sigma))) > 3 then return false; fi;
2439    cls := AsUnionOfFewClasses(Support(sigma:BeQuiet));
2440    if   Length(cls) = 1 then
2441      for split in List([[2,1],[1,2]],v->SplittedClass(cls[1],v)) do
2442        if sigma = ClassTransposition(split) then
2443          SetTransposedClasses(sigma,split);
2444          SetIsGeneralizedClassTransposition(sigma,true); return true;
2445        fi;
2446      od;
2447      return false;
2448    elif Length(cls) = 2 then
2449      if sigma = ClassTransposition(cls) then
2450        SetTransposedClasses(sigma,cls);
2451        SetIsGeneralizedClassTransposition(sigma,true); return true;
2452      fi;
2453    else return false; fi;
2454  end );
2455
2456#############################################################################
2457##
2458#M  IsGeneralizedClassTransposition( <sigma> ) . . . . . .  for rcwa mappings
2459##
2460InstallMethod( IsGeneralizedClassTransposition,
2461               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
2462
2463  function ( sigma )
2464
2465    local  cls, cls_fixedrep, affsrc, r1, m1, r2, m2;
2466
2467    if   HasIsClassTransposition(sigma) and IsClassTransposition(sigma)
2468    then return true; fi;
2469    if IsOne(sigma) or not IsBijective(sigma)
2470                    or Length(Set(Coefficients(sigma))) > 3
2471    then return false; fi;
2472    affsrc := LargestSourcesOfAffineMappings(sigma);
2473    if Length(affsrc) > 3 then return false; fi;
2474    cls := Filtered(affsrc,cl->IsResidueClass(cl)
2475                           and IsSubset(Support(sigma),cl));
2476    if Length(cls) <> 2 then return false; fi;
2477    if Permutation(sigma,cls) = (1,2) then
2478      m1 := Modulus(cls[1]); r1 := Residue(cls[1]);
2479      m2 := Modulus(cls[2]); r2 := r1^sigma;
2480      if not IsClassWiseOrderPreserving(sigma) then m2 := -m2; fi;
2481      cls_fixedrep := [ ResidueClassWithFixedRep(Source(sigma),m1,r1),
2482                        ResidueClassWithFixedRep(Source(sigma),m2,r2) ];
2483      Assert(4,sigma=ClassTransposition(cls_fixedrep));
2484      SetTransposedClasses(sigma,cls_fixedrep);
2485      return true;
2486    else return false; fi;
2487  end );
2488
2489#############################################################################
2490##
2491#M  TransposedClasses( <sigma> ) . . . . . . . . . . for class transpositions
2492##
2493InstallMethod( TransposedClasses,
2494               "for class transpositions (RCWA)", true, [ IsRcwaMapping ], 0,
2495
2496  function ( ct )
2497    if   IsClassTransposition(ct) or IsGeneralizedClassTransposition(ct)
2498    then return TransposedClasses(ct);
2499    else TryNextMethod(); fi;
2500  end );
2501
2502#############################################################################
2503##
2504#M  String( <ct> ) . . . . . . . . . . . . . . . . . for class transpositions
2505#M  ViewString( <ct> ) . . . . . . . . . . . . . . . for class transpositions
2506#M  PrintObj( <ct> ) . . . . . . . . . . . . . . . . for class transpositions
2507#M  ViewObj( <ct> )  . . . . . . . . . . . . . . . . for class transpositions
2508##
2509InstallMethod( String, "for class transpositions (RCWA)", true,
2510               [ IsRcwaMapping and IsGeneralizedClassTransposition ],
2511               SUM_FLAGS,
2512
2513  function ( ct )
2514
2515    local  type, cls, str;
2516
2517    cls := TransposedClasses(ct);
2518    if   ForAll(cls,IsResidueClass)
2519    then type := "ClassTransposition(";
2520    else type := "GeneralizedClassTransposition("; fi;
2521    if IsRcwaMappingOfZ(ct) then
2522      str := Concatenation(List([type,
2523                                 Residue(cls[1]),",",Modulus(cls[1]),",",
2524                                 Residue(cls[2]),",",Modulus(cls[2]),")"],
2525                                BlankFreeString));
2526    else
2527      str := Concatenation(List([type,Source(ct),",",
2528                                 Residue(cls[1]),",",Modulus(cls[1]),",",
2529                                 Residue(cls[2]),",",Modulus(cls[2]),")"],
2530                                BlankFreeString));
2531    fi;
2532    return BlankFreeString(str);
2533  end );
2534
2535InstallMethod( ViewString, "for class transpositions (RCWA)", true,
2536               [ IsRcwaMapping and IsGeneralizedClassTransposition ],
2537               SUM_FLAGS,
2538
2539  function ( ct )
2540
2541    local  cls, name;
2542
2543    if ValueOption("PrintNotation") = true then return String(ct); fi;
2544
2545    cls := TransposedClasses(ct);
2546    if   ForAll(cls,IsResidueClass)
2547    then name := "ClassTransposition";
2548    else name := "GeneralizedClassTransposition"; fi;
2549    if    ValueOption("CycleNotation") <> false
2550      and ValueOption("AbridgedNotation") <> false
2551    then return Filtered(Concatenation(List(["( ",cls[1],", ",
2552                                                  cls[2]," )"],
2553                                             ViewString)),ch->ch<>'\"');
2554    else return Filtered(Concatenation(List([name,"( ",cls[1],", ",
2555                                                       cls[2]," )"],
2556                                             ViewString)),ch->ch<>'\"');
2557    fi;
2558
2559  end );
2560
2561InstallMethod( PrintObj, "for class transpositions (RCWA)", true,
2562               [ IsRcwaMapping and IsGeneralizedClassTransposition ],
2563               SUM_FLAGS+10, function ( ct ) Print( String( ct ) ); end );
2564
2565InstallMethod( ViewObj, "for class transpositions (RCWA)", true,
2566               [ IsRcwaMapping and IsGeneralizedClassTransposition ], 20,
2567               function ( ct ) Print( ViewString( ct ) ); end );
2568
2569#############################################################################
2570##
2571#M  SplittedClassTransposition( <ct>, <k> ) . . . . . . .  2-argument version
2572##
2573InstallMethod( SplittedClassTransposition,
2574               "default method (RCWA)", ReturnTrue,
2575               [ IsRcwaMapping and IsClassTransposition, IsObject ], 0,
2576               function ( ct, k )
2577                 return SplittedClassTransposition(ct,k,false);
2578               end );
2579
2580#############################################################################
2581##
2582#M  SplittedClassTransposition( <ct>, <k>, <cross> ) . . . 3-argument version
2583##
2584InstallMethod( SplittedClassTransposition,
2585               "for a class transposition (RCWA)", ReturnTrue,
2586               [ IsRcwaMapping and IsClassTransposition,
2587                 IsObject, IsBool ], 0,
2588
2589  function ( ct, k, cross )
2590
2591    local  cls, pairs;
2592
2593    if IsZero(k) or not k in Source(ct) then TryNextMethod(); fi;
2594    cls := List(TransposedClasses(ct),cl->SplittedClass(cl,k));
2595    if cross then pairs := Cartesian(cls);
2596             else pairs := TransposedMat(cls); fi;
2597    return List(pairs,ClassTransposition);
2598  end );
2599
2600#############################################################################
2601##
2602#F  ClassPairs( [ <P> ], <m> )
2603#F  ClassPairs( [ <R> ], <m> )
2604##
2605##  In the one-argument version, this function returns a list of all
2606##  unordered pairs of disjoint residue classes of Z with modulus <= <m>.
2607##  If the option "divisors" is set, the list contains only those pairs of
2608##  residue classes where the moduli divide <m>.
2609##
2610##  In the two-argument version, if <P> is a set of primes, the function
2611##  returns a list of all unordered pairs of disjoint residue classes of Z
2612##  whose moduli are less than or equal to <m> and factor completely
2613##  over <P>.
2614##
2615##  If <R> is a ring, the function does the following:
2616##
2617##    - If <R> is either the ring of integers or a semilocalization thereof,
2618##      it returns a list of all unordered pairs of disjoint residue classes
2619##      of <R> with modulus <= <m>.
2620##
2621##    - If <R> is a univariate polynomial ring over a finite field, it
2622##      returns a list of all unordered pairs of disjoint residue classes
2623##      of <R> whose moduli have degree less than <m>.
2624##
2625##    - If <R> is Integers^2, it returns a list of all unordered pairs of
2626##      disjoint residue classes whose moduli have determinant at most <m>.
2627##
2628##  The purpose of this function is to generate a list of all
2629##  class transpositions whose moduli do not exceed a given bound.
2630##  For reasons of saving time or memory, it may return pairs of
2631##  residue classes [r1(m1),r2(m2)] in the form of 4-tuples [r1,m1,r2,m2]
2632##  of ring elements. Regardless of whether it does so or not, one can
2633##  always obtain the corresponding list of class transpositions by
2634##  List( ClassPairs( <R>, <m> ), ClassTransposition );
2635##
2636InstallGlobalFunction( ClassPairs,
2637
2638  function ( arg )
2639
2640    local  R, P, m, tuples, moduli, Degree, classes,
2641           m1, r1, m2, r2, d, i, j, usage;
2642
2643    usage := "usage: ClassPairs( [ <R> | <P> ], <m> )\n";
2644
2645    if Length(arg) = 1 then
2646      m := arg[1];
2647      if IsInt(m) then R := Integers; else R := DefaultRing(m); fi;
2648    elif Length(arg) = 2 then
2649      if IsRing(arg[1]) or IsZxZ(arg[1]) then
2650        R := arg[1];
2651      elif IsList(arg[1]) and ForAll(arg[1],p->IsPosInt(p) and IsPrime(p))
2652      then R := Integers; P := arg[1];
2653      else Error(usage); return fail; fi;
2654      m := arg[2];
2655    else Error(usage); return fail; fi;
2656
2657    if IsIntegers(R) or IsZ_pi(R) then
2658      tuples := [];
2659      if ValueOption("divisors") = true then
2660        moduli := Difference(DivisorsInt(m),[1]);
2661      elif IsBound(P) then
2662        moduli := Difference(AllSmoothIntegers(P,m),[1]);
2663      else
2664        moduli := [2..m];
2665      fi;
2666      for i in [1..Length(moduli)] do
2667        m2 := moduli[i];
2668        for j in [1..i] do
2669          m1 := moduli[j];
2670          d := Gcd(m1,m2);
2671          for r1 in [0..m1-1] do
2672            for r2 in [0..m2-1] do
2673              if m1 = m2 and r1 > r2 then continue; fi;
2674              if (r1 - r2) mod d <> 0 then
2675                Add(tuples,[r1,m1,r2,m2]);
2676              fi;
2677            od;
2678          od;
2679        od;
2680      od;
2681      tuples := Set(tuples);
2682      if IsZ_pi(R) then
2683        tuples := Filtered(tuples,t->IsSubset(NoninvertiblePrimes(R),
2684                                              Factors(t[2]*t[4])));
2685      fi;
2686    elif     IsUnivariatePolynomialRing(R) and IsField(LeftActingDomain(R))
2687         and IsFinite(LeftActingDomain(R))
2688    then
2689      Degree := DegreeOfUnivariateLaurentPolynomial;
2690      tuples := [];
2691      moduli := Filtered(AllResidues(R,m),r->IsPosInt(Degree(r)));
2692      for m1 in moduli do
2693        for m2 in moduli do
2694          if m1 > m2 then continue; fi;
2695          for r1 in AllResidues(R,m1) do
2696            for r2 in AllResidues(R,m2) do
2697              if (m1 <> m2 or r1 < r2) and not IsZero((r1-r2) mod Gcd(m1,m2))
2698              then Add(tuples,[r1,m1,r2,m2]); fi;
2699            od;
2700          od;
2701        od;
2702      od;
2703    elif IsZxZ(R) then
2704      moduli  := All2x2IntegerMatricesInHNFWithDeterminantUpTo(m);
2705      classes := Concatenation(List(moduli,m->AllResidueClassesModulo(R,m)));
2706      tuples  := Filtered(Combinations(classes,2),
2707                          t->Intersection(t[1],t[2])=[]);
2708    else
2709      Error("ClassPairs: Sorry, the ring ",R,"\n",String(" ",19),
2710            "is currently not supported by this function.\n");
2711    fi;
2712    return tuples;
2713  end );
2714
2715InstallValue( CLASS_PAIRS, [ 6, ClassPairs(6) ] );
2716InstallValue( CLASS_PAIRS_LARGE, CLASS_PAIRS );
2717
2718#############################################################################
2719##
2720#F  NumberClassPairs( <m> ) . . compute Length( ClassPairs( m ) ) efficiently
2721#F  NrClassPairs( <m> )
2722##
2723InstallGlobalFunction( NumberClassPairs,
2724
2725  function ( m )
2726
2727    local  nr, coprimes, m1, m2, modlist, mods, d;
2728
2729    if not IsPosInt( m ) then Error("usage: NrClassPairs( <m> )\n"); fi;
2730
2731    coprimes := Union([[1,1]],Filtered(Combinations([1..Int(m/2)],2),
2732                                       t->Gcd(t) = 1));
2733    nr := 0;
2734    for d in [2..m] do
2735      modlist := Filtered(d*coprimes,mods->Maximum(mods)<=m);
2736      for mods in modlist do
2737        m1 := mods[1]; m2 := mods[2];
2738        if m1 = m2 then nr := nr + m1 * (m1 - 1)/2;
2739                   else nr := nr + (d - 1)/d * m1 * m2;
2740        fi;
2741      od;
2742    od;
2743
2744    return nr;
2745  end );
2746
2747#############################################################################
2748##
2749#M  PrimeSwitch( <p> ) . . . . . . . . . . . . . . .  for an odd prime number
2750##
2751InstallMethod( PrimeSwitch,
2752               "for an odd prime number (RCWA)",  true, [ IsPosInt ], 0,
2753
2754  function ( p )
2755    if   p = 2 or not IsPrimeInt(p)
2756    then Error("PrimeSwitch( <p> ): <p> must be an odd prime\n"); fi;
2757    return PrimeSwitch(p,1);
2758  end );
2759
2760#############################################################################
2761##
2762#M  PrimeSwitch( <p>, <k> ) .  for an odd prime number and a positive integer
2763##
2764InstallMethod( PrimeSwitch,
2765               "for an odd prime number and a positive integer (RCWA)",
2766               ReturnTrue, [ IsPosInt, IsPosInt ], 0,
2767
2768  function ( p, k )
2769
2770    local  result, facts, kstr, latex;
2771
2772    if   p = 2 or not IsPrimeInt(p)
2773    then Error("PrimeSwitch( <p>, <k> ): <p> must be an odd prime\n"); fi;
2774    facts := List([[k,2*k*p,0,8*k],[2*k*p-k,2*k*p,4*k,8*k],
2775                   [0,4*k,k,2*k*p],[2*k,4*k,2*k*p-k,2*k*p],
2776                   [2*k,2*k*p,k,4*k*p],[4*k,2*k*p,2*k*p+k,4*k*p]],
2777                  ClassTransposition);
2778    result := Product(facts); SetIsPrimeSwitch(result,true);
2779    SetIsTame(result,false); SetOrder(result,infinity);
2780    SetBaseRoot(result,result); SetPowerOverBaseRoot(result,1);
2781    SetFactorizationIntoCSCRCT(result,facts);
2782    SetFactorizationIntoCSCRCT(result^-1,Reversed(facts));
2783    latex := ValueOption("LaTeXString");
2784    if latex = fail then
2785      if k=1 then kstr := ""; else kstr := Concatenation(",",String(k)); fi;
2786      SetLaTeXString(result,Concatenation("\\sigma_{",String(p),kstr,"}"));
2787    elif not IsEmpty(latex) then SetLaTeXString(result,latex); fi;
2788    return result;
2789  end );
2790
2791#############################################################################
2792##
2793#M  PrimeSwitch( <p>, <r>, <m> ) .  for odd prime number, residue and modulus
2794##
2795InstallMethod( PrimeSwitch,
2796               "for an odd prime number, a residue and a modulus (RCWA)",
2797               ReturnTrue, [ IsPosInt, IsInt, IsPosInt ], 0,
2798
2799  function ( p, r, m )
2800
2801    local  result, facts, mp, r1, r2, rc, latex;
2802
2803    if p = 2 or not IsPrimeInt(p) or m mod 4 <> 0 then
2804      Error("PrimeSwitch( <p>, <r>, <m> ): <p> must be an odd prime ",
2805            "and <m> must be\na multiple of 4\n");
2806      return fail;
2807    fi;
2808
2809    r := r mod m; r1 := 1 - r mod 2;
2810    if r < m/2 then r2 := r+m/2; else r2 := r-m/2; fi;
2811    facts := List([[r1,m/2,r2,m],[r2,m,r1,p*m/2],[r mod (m/2),m/2,r1,p*m/2]],
2812                  ClassTransposition);
2813    result := Product(facts);
2814
2815    SetIsPrimeSwitch(result,true);
2816    SetIsTame(result,false); SetOrder(result,infinity);
2817    SetBaseRoot(result,result); SetPowerOverBaseRoot(result,1);
2818    SetFactorizationIntoCSCRCT(result,facts);
2819    SetFactorizationIntoCSCRCT(result^-1,Reversed(facts));
2820    latex := ValueOption("LaTeXString");
2821    if latex = fail then
2822      SetLaTeXString(result,Concatenation("\\sigma_{",String(p),",",
2823                                          String(r),"(",String(m),")}"));
2824    elif not IsEmpty(latex) then SetLaTeXString(result,latex); fi;
2825
2826    return result;
2827  end );
2828
2829#############################################################################
2830##
2831#M  PrimeSwitch( <p>, <cl> ) . .  for an odd prime number and a residue class
2832##
2833InstallMethod( PrimeSwitch,
2834               "for an odd prime number and a residue class (RCWA)",
2835               ReturnTrue, [ IsPosInt, IsUnionOfResidueClassesOfZ ], 0,
2836
2837  function ( p, cl )
2838    if   p = 2 or not IsPrimeInt(p)
2839    then Error("PrimeSwitch( <p>, <cl> ): <p> must be an odd prime\n"); fi;
2840    if not IsResidueClass(cl) then TryNextMethod(); fi;
2841    return PrimeSwitch(p,Residue(cl),Mod(cl));
2842  end );
2843
2844#############################################################################
2845##
2846#M  IsPrimeSwitch( <g> ) . . . . . . . . . . . . . . . for rcwa mappings of Z
2847##
2848InstallMethod( IsPrimeSwitch,
2849               "for rcwa mappings of Z (RCWA)",
2850               true, [ IsRcwaMappingOfZ ], 0,
2851
2852  function ( g )
2853
2854    local  p, cl, k;
2855
2856    if not IsBijective(g) or not IsSignPreserving(g) then return false; fi;
2857    p := Maximum(Factors(Mult(g)));
2858    if Mult(g) <> p or Div(g) <> 2 then return false; fi;
2859    cl := Multpk(g,p,1);
2860    if not IsResidueClass(cl) then return false; fi;
2861    k := Mod(cl)/4;
2862    if IsInt(k) and g = PrimeSwitch(p,k) then return true; fi;
2863    return g = PrimeSwitch(p,cl);
2864  end );
2865
2866#############################################################################
2867##
2868#M  String( <sigma_p> ) . . . . . . . . . . . . . . . . .  for prime switches
2869#M  ViewString( <sigma_p> ) . . . . . . . . . . . . . . .  for prime switches
2870#M  PrintObj( <sigma_p> ) . . . . . . . . . . . . . . . .  for prime switches
2871#M  ViewObj( <sigma_p> )  . . . . . . . . . . . . . . . .  for prime switches
2872##
2873InstallMethod( String, "for prime switches (RCWA)", true,
2874               [ IsRcwaMapping and IsPrimeSwitch ], SUM_FLAGS,
2875
2876  function ( sigma_p )
2877
2878    local  p, k, kstr, cl;
2879
2880    p := Maximum(Factors(Multiplier(sigma_p)));
2881    k := 1/(4*Density(Multpk(sigma_p,p,1)));
2882    if IsInt(k) and sigma_p = PrimeSwitch(p,k) then
2883      if k = 1 then kstr := "";
2884               else kstr := Concatenation(",",String(k)); fi;
2885      return Concatenation("PrimeSwitch(",String(p),kstr,")");
2886    else
2887      cl := Multpk(sigma_p,p,1);
2888      if sigma_p = PrimeSwitch(p,cl) then
2889        return Concatenation("PrimeSwitch(",String(p),",",
2890                             String(Residue(cl)),",",String(Mod(cl)),")");
2891      else return fail; fi;
2892    fi;
2893  end );
2894
2895InstallMethod( ViewString, "for prime switches (RCWA)", true,
2896               [ IsRcwaMapping and IsPrimeSwitch ], SUM_FLAGS,
2897
2898  function ( ps )
2899
2900    local  name;
2901
2902    if   ValueOption("PrintNotation") = true
2903    then return String(ps); fi;
2904    if   ValueOption("AbridgedNotation") = true
2905    then name := "ps"; else name := "PrimeSwitch"; fi;
2906    return ReplacedString(String(ps),"PrimeSwitch",name);
2907  end );
2908
2909InstallMethod( PrintObj, "for prime switches (RCWA)", true,
2910               [ IsRcwaMapping and IsPrimeSwitch ], SUM_FLAGS+10,
2911               function ( sigma_p ) Print( String( sigma_p ) ); end );
2912
2913InstallMethod( ViewObj, "for prime switches (RCWA)", true,
2914               [ IsRcwaMapping and IsPrimeSwitch ], 20,
2915               function ( sigma_p ) Print( ViewString( sigma_p ) ); end );
2916
2917#############################################################################
2918##
2919#F  mKnot( <m> ) . . . . . .  an rcwa permutation of Timothy P. Keller's type
2920##
2921InstallGlobalFunction ( mKnot,
2922
2923  function ( m )
2924
2925    local  result;
2926
2927    if   not IsPosInt(m) or m mod 2 <> 1 or m = 1
2928    then Error("usage: see ?mKnot( m )\n"); fi;
2929    result := RcwaMapping(List([0..m-1],r->[m+(-1)^r,(-1)^(r+1)*r,m]));
2930    SetIsBijective(result,true);
2931    SetIsTame(result,false); SetOrder(result,infinity);
2932    SetName(result,Concatenation("mKnot(",String(m),")"));
2933    SetLaTeXString(result,Concatenation("\\kappa_{",String(m),"}"));
2934    return result;
2935  end );
2936
2937#############################################################################
2938##
2939#F  ClassUnionShift( <S> ) . . . . . shift of rc.-union <S> by Modulus( <S> )
2940##
2941InstallGlobalFunction( ClassUnionShift,
2942
2943  function ( S )
2944
2945    local  R, m, res, resS, r, c, f;
2946
2947    R := UnderlyingRing(FamilyObj(S));
2948    m := Modulus(S); resS := Residues(S); res := AllResidues(R,m);
2949    c := List(res,r->[1,0,1]*One(R));
2950    for r in resS do c[PositionSorted(res,r)] := [1,m,1]*One(R); od;
2951    return RcwaMapping(R,m,c);
2952  end );
2953
2954#############################################################################
2955##
2956#S  Methods for `String', `Print', `View', `Display' and `LaTeX'. ///////////
2957##
2958#############################################################################
2959
2960#############################################################################
2961##
2962#M  String( <f> ) . . . . . . . . . . . . . . . . . .  for rcwa mappings of Z
2963##
2964InstallMethod( String,
2965               "for rcwa mappings of Z (RCWA)", true,
2966               [ IsRcwaMappingOfZ ], 0,
2967
2968  function ( arg )
2969
2970    local f, lng, s;
2971
2972    f := arg[1]; if Length(arg) > 1 then lng := arg[2]; fi;
2973    s := Concatenation( "RcwaMapping( ", String( Coefficients(f) ), " )" );
2974    if IsBound(lng) then s := String(s,lng); fi;
2975    return s;
2976  end );
2977
2978#############################################################################
2979##
2980#M  String( <f> ) . . . . . . . . . . . . . . . . .  for rcwa mappings of Z^2
2981##
2982InstallMethod( String,
2983               "for rcwa mappings of Z^2 (RCWA)", true,
2984               [ IsRcwaMappingOfZxZInStandardRep ], 0,
2985
2986  function ( arg )
2987
2988    local f, lng, s;
2989
2990    f := arg[1]; if Length(arg) > 1 then lng := arg[2]; fi;
2991    s := Concatenation( "RcwaMapping( Integers^2, ", String( Modulus(f) ),
2992                        ", ", String( Coefficients(f) ), " )" );
2993    if IsBound(lng) then s := String(s,lng); fi;
2994    return s;
2995  end );
2996
2997#############################################################################
2998##
2999#M  String( <f> ) . . . . . . . . . . . . . . . . for rcwa mappings of Z_(pi)
3000##
3001InstallMethod( String,
3002               "for rcwa mappings of Z_(pi) (RCWA)",
3003               true, [ IsRcwaMappingOfZ_piInStandardRep ], 0,
3004
3005  function ( arg )
3006
3007    local  f, lng, s;
3008
3009    f := arg[1]; if Length(arg) > 1 then lng := arg[2]; fi;
3010    s := Concatenation( "RcwaMapping( ",
3011                        String(NoninvertiblePrimes(Source(f))), ", ",
3012                        String(Coefficients(f)), " )" );
3013    if IsBound(lng) then s := String(s,lng); fi;
3014    return s;
3015  end );
3016
3017#############################################################################
3018##
3019#M  String( <f> ) . . . . . . . . . . . . . . . for rcwa mappings of GF(q)[x]
3020##
3021InstallMethod( String,
3022               "for rcwa mappings of GF(q)[x] (RCWA)",
3023               true, [ IsRcwaMappingOfGFqxInStandardRep ], 0,
3024
3025  function ( arg )
3026
3027    local  f, lng, s;
3028
3029    f := arg[1]; if Length(arg) > 1 then lng := arg[2]; fi;
3030    s := Concatenation( "RcwaMapping( ",
3031                        String(Size(UnderlyingField(f))), ", ",
3032                        String(Modulus(f)), ", ",
3033                        String(Coefficients(f)), " )" );
3034    if IsBound(lng) then s := String(s,lng); fi;
3035    return s;
3036  end );
3037
3038#############################################################################
3039##
3040#M  String( <f> ) . . . . . . . . . . . . .  for rcwa mappings with base root
3041#M  ViewString( <f> ) . . . . . . . . . . .  for rcwa mappings with base root
3042##
3043InstallMethod( String, "for rcwa mappings with base root (RCWA)", true,
3044               [ IsRcwaMapping and HasBaseRoot ], 20,
3045               f -> Concatenation(String(BaseRoot(f)),"^",
3046                                  String(PowerOverBaseRoot(f))) );
3047
3048InstallMethod( ViewString, "for rcwa mappings with base root (RCWA)", true,
3049               [ IsRcwaMapping and HasBaseRoot ], 0,
3050               f -> Concatenation(ViewString(BaseRoot(f)),"^",
3051                                  String(PowerOverBaseRoot(f))) );
3052
3053#############################################################################
3054##
3055#M  PrintObj( <f> ) . . . . . . . . . . . . . . . . .  for rcwa mappings of Z
3056##
3057InstallMethod( PrintObj,
3058               "for rcwa mappings of Z (RCWA)", true,
3059               [ IsRcwaMappingOfZ ], SUM_FLAGS,
3060
3061  function ( f )
3062    Print( "RcwaMapping( ", Coefficients(f), " )" );
3063  end );
3064
3065#############################################################################
3066##
3067#M  PrintObj( <f> ) . . . . . . . . . . . . . . . .  for rcwa mappings of Z^2
3068##
3069InstallMethod( PrintObj,
3070               "for rcwa mappings of Z^2 (RCWA)", true,
3071               [ IsRcwaMappingOfZxZInStandardRep ], SUM_FLAGS,
3072
3073  function ( f )
3074    Print( "RcwaMapping( Integers^2, ",
3075                         Modulus(f), ", ", Coefficients(f), " )" );
3076  end );
3077
3078#############################################################################
3079##
3080#M  PrintObj( <f> ) . . . . . . . . . . . . . . . for rcwa mappings of Z_(pi)
3081##
3082InstallMethod( PrintObj,
3083               "for rcwa mappings of Z_(pi) (RCWA)", true,
3084               [ IsRcwaMappingOfZ_piInStandardRep ],  SUM_FLAGS,
3085
3086  function ( f )
3087    Print( "RcwaMapping( ",
3088           NoninvertiblePrimes(Source(f)), ", ", Coefficients(f), " )" );
3089  end );
3090
3091#############################################################################
3092##
3093#M  PrintObj( <f> ) . . . . . . . . . . . . . . for rcwa mappings of GF(q)[x]
3094##
3095InstallMethod( PrintObj,
3096               "for rcwa mappings of GF(q)[x] (RCWA)", true,
3097               [ IsRcwaMappingOfGFqxInStandardRep ], SUM_FLAGS,
3098
3099  function ( f )
3100    Print( "RcwaMapping( ", Size(UnderlyingField(f)),
3101           ", ", Modulus(f), ", ", Coefficients(f), " )" );
3102  end );
3103
3104#############################################################################
3105##
3106#M  ViewObj( <f> ) . . . . . . . . . . . . . . . . . . . .  for rcwa mappings
3107##
3108InstallMethod( ViewObj,
3109               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
3110
3111  function ( f )
3112
3113    local  cycles, cyclenotation, i, j;
3114
3115    if IsZero(f) or IsOne(f) then View(f); return; fi;
3116    if HasBaseRoot(f) then
3117      View(BaseRoot(f)); Print("^",PowerOverBaseRoot(f)); return;
3118    fi;
3119    if IsOne(Modulus(f)) then Display(f:NoLineFeed); return; fi;
3120    cyclenotation := ValueOption("CycleNotation");
3121    if     IsRcwaMappingOfZ(f) and cyclenotation <> false
3122       and ( HasIsBijective(f) or cyclenotation = true ) and IsBijective(f)
3123       and ( cyclenotation = true or Length(Set(Coefficients(f))) <= 10 )
3124       and ( cyclenotation = true or HasIsTame(f) or IsIntegral(f) )
3125       and IsSignPreserving(f) and IsTame(f)
3126    then
3127      cycles := Filtered(Cycles(f,RespectedPartition(f)),
3128                         cycle->Length(cycle)>1);
3129      for i in [1..Length(cycles)] do
3130        Print("( ");
3131        for j in [1..Length(cycles[i])] do
3132          Print(ViewString(cycles[i][j]));
3133          if j < Length(cycles[i]) then Print(", "); fi;
3134        od;
3135        Print(" )");
3136        if i < Length(cycles) then Print(" "); fi;
3137      od;
3138    else
3139      Print("<");
3140      if   HasIsTame(f) and not (HasOrder(f) and IsInt(Order(f)))
3141      then if IsTame(f) then Print("tame "); else Print("wild "); fi; fi;
3142      if   HasIsBijective(f) and IsBijective(f)
3143      then Print("rcwa permutation");
3144      elif HasIsInjective(f) and IsInjective(f)
3145      then Print("injective rcwa mapping");
3146      elif HasIsSurjective(f) and IsSurjective(f)
3147      then Print("surjective rcwa mapping");
3148      else Print("rcwa mapping");
3149      fi;
3150      Print(" of ",RingToString(Source(f)));
3151      Print(" with modulus ",ModulusAsFormattedString(Modulus(f)));
3152      if IsRcwaMappingSparseRep(f) then
3153        Print(" and ",Length(f!.coeffs)," affine parts");
3154      fi;
3155      if   HasOrder(f) and not (HasIsTame(f) and not IsTame(f))
3156      then Print(", of order ",Order(f)); fi;
3157      Print(">");
3158    fi;
3159  end );
3160
3161#############################################################################
3162##
3163#M  ViewObj( <elm> ) . . . . . . . for elements of group rings of rcwa groups
3164##
3165InstallMethod( ViewObj,
3166               "for elements of group rings of rcwa groups (RCWA)",
3167               ReturnTrue, [ IsElementOfFreeMagmaRing ], 100,
3168
3169  function ( elm )
3170
3171    local  l, grpelms, coeffs, supplng, g, i;
3172
3173    if not IsRcwaMapping(CoefficientsAndMagmaElements(One(elm))[1]) then
3174      TryNextMethod();
3175    fi;
3176    l       := CoefficientsAndMagmaElements(elm);
3177    grpelms := l{[1,3..Length(l)-1]};
3178    coeffs  := l{[2,4..Length(l)]};
3179    supplng := Length(grpelms);
3180    if not ForAll(grpelms,IsRcwaMapping) then TryNextMethod(); fi;
3181    if supplng = 0 then Print("0"); return; fi;
3182    for i in [1..supplng] do
3183      if coeffs[i] < 0 then
3184        if i > 1 then Print(" - "); else Print("-"); fi;
3185      else
3186        if i > 1 then Print(" + "); fi;
3187      fi;
3188      if AbsInt(coeffs[i]) > 1 then Print(AbsInt(coeffs[i]),"*"); fi;
3189      ViewObj(grpelms[i]);
3190      if i < supplng then Print("\n"); fi;
3191    od;
3192  end );
3193
3194#############################################################################
3195##
3196#M  Display( <f> ) . . . . . . . . . . . . . . . . . . . .  for rcwa mappings
3197##
3198##  Displays the rcwa mapping <f> in nice human-readable form.
3199##
3200InstallMethod( Display,
3201               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 10,
3202
3203  function ( f )
3204
3205    local  StringAffineMapping, StringAffineMappingOfZ,
3206           StringAffineMappingOfZxZ, StringAffineMappingOfZ_pi,
3207           StringAffineMappingOfGFqx,
3208
3209           R, F, F_el, F_elints, m, c, src, img, res, idcoeffs, inds,
3210           P, Pcl, D, affs, affstrings, maxafflng, lines, line, maxlinelng,
3211           cycles, cl, str, ustr, ringname, varname, maxsrclng, maximglng,
3212           looppos, prefix, col, i, j;
3213
3214    StringAffineMappingOfZ := function ( t )
3215
3216      local  append, str, a, b, c, n;
3217
3218      append := function ( arg )
3219        str := CallFuncList(Concatenation,
3220                            Concatenation([str],List(arg,String)));
3221      end;
3222
3223      a := t[1]; b := t[2]; c := t[3];
3224      str := ""; n := varname;
3225
3226      if c > 1 and Number([a,b],n->n<>0) > 1 then append("("); fi;
3227      if a <> 0 then
3228        if a = -1 then append("-"); elif a <> 1 then append(a); fi;
3229        append(n);
3230        if b > 0 then
3231          if m = 1 then append(" + "); else append("+"); fi;
3232        fi;
3233      fi;
3234      if a = 0 or b <> 0 then
3235        if a = 0 or b > 0 then append(b); else
3236          if m = 1 then append(" - ",-b);
3237                   else append(b); fi;
3238        fi;
3239      fi;
3240      if c > 1 and Number([a,b],n->n<>0) > 1 then append(")"); fi;
3241      if c > 1 then append("/",c); fi;
3242
3243      return str;
3244    end;
3245
3246    StringAffineMappingOfZxZ := function ( t )
3247
3248      local  Stringaff, append, str,
3249             a, b, c, d, e, f, g, d1, d2, g1, g2, m, n;
3250
3251      Stringaff := function ( a, b, c, d )
3252        if d > 1 and Number([a,b,c],n->n<>0) > 1 then append("("); fi;
3253        if a <> 0 then
3254          if a = -1 then append("-"); elif a <> 1 then append(a); fi;
3255          append(m);
3256          if b > 0 or (b = 0 and c > 0) then append("+"); fi;
3257        fi;
3258        if b <> 0 then
3259          if b = -1 then append("-"); elif b <> 1 then append(b); fi;
3260          append(n);
3261          if c > 0 then append("+"); fi;
3262        fi;
3263        if (a = 0 and b = 0) or c <> 0 then append(c); fi;
3264        if d > 1 and Number([a,b,c],n->n<>0) > 1 then append(")"); fi;
3265        if d > 1 then append("/",d); fi;
3266      end;
3267
3268      append := function ( arg )
3269        str := CallFuncList(Concatenation,
3270                            Concatenation([str],List(arg,String)));
3271      end;
3272
3273      str := "";
3274      m := varname{[2]}; n := varname{[4]};
3275      a := t[1][1][1]; b := t[1][1][2];
3276      c := t[1][2][1]; d := t[1][2][2];
3277      e := t[2][1];    f := t[2][2];
3278      g := t[3];
3279      d1 := Gcd(a,c,e,g); d2 := Gcd(b,d,f,g);
3280      a := a/d1; c := c/d1; e := e/d1; g1 := g/d1;
3281      b := b/d2; d := d/d2; f := f/d2; g2 := g/d2;
3282      append("(");
3283      Stringaff(a,c,e,g1); append(","); Stringaff(b,d,f,g2);
3284      append(")");
3285
3286      return str;
3287    end;
3288
3289    StringAffineMappingOfZ_pi := function ( t )
3290
3291      local  append, str, a, b, c, n;
3292
3293      append := function ( arg )
3294        str := CallFuncList(Concatenation,
3295                            Concatenation([str],List(arg,String)));
3296      end;
3297
3298      a := t[1]; b := t[2]; c := t[3];
3299      str := ""; n := varname;
3300
3301      if   c = 1
3302      then if   a = 0
3303           then append(b);
3304           else if   AbsInt(a) <> 1 then append(a," ");
3305                elif a = -1         then append("-");
3306                fi;
3307                append(n);
3308                if   b > 0 then append(" + ", b);
3309                elif b < 0 then append(" - ",-b);
3310                fi;
3311           fi;
3312      elif b = 0 then if   AbsInt(a) <> 1 then append(a," ");
3313                      elif a = -1         then append("-");
3314                      fi;
3315                      append(n," / ",c);
3316      else append("(");
3317           if   AbsInt(a) <> 1 then append(a," ");
3318           elif a = -1         then append("-");
3319           fi;
3320           append(n);
3321           if   b > 0 then append(" + ", b);
3322           elif b < 0 then append(" - ",-b);
3323           fi;
3324           append(") / ",c);
3325      fi;
3326
3327      return str;
3328    end;
3329
3330    StringAffineMappingOfGFqx := function ( t )
3331
3332      local  append, factorstr, str, a, b, c, P, one, zero, x;
3333
3334      append := function ( arg )
3335        str := CallFuncList(Concatenation,
3336                            Concatenation([str],List(arg,String)));
3337      end;
3338
3339      factorstr := function ( p )
3340        if   Length(CoefficientsOfLaurentPolynomial(p)[1]) <= 1
3341        then return String(p);
3342        else return Concatenation("(",String(p),")"); fi;
3343      end;
3344
3345      a := t[1]; b := t[2]; c := t[3];
3346      str := ""; P := varname;
3347
3348      one := One(a); zero := Zero(a);
3349      x := IndeterminateOfLaurentPolynomial(a);
3350
3351      if   c = one
3352      then if   a = zero
3353           then append(b);
3354           else if   not a in [-one,one] then append(factorstr(a),"*",P);
3355                elif a = one then append(P); else append("-",P); fi;
3356                if b <> zero then append(" + ",b); fi;
3357           fi;
3358      elif b = zero then if   not a in [-one,one]
3359                         then append(factorstr(a),"*",P);
3360                         elif a = one then append(P);
3361                         else append("-",P); fi;
3362                         append("/",factorstr(c));
3363      else append("(");
3364           if   not a in [-one,one]
3365           then append(factorstr(a),"*",P," + ",b,")/",factorstr(c));
3366           elif a <> one and a = -one
3367           then append("-",P," + ",b,")/",factorstr(c));
3368           else append(P," + ",b,")/",factorstr(c));
3369           fi;
3370      fi;
3371
3372      return str;
3373    end;
3374
3375    R := Source(f);
3376
3377    if   ValueOption("xdvi") = true and IsIntegers(R)
3378    then LaTeXAndXDVI(f); return; fi;
3379
3380    # If option "AsTable" is set, use old-style format:
3381    if   ValueOption("AsTable") = true or ValueOption("table") = true
3382    then TryNextMethod(); fi;
3383
3384    if   IsRcwaMappingOfZ(f)
3385    then StringAffineMapping := StringAffineMappingOfZ;
3386    elif IsRcwaMappingOfZxZ(f)
3387    then StringAffineMapping := StringAffineMappingOfZxZ;
3388    elif IsRcwaMappingOfZ_pi(f)
3389    then StringAffineMapping := StringAffineMappingOfZ_pi;
3390    elif IsRcwaMappingOfGFqx(f)
3391    then StringAffineMapping := StringAffineMappingOfGFqx; fi;
3392
3393    m := Modulus(f); c := Coefficients(f); res := AllResidues(R,m);
3394
3395    prefix := false; ringname := RingToString(Source(f));
3396
3397    if   IsRcwaMappingOfGFqx(f) then varname := "P";
3398    elif IsRcwaMappingOfZxZ(f)  then
3399      varname := First(List(["varnames","VarNames"],ValueOption),
3400                       names->names<>fail);
3401      if varname = fail then varname := "mn"; fi;
3402      if Length(varname) = 2 then
3403        varname := Concatenation("(",varname{[1]},",",varname{[2]},")");
3404      fi;
3405    else varname := "n"; fi;
3406
3407    if   IsOne(f)  then Print("Identity rcwa mapping of ",ringname);
3408    elif IsZero(f) then Print("Zero rcwa mapping of ",ringname);
3409    elif IsOne(m) and IsRcwaMappingStandardRep(f) and IsZero(c[1][1])
3410    then Print("Constant rcwa mapping of ",ringname," with value ",c[1][2]);
3411    elif IsOne(m) and IsRcwaMappingSparseRep(f) and IsZero(c[1][3])
3412    then Print("Constant rcwa mapping of ",ringname," with value ",c[1][4]);
3413    else
3414      if not IsOne(m) then Print("\n"); fi;
3415
3416      if HasIsTame(f) and not (HasOrder(f) and IsInt(Order(f))) then
3417        if IsTame(f) then Print("Tame "); else Print("Wild "); fi;
3418        prefix := true;
3419      fi;
3420      if   HasIsBijective(f) and IsBijective(f)
3421      then if prefix then Print("rcwa permutation");
3422                     else Print("Rcwa permutation"); fi;
3423           prefix := true;
3424      elif HasIsInjective(f) and IsInjective(f)
3425      then if prefix then Print("injective rcwa mapping");
3426                     else Print("Injective rcwa mapping"); fi;
3427           prefix := true;
3428      elif HasIsSurjective(f) and IsSurjective(f)
3429      then if prefix then Print("surjective rcwa mapping");
3430                     else Print("Surjective rcwa mapping"); fi;
3431           prefix := true;
3432      fi;
3433      if not prefix then Print("Rcwa mapping"); fi;
3434      Print(" of ",ringname);
3435
3436      if IsOne(m) then
3437
3438        if   IsRcwaMappingStandardRep(f) then
3439          Print(": ",varname," -> ",StringAffineMapping(c[1]));
3440        elif IsRcwaMappingSparseRep(f) then
3441          Print(": ",varname," -> ",StringAffineMapping(c[1]{[3..5]}));
3442        fi;
3443
3444      else
3445
3446        Print(" with modulus ",ModulusAsFormattedString(m));
3447        if IsRcwaMappingSparseRep(f) then
3448          Print(" and ",Length(c)," affine parts");
3449        fi;
3450        if   HasOrder(f) and not (HasIsTame(f) and not IsTame(f))
3451        then Print(", of order ",Order(f)); fi;
3452        Print("\n\n");
3453
3454        if   IsRcwaMappingOfZ(f) and HasIsBijective(f) and IsBijective(f)
3455          and (HasIsTame(f) and IsTame(f) or IsIntegral(f))
3456          and IsSignPreserving(f) and ValueOption("CycleNotation") <> false
3457        then
3458          cycles := Filtered(Cycles(f,RespectedPartition(f)),
3459                             cycle->Length(cycle)>1);
3460          maxlinelng := SizeScreen()[1]; col := 1;
3461          for i in [1..Length(cycles)] do
3462            Print("( "); col := col + 2;
3463            for j in [1..Length(cycles[i])] do
3464              str := ViewString(cycles[i][j]);
3465              Print(str); col := col + Length(str);
3466              if j < Length(cycles[i]) then
3467                Print(", "); col := col + 2;
3468                if col + Length(str) + 8 >= maxlinelng then
3469                  Print("\n  "); col := 3;
3470                fi;
3471              fi;
3472            od;
3473            Print(" )"); col := col + 2;
3474            if i < Length(cycles) then
3475              if   col + 20 >= maxlinelng
3476              then Print("\n"); col := 1;
3477              else Print(" "); col := col + 1; fi;
3478            else Print("\n\n");
3479            fi;
3480          od;
3481          return; # done.
3482        fi;
3483
3484        if IsRcwaMappingOfZ(f) and ValueOption("AsClassMapping") = true then
3485          if   not IsRcwaMappingInSparseRep(f)
3486          then c := Coefficients(SparseRep(f)); fi;
3487          src := [];
3488          for i in [1..Length(c)] do
3489            Add(src,ResidueClass(c[i][1],c[i][2]));
3490          od;
3491          img := List(src,cl->cl^f);
3492          looppos := Filtered([1..Length(c)],i->img[i]<>src[i]
3493                              and Intersection(src[i],img[i])<>[]);
3494          src := List(src,ViewString);
3495          img := List(img,ViewString);
3496          maxsrclng := Maximum(List(src,Length));
3497          maximglng := Maximum(List(img,Length));
3498          for i in [1..Length(src)] do
3499            Print("  ",String(src[i],maxsrclng)," -> ",img[i]);
3500            if i in looppos then
3501              Print(String("",maximglng-Length(img[i]))," loop");
3502            fi;
3503            if c[i]{[3..5]} = [1,0,1] then
3504              Print(String("",maximglng-Length(img[i]))," id");
3505            fi;
3506            Print("\n");
3507          od;
3508          if ValueOption("NoLineFeed") <> true then Print("\n"); fi;
3509          return; # done.
3510        fi;
3511
3512        P := ShallowCopy(LargestSourcesOfAffineMappings(f));
3513        D := List(P,src->[1/Density(src),src]);
3514        #i := First([1..Length(P)],j->IsOne(RestrictedMapping(f,P[j])));
3515        if   IsRing(R) then idcoeffs := [1,0,1] * One(R);
3516        elif IsZxZ(R)  then idcoeffs := [[[1,0],[0,1]],[0,0],1]; fi;
3517        if IsRcwaMappingStandardRep(f) then
3518          i := First([1..Length(P)],
3519                     j -> c[First([1..Length(res)],k->res[k] in P[j])]
3520                        = idcoeffs);
3521        elif IsRcwaMappingOfZInSparseRep(f) then
3522          i := First([1..Length(P)],
3523                     j -> c[PositionProperty(c,d->d[1] in P[j])]{[3..5]}
3524                        = idcoeffs);
3525        fi;
3526        if i <> fail then D[i][1] := infinity; fi; # constant mappings -> end
3527
3528        SortParallel(D,P);
3529
3530        if   IsRcwaMappingStandardRep(f) then
3531          affs := List(P,preimg->c[First([1..Length(res)],
3532                                         i->res[i] in preimg)]);
3533        elif IsRcwaMappingSparseRep(f) then
3534          affs := List(P,preimg->First(c,d->d[1] in preimg){[3..5]});
3535        fi;
3536
3537        Pcl  := List(P,AsUnionOfFewClasses);
3538
3539        affstrings := List(affs,StringAffineMapping);
3540
3541        if IsRcwaMappingOfGFqx(f) and IsPrimeField(LeftActingDomain(R)) then
3542          F        := LeftActingDomain(R);
3543          F_el     := List(AsList(F),String);
3544          F_elints := List(List(AsList(F),Int),String);
3545          for i in [1..Length(affstrings)] do
3546            for j in [1..Length(F_el)] do
3547              affstrings[i] := ReplacedString(affstrings[i],
3548                                              F_el[j],F_elints[j]);
3549            od;
3550          od;
3551        fi;
3552
3553        maxafflng  := Maximum(List(affstrings,Length));
3554
3555        maxlinelng := SizeScreen()[1] - Length(varname) - 11;
3556        lines      := [String("/",Length(varname)+8)];
3557
3558        for i in [1..Length(affs)] do
3559          line := String(affstrings[i],-maxafflng);
3560          if i < Length(affs) or Length(Pcl[i]) <= 2
3561            or (IsRcwaMappingOfZOrZ_pi(f) and Length(Pcl[i]) <= 4)
3562          then
3563            Append(line," if ");
3564            Append(line,varname);
3565            Append(line," in ");
3566            str := DisplayString(P[i]);
3567            if Length(line) + Length(str) <= maxlinelng then
3568              Append(line,str);
3569            else
3570              for j in [1..Length(Pcl[i])] do
3571                str := ViewString(Pcl[i][j]);
3572                if   j = Length(Pcl[i])
3573                then ustr := ""; else ustr := " U "; fi;
3574                if Length(line) + Length(str) + Length(ustr) > maxlinelng
3575                  and j > 1
3576                then
3577                  Add(lines,line);
3578                  line := String(" ",maxafflng+Length(" if ")+Length(varname)
3579                                              +Length(" in "));
3580                fi;
3581                Append(line,str);
3582                Append(line,ustr);
3583              od;
3584            fi;
3585          else
3586            Append(line," otherwise");
3587          fi;
3588          Add(lines,line);
3589        od;
3590        if   Length(lines) mod 2 = 1
3591        then Add(lines,String("|",Length(varname)+8)); fi;
3592        Add(lines,String("\\",Length(varname)+8));
3593
3594        for i in [2..Length(lines)-1] do
3595          if i = (Length(lines)+1)/2 then
3596            lines[i] := Concatenation(" ",varname," |-> <  ",lines[i]);
3597          elif not '|' in lines[i] then
3598            lines[i] := Concatenation(String("|",Length(varname)+8)," ",
3599                                      lines[i]);
3600          fi;
3601        od;
3602
3603        if IsRcwaMappingOfGFqx(f) and IsPrimeField(LeftActingDomain(R)) then
3604          for i in [1..Length(lines)] do
3605            for j in [1..Length(F_el)] do
3606              lines[i] := ReplacedString(lines[i],F_el[j],F_elints[j]);
3607            od;
3608          od;
3609        fi;
3610
3611        for i in [1..Length(lines)] do
3612          Print(lines[i],"\n");
3613        od;
3614
3615      fi;
3616
3617    fi;
3618
3619    if ValueOption("NoLineFeed") <> true then Print("\n"); fi;
3620  end );
3621
3622#############################################################################
3623##
3624#M  Display( <f> ) . . . . . . . . . . . . . . . . . . . .  for rcwa mappings
3625##
3626##  Displays the rcwa mapping <f> as a table, in the "old-style" format used
3627##  by RCWA since its first release in 2005.
3628##
3629InstallMethod( Display,
3630               "for rcwa mappings (RCWA)",
3631               true, [ IsRcwaMappingInStandardRep ], 0,
3632
3633  function ( f )
3634
3635    local  IdChars, DisplayAffineMappingOfZ, DisplayAffineMappingOfZxZ,
3636           DisplayAffineMappingOfZ_pi, DisplayAffineMappingOfGFqx,
3637           R, m, c, r, poses, pos, i, scr, l1, l2, l3,
3638           str, ringname, mapname, varname, imageexpr,
3639           mstr, mcharstop, maxreschars, flushlng, prefix;
3640
3641    IdChars := function ( n, ch )
3642      return Concatenation( ListWithIdenticalEntries( n, ch ) );
3643    end;
3644
3645    DisplayAffineMappingOfZ := function ( t )
3646
3647      local  a, b, c;
3648
3649      a := t[1]; b := t[2]; c := t[3];
3650      if   c = 1
3651      then if   a = 0
3652           then Print(b);
3653           else if   AbsInt(a) <> 1 then Print(a);
3654                elif a = -1         then Print("-");
3655                fi;
3656                Print("n");
3657                if   b > 0 then Print(" + ", b);
3658                elif b < 0 then Print(" - ",-b);
3659                fi;
3660           fi;
3661      elif b = 0 then if   AbsInt(a) <> 1 then Print(a);
3662                      elif a = -1         then Print("-");
3663                      fi;
3664                      Print("n/",c);
3665      else Print("(");
3666           if   AbsInt(a) <> 1 then Print(a);
3667           elif a = -1         then Print("-");
3668           fi;
3669           Print("n");
3670           if   b > 0 then Print(" + ", b);
3671           elif b < 0 then Print(" - ",-b);
3672           fi;
3673           Print(")/",c);
3674      fi;
3675    end;
3676
3677    DisplayAffineMappingOfZxZ := function ( t )
3678
3679      local  Print_vxa, PrintAff,
3680             a, b, c, d, e, f, g, d1, d2, g1, g2, m, n;
3681
3682      Print_vxa := function ( )
3683        if   IsOne( a) then Print("v");
3684        elif IsOne(-a) then Print("-v");
3685        else Print("v * ",BlankFreeString(a)); fi;
3686      end;
3687
3688      PrintAff := function ( a, b, c, d )
3689        if d > 1 and Number([a,b,c],n->n<>0) > 1 then Print("("); fi;
3690        if a <> 0 then
3691          if a = -1 then Print("-"); elif a <> 1 then Print(a); fi;
3692          Print(m);
3693          if b > 0 or (b = 0 and c > 0) then Print("+"); fi;
3694        fi;
3695        if b <> 0 then
3696          if b = -1 then Print("-"); elif b <> 1 then Print(b); fi;
3697          Print(n);
3698          if c > 0 then Print("+"); fi;
3699        fi;
3700        if c <> 0 then Print(c); fi;
3701        if d > 1 and Number([a,b,c],n->n<>0) > 1 then Print(")"); fi;
3702        if d > 1 then Print("/",d); fi;
3703      end;
3704
3705      if   varname = "v" then
3706        a := t[1]; b := t[2]; c := t[3];
3707        if   c = 1
3708        then if   IsZero(a)
3709             then Print(BlankFreeString(b));
3710             else Print_vxa();
3711                  if   not IsZero(b)
3712                  then Print(" + ",BlankFreeString(b)); fi;
3713             fi;
3714        elif IsZero(b) then Print_vxa(); Print("/",c);
3715        else Print("(");
3716             if   IsZero(a)
3717             then Print(BlankFreeString(b));
3718             else Print_vxa(); Print(" + ",BlankFreeString(b));
3719             fi;
3720             Print(")/",c);
3721        fi;
3722      elif Length(varname) = 5 then
3723        m := varname{[2]}; n := varname{[4]};
3724        a := t[1][1][1]; b := t[1][1][2];
3725        c := t[1][2][1]; d := t[1][2][2];
3726        e := t[2][1];    f := t[2][2];
3727        g := t[3];
3728        d1 := Gcd(a,c,e,g); d2 := Gcd(b,d,f,g);
3729        a := a/d1; c := c/d1; e := e/d1; g1 := g/d1;
3730        b := b/d2; d := d/d2; f := f/d2; g2 := g/d2;
3731        Print("[");
3732        PrintAff(a,c,e,g1); Print(","); PrintAff(b,d,f,g2);
3733        Print("]");
3734      else Print("<unknown output format>"); fi;
3735    end;
3736
3737    DisplayAffineMappingOfZ_pi := function ( t )
3738
3739      local  a, b, c;
3740
3741      a := t[1]; b := t[2]; c := t[3];
3742      if   c = 1
3743      then if   a = 0
3744           then Print(b);
3745           else if   AbsInt(a) <> 1 then Print(a," ");
3746                elif a = -1         then Print("-");
3747                fi;
3748                Print("n");
3749                if   b > 0 then Print(" + ", b);
3750                elif b < 0 then Print(" - ",-b);
3751                fi;
3752           fi;
3753      elif b = 0 then if   AbsInt(a) <> 1 then Print(a," ");
3754                      elif a = -1         then Print("-");
3755                      fi;
3756                      Print("n / ",c);
3757      else Print("(");
3758           if   AbsInt(a) <> 1 then Print(a," ");
3759           elif a = -1         then Print("-");
3760           fi;
3761           Print("n");
3762           if   b > 0 then Print(" + ", b);
3763           elif b < 0 then Print(" - ",-b);
3764           fi;
3765           Print(") / ",c);
3766      fi;
3767    end;
3768
3769    DisplayAffineMappingOfGFqx := function ( t, maxlng )
3770
3771      local  append, factorstr, str, a, b, c, one, zero, x;
3772
3773      append := function ( arg )
3774        str := CallFuncList(Concatenation,
3775                            Concatenation([str],List(arg,String)));
3776      end;
3777
3778      factorstr := function ( p )
3779        if   Length(CoefficientsOfLaurentPolynomial(p)[1]) <= 1
3780        then return String(p);
3781        else return Concatenation("(",String(p),")"); fi;
3782      end;
3783
3784      a := t[1]; b := t[2]; c := t[3];
3785      one := One(a); zero := Zero(a);
3786      x := IndeterminateOfLaurentPolynomial(a);
3787      str := "";
3788      if   c = one
3789      then if   a = zero
3790           then append(b);
3791           else if   not a in [-one,one] then append(factorstr(a),"*P");
3792                elif a = one then append("P"); else append("-P"); fi;
3793                if b <> zero then append(" + ",b); fi;
3794           fi;
3795      elif b = zero then if   not a in [-one,one]
3796                         then append(factorstr(a),"*P");
3797                         elif a = one then append("P");
3798                         else append("-P"); fi;
3799                         append("/",factorstr(c));
3800      else append("(");
3801           if   not a in [-one,one]
3802           then append(factorstr(a),"*P + ",b,")/",factorstr(c));
3803           elif a <> one and a = -one
3804           then append("-P + ",b,")/",factorstr(c));
3805           else append("P + ",b,")/",factorstr(c));
3806           fi;
3807      fi;
3808      if Length(str) > maxlng then str := "< ... >"; fi;
3809      Print(str);
3810    end;
3811
3812    if   ValueOption("AsTable") <> true and ValueOption("table") <> true
3813    then TryNextMethod(); fi;
3814
3815    R := Source(f);
3816    if   ValueOption("xdvi") = true and IsIntegers(R)
3817    then LaTeXAndXDVI(f); return; fi;
3818
3819    m := Modulus(f); c := Coefficients(f); r := AllResidues(R,m);
3820    if HasName(f) and ValueOption("PrintName") <> fail then
3821      mapname := Name(f);
3822      if   Position(mapname,'^') <> fail
3823      then mapname := Concatenation("(",mapname,")"); fi;
3824    else mapname := "Image of "; fi;
3825    prefix := false; ringname := RingToString(Source(f));
3826    if   IsRcwaMappingOfGFqx(f) then varname := "P";
3827    elif IsRcwaMappingOfZxZ(f)  then
3828      varname := First(List(["varnames","VarNames"],ValueOption),
3829                       names->names<>fail);
3830      if varname = fail then varname := "mn"; fi;
3831      if Length(varname) = 2 then
3832        varname := Concatenation("[",varname{[1]},",",varname{[2]},"]");
3833      fi;
3834    else varname := "n"; fi;
3835    maxreschars := Maximum(List(List(r,BlankFreeString),Length));
3836    if   IsOne(f)
3837    then Print("Identity rcwa mapping of ",ringname);
3838    elif IsZero(f)
3839    then Print("Zero rcwa mapping of ",ringname);
3840    elif IsOne(m) and IsZero(c[1][1])
3841    then Print("Constant rcwa mapping of ",ringname,
3842               " with value ",c[1][2]);
3843    else if not IsOne(m) then Print("\n"); fi;
3844         if HasIsTame(f) and not (HasOrder(f) and IsInt(Order(f))) then
3845           if IsTame(f) then Print("Tame "); else Print("Wild "); fi;
3846           prefix := true;
3847         fi;
3848         if   HasIsBijective(f) and IsBijective(f)
3849         then if prefix then Print("rcwa permutation");
3850                        else Print("Rcwa permutation"); fi;
3851              prefix := true;
3852         elif HasIsInjective(f) and IsInjective(f)
3853         then if prefix then Print("injective rcwa mapping");
3854                        else Print("Injective rcwa mapping"); fi;
3855              prefix := true;
3856         elif HasIsSurjective(f) and IsSurjective(f)
3857         then if prefix then Print("surjective rcwa mapping");
3858                        else Print("Surjective rcwa mapping"); fi;
3859              prefix := true;
3860         fi;
3861         if not prefix then Print("Rcwa mapping"); fi;
3862         Print(" of ",ringname);
3863         if IsOne(m) then
3864           Print(": ",varname," -> ");
3865           if   IsRcwaMappingOfZ(f)
3866           then DisplayAffineMappingOfZ(c[1]);
3867           elif IsRcwaMappingOfZxZ(f)
3868           then DisplayAffineMappingOfZxZ(c[1]);
3869           elif IsRcwaMappingOfZ_pi(f)
3870           then DisplayAffineMappingOfZ_pi(c[1]);
3871           else DisplayAffineMappingOfGFqx(c[1],SizeScreen()[1]-48); fi;
3872         else
3873           Print(" with modulus ",ModulusAsFormattedString(m));
3874           if   HasOrder(f) and not (HasIsTame(f) and not IsTame(f))
3875           then Print(", of order ",Order(f)); fi;
3876           Print("\n\n");
3877           scr := SizeScreen()[1] - 2;
3878           if   IsRcwaMappingOfZOrZ_pi(f) then l1 := Int(scr/2);
3879           elif IsRcwaMappingOfZxZ(f)     then l1 := Int(2*scr/5);
3880           else                                l1 := Int(scr/3); fi;
3881           mstr := ModulusAsFormattedString(m);
3882           if l1 - Length(mstr) - 6 <= 0 then mstr := "<modulus>"; fi;
3883           mcharstop := Length(mstr) + Length(varname) - 1;
3884           l2 := Int((l1 - mcharstop - 6)/2);
3885           l3 := Int((scr - l1 - Length(mapname) - 3)/2);
3886           if   l3 < 3
3887           then mapname := "Image of "; l3 := Int((scr-l1-12)/2); fi;
3888           if Length(varname) = 5 then l3 := l3 - 2; fi;
3889           if   mapname = "Image of "
3890           then imageexpr := Concatenation(mapname,varname);
3891           else imageexpr := Concatenation(varname,"^",mapname); fi;
3892           flushlng := l1 - maxreschars - 1;
3893           Print(IdChars(l2," "),varname," mod ",mstr,
3894                 IdChars(l1-l2-mcharstop-6," "),"|",IdChars(l3," "),
3895                 imageexpr,"\n",IdChars(l1,"-"),"+",IdChars(scr-l1-1,"-"));
3896           poses := AsSortedList(List(Set(c),t->Positions(c,t)));
3897           for pos in poses do
3898             str := " ";
3899             for i in pos do
3900               if IsRcwaMappingOfZOrZ_pi(f) then
3901                 Append(str,String(r[i],maxreschars+1));
3902               else
3903                 Append(str,String(BlankFreeString(r[i]),-(maxreschars+1)));
3904               fi;
3905               if Length(str) >= flushlng then
3906                 if   Length(str) < l1
3907                 then Print("\n",String(str, -l1),"| ");
3908                 else Print("\n",String(" < ... > ",-l1),"| "); fi;
3909                 str := " ";
3910               fi;
3911             od;
3912             if   str <> " "
3913             then Print("\n",String(str, -l1),"| "); fi;
3914             if   IsRcwaMappingOfZ(f)
3915             then DisplayAffineMappingOfZ(c[pos[1]]);
3916             elif IsRcwaMappingOfZxZ(f)
3917             then DisplayAffineMappingOfZxZ(c[pos[1]]);
3918             elif IsRcwaMappingOfZ_pi(f)
3919             then DisplayAffineMappingOfZ_pi(c[pos[1]]);
3920             else DisplayAffineMappingOfGFqx(c[pos[1]],scr-l1-4); fi;
3921           od;
3922           Print("\n");
3923         fi;
3924    fi;
3925    if ValueOption("NoLineFeed") <> true then Print("\n"); fi;
3926  end );
3927
3928#############################################################################
3929##
3930#M  LaTeXStringRcwaMapping( <f> ) . . . . . . . . . .  for rcwa mappings of Z
3931##
3932InstallMethod( LaTeXStringRcwaMapping,
3933               "for rcwa mappings of Z in standard rep. (RCWA)",
3934               true, [ IsRcwaMappingOfZInStandardRep ], 0,
3935
3936  function ( f )
3937
3938    local  LaTeXAffineMappingOfZ, append, indent, varname, german,
3939           gens, c, m, res, P, str, affs, affstrings, maxafflng, i, j;
3940
3941    append := function ( arg )
3942      str := CallFuncList(Concatenation,
3943                          Concatenation([str],List(arg,String)));
3944    end;
3945
3946    LaTeXAffineMappingOfZ := function ( t )
3947
3948      local  append, str, a, b, c, n;
3949
3950      append := function ( arg )
3951        str := CallFuncList(Concatenation,
3952                            Concatenation([str],List(arg,String)));
3953      end;
3954
3955      a := t[1]; b := t[2]; c := t[3];
3956      str := ""; n := varname;
3957
3958      if c > 1 and Number([a,b],n->n<>0) > 1 then append("("); fi;
3959      if a <> 0 then
3960        if a = -1 then append("-"); elif a <> 1 then append(a); fi;
3961        append(n);
3962        if b > 0 then append("+"); fi;
3963      fi;
3964      if a = 0 or b <> 0 then append(b); fi;
3965      if c > 1 and Number([a,b],n->n<>0) > 1 then append(")"); fi;
3966      if c > 1 then append("/",c); fi;
3967
3968      return str;
3969    end;
3970
3971    if HasLaTeXString(f) then return LaTeXString(f); fi;
3972
3973    indent := ValueOption("Indentation");
3974    if not IsPosInt(indent)
3975    then indent := ""; else indent := String(" ",indent); fi;
3976    str := indent;
3977
3978    if ValueOption("Factorization") = true and IsBijective(f) then
3979      gens := List(FactorizationIntoCSCRCT(f),LaTeXString);
3980      append("      &");
3981      for i in [1..Length(gens)] do
3982        append(gens[i]);
3983        if i < Length(gens) then
3984          if i mod 5 = 0 then append(" \\\\\n"); fi;
3985          if i mod 5 in [2,4] then append("\n"); fi;
3986          append(" \\cdot ");
3987          if i mod 5 = 0 then append("&"); fi;
3988        else append("\n"); fi;
3989      od;
3990      return str;
3991    fi;
3992
3993    german :=   ValueOption("German") = true
3994             or ValueOption("german") = true;
3995    varname := First(List(["varname","VarName"],ValueOption),
3996                     name->name<>fail);
3997    if varname = fail then varname := "n"; fi;
3998
3999    c := Coefficients(f); m := Length(c);
4000    if m = 1 then
4001      return Concatenation("n \\ \\mapsto \\ ",LaTeXAffineMappingOfZ(c[1]));
4002    fi;
4003    res := AllResidues(Integers,m);
4004
4005    append("n \\ \\mapsto \\\n",indent,"\\begin{cases}\n");
4006
4007    P := ShallowCopy(LargestSourcesOfAffineMappings(f));
4008    Sort(P,function(Pi,Pj) return Density(Pi)>Density(Pj); end);
4009
4010    affs := List(P,preimg->c[First([1..Length(res)],i->res[i] in preimg)]);
4011    P    := List(P,AsUnionOfFewClasses);
4012
4013    affstrings := List( affs, LaTeXAffineMappingOfZ );
4014    maxafflng  := Maximum( List( affstrings, Length ) );
4015
4016    for i in [1..Length(P)] do
4017      append(indent,"  ",affstrings[i],
4018             String("",maxafflng-Length(affstrings[i])));
4019      if german then append(" & \\text{falls}");
4020                else append(" & \\text{if}"); fi;
4021      append(" \\ ",varname," \\in ");
4022      for j in [1..Length(P[i])] do
4023        append(String(Residue(P[i][j])),"(",String(Modulus(P[i][j])),")");
4024        if j < Length(P[i]) then append(" \\cup "); fi;
4025      od;
4026      if i = Length(P) then append(".\n"); else append(", \\\\\n"); fi;
4027    od;
4028
4029    append(indent,"\\end{cases}\n");
4030    return str;
4031  end );
4032
4033#############################################################################
4034##
4035#M  LaTeXStringRcwaMapping( <f> ) . . . . . . . . .  for rcwa mappings of Z^2
4036##
4037InstallMethod( LaTeXStringRcwaMapping,
4038               "for rcwa mappings of Z^2 (RCWA)",
4039               true, [ IsRcwaMappingOfZxZ ], 0,
4040
4041  function ( f )
4042
4043    local  LaTeXAffineMappingOfZxZ, append, indent, varname, german,
4044           gens, c, m, res, P, str, affs, affstrings, maxafflng, i, j;
4045
4046    append := function ( arg )
4047      str := CallFuncList(Concatenation,
4048                          Concatenation([str],List(arg,String)));
4049    end;
4050
4051    LaTeXAffineMappingOfZxZ := function ( t )
4052
4053      local  append, LaTeXaff, str,
4054             a, b, c, d, e, f, g, d1, d2, g1, g2, m, n;
4055
4056      append := function ( arg )
4057        str := CallFuncList(Concatenation,
4058                            Concatenation([str],List(arg,String)));
4059      end;
4060
4061      LaTeXaff := function ( a, b, c, d )
4062        if d > 1 and Number([a,b,c],n->n<>0) > 1 then append("("); fi;
4063        if a <> 0 then
4064          if a = -1 then append("-"); elif a <> 1 then append(a); fi;
4065          append(m);
4066          if b > 0 or (b = 0 and c > 0) then append("+"); fi;
4067        fi;
4068        if b <> 0 then
4069          if b = -1 then append("-"); elif b <> 1 then append(b); fi;
4070          append(n);
4071          if c > 0 then append("+"); fi;
4072        fi;
4073        if (a = 0 and b = 0) or c <> 0 then append(c); fi;
4074        if d > 1 and Number([a,b,c],n->n<>0) > 1 then append(")"); fi;
4075        if d > 1 then append("/",d); fi;
4076      end;
4077
4078      str := "";
4079      m := varname{[1]}; n := varname{[2]};
4080      a := t[1][1][1]; b := t[1][1][2];
4081      c := t[1][2][1]; d := t[1][2][2];
4082      e := t[2][1];    f := t[2][2];
4083      g := t[3];
4084      d1 := Gcd(a,c,e,g); d2 := Gcd(b,d,f,g);
4085      a := a/d1; c := c/d1; e := e/d1; g1 := g/d1;
4086      b := b/d2; d := d/d2; f := f/d2; g2 := g/d2;
4087      append("(");
4088      LaTeXaff(a,c,e,g1); append(","); LaTeXaff(b,d,f,g2);
4089      append(")");
4090      return str;
4091    end;
4092
4093    if HasLaTeXString(f) then return LaTeXString(f); fi;
4094
4095    indent := ValueOption("Indentation");
4096    if not IsPosInt(indent)
4097    then indent := ""; else indent := String(" ",indent); fi;
4098    str := indent;
4099
4100    if ValueOption("Factorization") = true and IsBijective(f) then
4101      gens := List(FactorizationIntoCSCRCT(f),LaTeXString);
4102      append("      &");
4103      for i in [1..Length(gens)] do
4104        append(gens[i]);
4105        if i < Length(gens) then
4106          if i mod 2 = 0 then append(" \\\\\n"); else append("\n"); fi;
4107          append(" \\cdot ");
4108          if i mod 2 = 0 then append("&"); fi;
4109        else append("\n"); fi;
4110      od;
4111      return str;
4112    fi;
4113
4114    german :=   ValueOption("German") = true
4115             or ValueOption("german") = true;
4116    varname := First(List(["varnames","VarNames"],ValueOption),
4117                     names->names<>fail);
4118    if varname = fail then varname := "mn"; fi;
4119
4120    c := Coefficients(f); m := Modulus(f);
4121    if IsOne(m) then
4122      return Concatenation("(",varname{[1]},",",varname{[2]},")",
4123                           "\\ \\mapsto \\ ",
4124                           LaTeXAffineMappingOfZxZ(c[1]));
4125    fi;
4126    res := AllResidues(Integers^2,m);
4127
4128    append("(",varname{[1]},",",varname{[2]},")","\\ \\mapsto \\\n",
4129           indent,"\\begin{cases}\n");
4130
4131    P := ShallowCopy(LargestSourcesOfAffineMappings(f));
4132    Sort(P,function(Pi,Pj) return Density(Pi)>Density(Pj); end);
4133
4134    affs := List(P,preimg->c[First([1..Length(res)],i->res[i] in preimg)]);
4135    P    := List(P,AsUnionOfFewClasses);
4136
4137    affstrings := List( affs, LaTeXAffineMappingOfZxZ );
4138    maxafflng  := Maximum( List( affstrings, Length ) );
4139
4140    for i in [1..Length(P)] do
4141      append(indent,"  ",affstrings[i],
4142             String("",maxafflng-Length(affstrings[i])));
4143      if german then append(" & \\text{falls}");
4144                else append(" & \\text{if}"); fi;
4145      append(" \\ (",varname{[1]},",",varname{[2]},") \\in ");
4146      for j in [1..Length(P[i])] do
4147        append(ReplacedString(ViewString(P[i][j]),"Z","\\mathbb{Z}"));
4148        if j < Length(P[i]) then append(" \\cup "); fi;
4149      od;
4150      if i = Length(P) then append(".\n"); else append(", \\\\\n"); fi;
4151    od;
4152
4153    append(indent,"\\end{cases}\n");
4154    return str;
4155  end );
4156
4157#############################################################################
4158##
4159#M  LaTeXAndXDVI( <f> ) . . . . . . . . . . . . . . .  for rcwa mappings of Z
4160##
4161InstallMethod( LaTeXAndXDVI,
4162               "for rcwa mappings of Z", true, [ IsRcwaMappingOfZ ], 0,
4163
4164  function ( f )
4165
4166    local  tmpdir, file, stream, str, latex, dvi, m, sizes, size,
4167           jectivity, cwop;
4168
4169    tmpdir := DirectoryTemporary( );
4170    file   := Filename(tmpdir,"rcwamap.tex");
4171    stream := OutputTextFile(file,false);
4172    SetPrintFormattingStatus(stream,false);
4173    AppendTo(stream,"\\documentclass[fleqn]{article}\n",
4174                    "\\usepackage{amsmath}\n",
4175                    "\\usepackage{amssymb}\n\n",
4176                    "\\setlength{\\paperwidth}{84cm}\n",
4177                    "\\setlength{\\textwidth}{80cm}\n",
4178                    "\\setlength{\\paperheight}{59.5cm}\n",
4179                    "\\setlength{\\textheight}{57cm}\n\n",
4180                    "\\begin{document}\n\n");
4181    sizes := ["Huge","huge","Large","large"];
4182    m := Modulus(f);
4183    if   ValueOption("Factorization") <> true
4184    then size := LogInt(Int(m/16)+1,2)+1;
4185    else size := Int(Length(FactorizationIntoCSCRCT(f))/50) + 1; fi;
4186    if size < 5 then AppendTo(stream,"\\begin{",sizes[size],"}\n\n"); fi;
4187    if   IsBijective(f)  then jectivity := " bijective";
4188    elif IsInjective(f)  then jectivity := "n injective, but not surjective";
4189    elif IsSurjective(f) then jectivity := " surjective, but not injective";
4190    else jectivity := " neither injective nor surjective"; fi;
4191    if   IsClassWiseOrderPreserving(f)
4192    then cwop := " class-wise order-preserving"; else cwop := ""; fi;
4193    AppendTo(stream,"\\noindent A",jectivity,cwop,
4194             " rcwa mapping of \\(\\mathbb{Z}\\) \\newline\nwith modulus ",
4195             String(Modulus(f)),", multiplier ",String(Multiplier(f)),
4196             " and divisor ",String(Divisor(f)),", given by\n");
4197    AppendTo(stream,"\\begin{align*}\n");
4198    str := LaTeXStringRcwaMapping(f:Indentation:=2);
4199    AppendTo(stream,str,"\\end{align*}");
4200    if HasIsTame(f) then
4201      if IsTame(f) then AppendTo(stream,"\nThis mapping is tame.");
4202                   else AppendTo(stream,"\nThis mapping is wild."); fi;
4203    fi;
4204    if HasOrder(f) then
4205      AppendTo(stream,"\nThe order of this mapping is \\(",
4206               IntOrInfinityToLaTeX(Order(f)),"\\).");
4207    fi;
4208    if HasIsTame(f) or HasOrder(f) then AppendTo(stream," \\newline"); fi;
4209    if IsBijective(f) then
4210      if IsClassWiseOrderPreserving(f) then
4211        AppendTo(stream,"\n\\noindent The determinant of this mapping is ",
4212                 String(Determinant(f)),", and its sign is ",
4213                 String(Sign(f)),".");
4214      else
4215        AppendTo(stream,"\n\\noindent The sign of this mapping is ",
4216                 String(Sign(f)),".");
4217      fi;
4218    fi;
4219    if size < 5 then AppendTo(stream,"\n\n\\end{",sizes[size],"}"); fi;
4220    AppendTo(stream,"\n\n\\end{document}\n");
4221    latex := Filename(DirectoriesSystemPrograms( ),"latex");
4222    Process(tmpdir,latex,InputTextNone( ),OutputTextNone( ),[file]);
4223    dvi := Filename(DirectoriesSystemPrograms( ),"xdvi");
4224    Process(tmpdir,dvi,InputTextNone( ),OutputTextNone( ),
4225            ["-paper","a1r","rcwamap.dvi"]);
4226  end );
4227
4228#############################################################################
4229##
4230#M  LaTeXAndXDVI( <f> ) . . . . . . . . . . . . . .  for rcwa mappings of ZxZ
4231##
4232InstallMethod( LaTeXAndXDVI,
4233               "for rcwa mappings of ZxZ", true, [ IsRcwaMappingOfZxZ ], 0,
4234
4235  function ( f )
4236
4237    local  tmpdir, file, stream, str, latex, dvi, jectivity, cwop;
4238
4239    tmpdir := DirectoryTemporary( );
4240    file   := Filename(tmpdir,"rcwamap.tex");
4241    stream := OutputTextFile(file,false);
4242    SetPrintFormattingStatus(stream,false);
4243    AppendTo(stream,"\\documentclass[fleqn]{article}\n",
4244                    "\\usepackage{amsmath}\n",
4245                    "\\usepackage{amssymb}\n\n",
4246                    "\\setlength{\\paperwidth}{84cm}\n",
4247                    "\\setlength{\\textwidth}{80cm}\n",
4248                    "\\setlength{\\paperheight}{59.5cm}\n",
4249                    "\\setlength{\\textheight}{57cm}\n\n",
4250                    "\\begin{document}\n\n");
4251    if   IsBijective(f)  then jectivity := " bijective";
4252    elif IsInjective(f)  then jectivity := "n injective, but not surjective";
4253    elif IsSurjective(f) then jectivity := " surjective, but not injective";
4254    else jectivity := " neither injective nor surjective"; fi;
4255    if   IsClassWiseOrderPreserving(f)
4256    then cwop := " class-wise order-preserving"; else cwop := ""; fi;
4257    AppendTo(stream,"\\noindent A",jectivity,cwop,
4258             " rcwa mapping of \\(\\mathbb{Z}^2\\) with modulus ",
4259             "\\(",ReplacedString(ModulusAsFormattedString(Modulus(f)),"Z",
4260                                                           "\\mathbb{Z}"),
4261             "\\), given by\n");
4262    AppendTo(stream,"\\begin{align*}\n");
4263    str := LaTeXStringRcwaMapping(f:Indentation:=2);
4264    AppendTo(stream,str,"\\end{align*}");
4265    if HasIsTame(f) then
4266      if IsTame(f) then AppendTo(stream,"\nThis mapping is tame.");
4267                   else AppendTo(stream,"\nThis mapping is wild."); fi;
4268    fi;
4269    if HasOrder(f) then
4270      AppendTo(stream,"\nThe order of this mapping is \\(",
4271               IntOrInfinityToLaTeX(Order(f)),"\\).");
4272    fi;
4273    if HasIsTame(f) or HasOrder(f) then AppendTo(stream," \\newline"); fi;
4274    AppendTo(stream,"\n\n\\end{document}\n");
4275    latex := Filename(DirectoriesSystemPrograms( ),"latex");
4276    Process(tmpdir,latex,InputTextNone( ),OutputTextNone( ),[file]);
4277    dvi := Filename(DirectoriesSystemPrograms( ),"xdvi");
4278    Process(tmpdir,dvi,InputTextNone( ),OutputTextNone( ),
4279            ["-paper","a1r","rcwamap.dvi"]);
4280  end );
4281
4282#############################################################################
4283##
4284#S  Comparing rcwa mappings. ////////////////////////////////////////////////
4285##
4286#############################################################################
4287
4288#############################################################################
4289##
4290#M  \=( <f>, <g> ) . . . . . . . . . . . . . for rcwa mappings of Z or Z_(pi)
4291##
4292InstallMethod( \=,
4293               "for two rcwa mappings of Z or Z_(pi), standard rep. (RCWA)",
4294               IsIdenticalObj,
4295               [ IsRcwaMappingOfZOrZ_piInStandardRep,
4296                 IsRcwaMappingOfZOrZ_piInStandardRep ], 0,
4297               function ( f, g ) return f!.coeffs = g!.coeffs; end );
4298InstallMethod( \=,
4299               "for two rcwa mappings of Z or Z_(pi), sparse rep. (RCWA)",
4300               IsIdenticalObj,
4301               [ IsRcwaMappingOfZOrZ_piInSparseRep,
4302                 IsRcwaMappingOfZOrZ_piInSparseRep ], 0,
4303               function ( f, g ) return f!.coeffs = g!.coeffs; end );
4304
4305#############################################################################
4306##
4307#M  \=( <f>, <g> ) . . . . . . . . . . . . . . . . . for rcwa mappings of Z^2
4308##
4309InstallMethod( \=,
4310               "for two rcwa mappings of Z^2 (RCWA)",
4311               IsIdenticalObj,
4312               [ IsRcwaMappingOfZxZInStandardRep,
4313                 IsRcwaMappingOfZxZInStandardRep ], 0,
4314
4315  function ( f, g )
4316    return f!.modulus = g!.modulus and f!.coeffs = g!.coeffs;
4317  end );
4318
4319#############################################################################
4320##
4321#M  \=( <f>, <g> ) . . . . . . . . . . . . . .  for rcwa mappings of GF(q)[x]
4322##
4323InstallMethod( \=,
4324               "for two rcwa mappings of GF(q)[x] (RCWA)",
4325               IsIdenticalObj,
4326               [ IsRcwaMappingOfGFqxInStandardRep,
4327                 IsRcwaMappingOfGFqxInStandardRep ], 0,
4328
4329  function ( f, g )
4330    return f!.modulus = g!.modulus and f!.coeffs = g!.coeffs;
4331  end );
4332
4333#############################################################################
4334##
4335#M  \=( <f>, <g> ) . . . . . . .  for rcwa mappings, standard vs. sparse rep.
4336#M  \=( <f>, <g> ) . . . . . . .  for rcwa mappings, sparse vs. standard rep.
4337##
4338InstallMethod( \=,
4339               "for two rcwa mappings, standard vs. sparse rep. (RCWA)",
4340               IsIdenticalObj,
4341               [ IsRcwaMappingInStandardRep, IsRcwaMappingInSparseRep ], 0,
4342               function ( f, g ) return SparseRepresentation(f) = g; end );
4343InstallMethod( \=,
4344               "for two rcwa mappings, sparse vs. standard rep. (RCWA)",
4345               IsIdenticalObj,
4346               [ IsRcwaMappingInSparseRep, IsRcwaMappingInStandardRep ], 0,
4347               function ( f, g ) return f = SparseRepresentation(g); end );
4348
4349#############################################################################
4350##
4351#M  \<( <f>, <g> ) . . . . . . . . . . . . for rcwa mappings in standard rep.
4352#M  \<( <f>, <g> ) . . . . . . . . . . . . . for rcwa mappings in sparse rep.
4353#M  \<( <f>, <g> ) . . . . . . .  for rcwa mappings: standard vs. sparse rep.
4354#M  \<( <f>, <g> ) . . . . . . .  for rcwa mappings: sparse vs. standard rep.
4355##
4356##  Total ordering of rcwa maps (for technical purposes, only).
4357##  More methods are needed as soon as further representations of
4358##  rcwa mappings are implemented.
4359##
4360InstallMethod( \<,
4361               "for rcwa mappings in standard rep. (RCWA)", IsIdenticalObj,
4362               [ IsRcwaMappingInStandardRep, IsRcwaMappingInStandardRep ], 0,
4363
4364  function ( f, g )
4365    if   f!.modulus <> g!.modulus
4366    then return f!.modulus < g!.modulus;
4367    else return f!.coeffs  < g!.coeffs; fi;
4368  end );
4369
4370InstallMethod( \<,
4371               "for rcwa mappings in sparse rep. (RCWA)", IsIdenticalObj,
4372               [ IsRcwaMappingInSparseRep, IsRcwaMappingInSparseRep ], 0,
4373
4374  function ( f, g )
4375
4376    local  r, m, cf, cg;
4377
4378    if   f!.modulus <> g!.modulus
4379    then return f!.modulus < g!.modulus;
4380    elif f = g
4381    then return false; else
4382      m := f!.modulus; r := 0;
4383      while r < m do
4384        cf := First(f!.coeffs,c->c[1] = r mod c[2]){[3..5]};
4385        cg := First(g!.coeffs,c->c[1] = r mod c[2]){[3..5]};
4386        if cf <> cg then return cf < cg; fi;
4387        r := r + 1;
4388      od;
4389    fi;
4390  end );
4391
4392InstallMethod( \<,
4393               "for rcwa mappings: standard vs. sparse rep. (RCWA)",
4394               IsIdenticalObj,
4395               [ IsRcwaMappingInStandardRep, IsRcwaMappingInSparseRep ], 0,
4396
4397  function ( f, g )
4398
4399    local  r, m, cf, cg;
4400
4401    if   f!.modulus <> g!.modulus
4402    then return f!.modulus < g!.modulus;
4403    elif f = g
4404    then return false; else
4405      m := f!.modulus; r := 0;
4406      while r < m do
4407        cf := f!.coeffs[r+1];
4408        cg := First(g!.coeffs,c->c[1] = r mod c[2]){[3..5]};
4409        if cf <> cg then return cf < cg; fi;
4410        r := r + 1;
4411      od;
4412    fi;
4413  end );
4414
4415InstallMethod( \<,
4416               "for rcwa mappings: sparse vs. standard rep. (RCWA)",
4417               IsIdenticalObj,
4418               [ IsRcwaMappingInSparseRep, IsRcwaMappingInStandardRep ], 0,
4419
4420  function ( f, g )
4421
4422    local  r, m, cf, cg;
4423
4424    if   f!.modulus <> g!.modulus
4425    then return f!.modulus < g!.modulus;
4426    elif f = g
4427    then return false; else
4428      m := f!.modulus; r := 0;
4429      while r < m do
4430        cf := First(f!.coeffs,c->c[1] = r mod c[2]){[3..5]};
4431        cg := g!.coeffs[r+1];
4432        if cf <> cg then return cf < cg; fi;
4433        r := r + 1;
4434      od;
4435    fi;
4436  end );
4437
4438#############################################################################
4439##
4440#S  On the zero- and the identity rcwa mapping. /////////////////////////////
4441##
4442#############################################################################
4443
4444#############################################################################
4445##
4446#V  ZeroRcwaMappingOfZ . . . . . . . . . . . . . . . . zero rcwa mapping of Z
4447#V  ZeroRcwaMappingOfZxZ . . . . . . . . . . . . . . zero rcwa mapping of Z^2
4448##
4449InstallValue( ZeroRcwaMappingOfZ, RcwaMapping( [ [ 0, 0, 1 ] ] ) );
4450SetIsZero( ZeroRcwaMappingOfZ, true );
4451SetImagesSource( ZeroRcwaMappingOfZ, [ 0 ] );
4452InstallValue( ZeroRcwaMappingOfZxZ,
4453              RcwaMapping( Integers^2, [ [ 1, 0 ], [ 0, 1 ] ],
4454                           [ [ [ [ 0, 0 ], [ 0, 0 ] ], [ 0, 0 ], 1 ] ] ) );
4455SetIsZero( ZeroRcwaMappingOfZxZ, true );
4456SetImagesSource( ZeroRcwaMappingOfZ, [ 0, 0 ] );
4457
4458#############################################################################
4459##
4460#M  Zero( <f> ) . . . . . . . . . . . for rcwa mappings of Z in standard rep.
4461#M  Zero( <f> ) . . . . . . . . . . . . for rcwa mappings of Z in sparse rep.
4462#M  Zero( <f> ) . . . . . . . . . . . . . . . . . .  for rcwa mappings of Z^2
4463##
4464##  Zero rcwa mapping of Z or Z^2, respectively.
4465##
4466InstallMethod( Zero, "for rcwa mappings of Z in standard rep. (RCWA)", true,
4467               [ IsRcwaMappingOfZInStandardRep ], 0,
4468               f -> ZeroRcwaMappingOfZ );
4469InstallMethod( Zero, "for rcwa mappings of Z in sparse rep. (RCWA)", true,
4470               [ IsRcwaMappingOfZInSparseRep ], 0,
4471               f -> SparseRep( ZeroRcwaMappingOfZ ) );
4472InstallMethod( Zero, "for rcwa mappings of Z^2 (RCWA)", true,
4473               [ IsRcwaMappingOfZxZInStandardRep ], 0,
4474               f -> ZeroRcwaMappingOfZxZ );
4475
4476#############################################################################
4477##
4478#M  Zero( <f> ) . . . . . . . . . . . . . . . . . for rcwa mappings of Z_(pi)
4479##
4480##  Zero rcwa mapping of Z_(pi).
4481##
4482InstallMethod( Zero, "for rcwa mappings of Z_(pi) (RCWA)", true,
4483               [ IsRcwaMappingOfZ_piInStandardRep ], 0,
4484
4485  function ( f )
4486
4487    local  zero;
4488
4489    zero := RcwaMappingNC( NoninvertiblePrimes(Source(f)), [ [ 0, 0, 1 ] ] );
4490    SetIsZero( zero, true );
4491    SetImagesSource( zero, [ 0 ] );
4492    return zero;
4493  end );
4494
4495#############################################################################
4496##
4497#M  Zero( <f> ) . . . . . . . . . . . . . . . . for rcwa mappings of GF(q)[x]
4498##
4499##  Zero rcwa mapping of GF(q)[x].
4500##
4501InstallMethod( Zero, "for rcwa mappings of GF(q)[x] (RCWA)", true,
4502               [ IsRcwaMappingOfGFqxInStandardRep ], 0,
4503
4504  function ( f )
4505
4506    local  zero;
4507
4508    zero := RcwaMappingNC( Size(UnderlyingField(f)), One(Source(f)),
4509                           [ [ 0, 0, 1 ] ] * One(Source(f)) );
4510    SetIsZero( zero, true );
4511    SetImagesSource( zero, [ Zero(Source(f)) ] );
4512    return zero;
4513  end );
4514
4515#############################################################################
4516##
4517#M  IsZero( <f> ) . . . . . . . . . . . . . . . . . . . . . for rcwa mappings
4518##
4519##  <f> = zero rcwa mapping?
4520##
4521InstallMethod( IsZero, "for rcwa mappings in standard rep. (RCWA)",
4522               true, [ IsRcwaMappingInStandardRep ], 0,
4523
4524  function ( f )
4525    if not IsRing( Source( f ) ) then TryNextMethod( ); fi;
4526    return f!.coeffs = [ [ 0, 0, 1 ] ] * One( Source( f ) );
4527  end );
4528
4529InstallMethod( IsZero, "for rcwa mappings of Z in sparse rep. (RCWA)",
4530               true, [ IsRcwaMappingOfZInSparseRep ], 0,
4531               f -> f!.coeffs = [ [ 0, 1, 0, 0, 1 ] ] );
4532
4533InstallMethod( IsZero, "for rcwa mappings of Z^2 (RCWA)", true,
4534               [ IsRcwaMappingOfZxZInStandardRep ], 0,
4535               f -> f = ZeroRcwaMappingOfZxZ );
4536
4537#############################################################################
4538##
4539#V  IdentityRcwaMappingOfZ . . . . . . . . . . . . identity rcwa mapping of Z
4540#V  IdentityRcwaMappingOfZxZ . . . . . . . . . . identity rcwa mapping of Z^2
4541##
4542InstallValue( IdentityRcwaMappingOfZ, RcwaMapping( [ [ 1, 0, 1 ] ] ) );
4543SetIsOne( IdentityRcwaMappingOfZ, true );
4544InstallValue( IdentityRcwaMappingOfZxZ,
4545              RcwaMapping( Integers^2, [ [ 1, 0 ], [ 0, 1 ] ],
4546                           [ [ [ [ 1, 0 ], [ 0, 1 ] ], [ 0, 0 ], 1 ] ] ) );
4547SetIsOne( IdentityRcwaMappingOfZxZ, true );
4548
4549#############################################################################
4550##
4551#M  One( <f> ) . . . . . . . . . . .  for rcwa mappings of Z in standard rep.
4552#M  One( <f> ) . . . . . . . . . . . .  for rcwa mappings of Z in sparse rep.
4553#M  One( <f> ) . . . . . . . . . . . . . . . . . . . for rcwa mappings of Z^2
4554##
4555##  Identity rcwa mapping of Z or Z^2, respectively.
4556##
4557InstallMethod( One, "for rcwa mappings of Z in standard rep. (RCWA)", true,
4558               [ IsRcwaMappingOfZInStandardRep ], 0,
4559               f -> IdentityRcwaMappingOfZ );
4560InstallMethod( One, "for rcwa mappings of Z in sparse rep. (RCWA)", true,
4561               [ IsRcwaMappingOfZInSparseRep ], 0,
4562               f -> SparseRep( IdentityRcwaMappingOfZ ) );
4563InstallMethod( One, "for rcwa mappings of Z^2 (RCWA)", true,
4564               [ IsRcwaMappingOfZxZInStandardRep ], 0,
4565               f -> IdentityRcwaMappingOfZxZ );
4566
4567#############################################################################
4568##
4569#M  One( <f> ) . . . . . . . . . . . . . . . . .  for rcwa mappings of Z_(pi)
4570##
4571##  Identity rcwa mapping of Z_(pi).
4572##
4573InstallMethod( One, "for rcwa mappings of Z_(pi) (RCWA)", true,
4574               [ IsRcwaMappingOfZ_piInStandardRep ], 0,
4575
4576  function ( f )
4577
4578    local  one;
4579
4580    one := RcwaMappingNC( NoninvertiblePrimes(Source(f)), [ [ 1, 0, 1 ] ] );
4581    SetIsOne( one, true ); return one;
4582  end );
4583
4584#############################################################################
4585##
4586#M  One( <f> ) . . . . . . . . . . . . . . . .  for rcwa mappings of GF(q)[x]
4587##
4588##  Identity rcwa mapping of GF(q)[x].
4589##
4590InstallMethod( One, "for rcwa mappings of GF(q)[x] (RCWA)", true,
4591               [ IsRcwaMappingOfGFqxInStandardRep ], 0,
4592
4593  function ( f )
4594
4595    local  one;
4596
4597    one := RcwaMappingNC( Size(UnderlyingField(f)), One(Source(f)),
4598                          [ [ 1, 0, 1 ] ] * One( Source( f ) ) );
4599    SetIsOne( one, true ); return one;
4600  end );
4601
4602#############################################################################
4603##
4604#M  IsOne( <f> ) . . . . . . . . . . . . . . . . . . . . .  for rcwa mappings
4605##
4606##  <f> = identity rcwa mapping?
4607##
4608InstallMethod( IsOne, "for rcwa mappings in standard rep. (RCWA)",
4609               true, [ IsRcwaMappingInStandardRep ], 0,
4610
4611  function ( f )
4612    if not IsRing( Source( f ) ) then TryNextMethod( ); fi;
4613    return f!.coeffs = [ [ 1, 0, 1 ] ] * One( Source( f ) );
4614  end );
4615
4616InstallMethod( IsOne, "for rcwa mappings of Z in sparse rep. (RCWA)",
4617               true, [ IsRcwaMappingOfZInSparseRep ], 0,
4618               f -> f!.coeffs = [ [ 0, 1, 1, 0, 1 ] ] );
4619
4620InstallMethod( IsOne, "for rcwa mappings of Z^2 (RCWA)", true,
4621               [ IsRcwaMappingOfZxZInStandardRep ], 0,
4622               f -> f = IdentityRcwaMappingOfZxZ );
4623
4624#############################################################################
4625##
4626#M  ViewString( <zero> ) . . . . . . . . . . . . .  for the zero rcwa mapping
4627#M  ViewString( <one> )  . . . . . . . . . . .  for the identity rcwa mapping
4628##
4629InstallMethod( ViewString, "for the zero rcwa mapping (RCWA)", true,
4630               [ IsRcwaMapping and IsZero ], 0,
4631               f -> Concatenation("ZeroMapping( ",String(Source(f)),", ",
4632                                                  String(Source(f))," )") );
4633
4634InstallMethod( ViewString, "for the identity rcwa mapping (RCWA)", true,
4635               [ IsRcwaMapping and IsOne ], 0,
4636               f -> Concatenation("IdentityMapping( ",
4637                                  String(Source(f))," )") );
4638
4639#############################################################################
4640##
4641#S  Accessing the components of an rcwa mapping object. /////////////////////
4642##
4643#############################################################################
4644
4645#############################################################################
4646##
4647#M  Modulus( <f> ) . . . . . . . for rcwa mappings in standard representation
4648#M  Modulus( <f> ) . . . . . . . . for rcwa mappings in sparse representation
4649##
4650InstallMethod( Modulus, "for rcwa mappings in standard rep. (RCWA)",
4651               true, [ IsRcwaMappingInStandardRep ], 0, f -> f!.modulus );
4652InstallMethod( Modulus, "for rcwa mappings in sparse rep. (RCWA)",
4653               true, [ IsRcwaMappingInSparseRep ], 0, f -> f!.modulus );
4654
4655#############################################################################
4656##
4657#M  Coefficients( <f> ) . . . .  for rcwa mappings in standard representation
4658#M  Coefficients( <f> ) . . . . .  for rcwa mappings in sparse representation
4659##
4660InstallMethod( Coefficients, "for rcwa mappings in standard rep. (RCWA)",
4661               true, [ IsRcwaMappingInStandardRep ], 0, f -> f!.coeffs );
4662InstallMethod( Coefficients, "for rcwa mappings in sparse rep. (RCWA)",
4663               true, [ IsRcwaMappingInSparseRep ], 0, f -> f!.coeffs );
4664
4665#############################################################################
4666##
4667#S  Methods for the attributes and properties derived from the coefficients.
4668##
4669#############################################################################
4670
4671#############################################################################
4672##
4673#M  Multiplier( <f> ) . . . . . . . . . . . . . . . . . . . for rcwa mappings
4674##
4675InstallMethod( Multiplier, "for rcwa mappings in standard rep. (RCWA)",
4676               true, [ IsRcwaMappingInStandardRep ], 0,
4677               f -> Lcm( UnderlyingRing( FamilyObj( f ) ),
4678                         List( f!.coeffs, c -> c[1] ) ) );
4679InstallMethod( Multiplier, "for rcwa mappings in sparse rep. (RCWA)",
4680               true, [ IsRcwaMappingInSparseRep ], 0,
4681               f -> Lcm( UnderlyingRing( FamilyObj( f ) ),
4682                         List( f!.coeffs, c -> c[3] ) ) );
4683InstallMethod( Multiplier, "for rcwa mappings of Z^2 (RCWA)", true,
4684               [ IsRcwaMappingOfZxZInStandardRep ], 10,
4685               f -> Lcm( List( f!.coeffs, c -> c[1] ) ) );
4686InstallMethod( Multiplier, "for rcwa mappings of Z_(pi) (RCWA)", true,
4687               [ IsRcwaMappingOfZ_piInStandardRep ], 10,
4688               f -> Lcm( List( f!.coeffs,
4689                               c -> StandardAssociate(Source(f),c[1]) ) ) );
4690
4691#############################################################################
4692##
4693#M  Divisor( <f> ) . . . . . . . . . . . . . . . . . . . .  for rcwa mappings
4694##
4695InstallMethod( Divisor, "for rcwa mappings in standard rep. (RCWA)",
4696               true, [ IsRcwaMappingInStandardRep ], 0,
4697               f -> Lcm( UnderlyingRing( FamilyObj( f ) ),
4698                         List( f!.coeffs, c -> c[3] ) ) );
4699InstallMethod( Divisor, "for rcwa mappings in sparse rep. (RCWA)",
4700               true, [ IsRcwaMappingInSparseRep ], 0,
4701               f -> Lcm( UnderlyingRing( FamilyObj( f ) ),
4702                         List( f!.coeffs, c -> c[5] ) ) );
4703InstallMethod( Divisor, "for rcwa mappings of Z^2 (RCWA)", true,
4704               [ IsRcwaMappingOfZxZInStandardRep ], 0,
4705               f -> Lcm( Integers, List( f!.coeffs, c -> c[3] ) ) );
4706
4707#############################################################################
4708##
4709#M  IsIntegral( <f> ) . . . . . . . . . . . . . . . . . . . for rcwa mappings
4710##
4711InstallMethod( IsIntegral, "for rcwa mappings (RCWA)", true,
4712               [ IsRcwaMapping ], 0, f -> IsOne( Divisor( f ) ) );
4713
4714#############################################################################
4715##
4716#M  IsBalanced( <f> ) . . . . . . . . . . . . . . . . . . . for rcwa mappings
4717##
4718InstallMethod( IsBalanced, "for rcwa mappings (RCWA)", true,
4719               [ IsRcwaMapping ], 0,
4720               f -> Set( Factors( Multiplier( f ) ) )
4721                  = Set( Factors( Divisor( f ) ) ) );
4722InstallMethod( IsBalanced, "for rcwa mappings of Z^2 (RCWA)", true,
4723               [ IsRcwaMappingOfZxZ ], 0,
4724               f -> Set( Factors( DeterminantMat( Multiplier( f ) ) ) )
4725                  = Set( Factors( Divisor( f ) ) ) );
4726
4727#############################################################################
4728##
4729#M  PrimeSet( <f> ) . . . . . . . . . . . . . . . . . . . . for rcwa mappings
4730##
4731InstallMethod( PrimeSet, "for rcwa mappings (RCWA)", true,
4732               [ IsRcwaMapping ], 0,
4733
4734  function ( f )
4735    if   IsZero(Multiplier(f))
4736    then Error("PrimeSet: Multiplier must not be zero.\n"); fi;
4737    return Filtered( Union( Factors(Source(f),Modulus(f)),
4738                            Factors(Source(f),Multiplier(f)),
4739                            Factors(Source(f),Divisor(f)) ),
4740                     x -> IsIrreducibleRingElement( Source( f ), x ) );
4741  end );
4742
4743InstallMethod( PrimeSet, "for rcwa mappings of Z^2 (RCWA)", true,
4744               [ IsRcwaMappingOfZxZ ], 0,
4745
4746  function ( f )
4747    if   IsZero(Multiplier(f))
4748    then Error("PrimeSet: Multiplier must not be zero.\n"); fi;
4749    return Filtered( Union( # Factors(DeterminantMat(Modulus(f))),
4750                            Factors(DeterminantMat(Multiplier(f))),
4751                            Factors(Divisor(f)) ), IsPrimeInt );
4752  end );
4753
4754#############################################################################
4755##
4756#M  IsClassWiseTranslating( <f> ) . . . . . . . . . . . . . for rcwa mappings
4757##
4758InstallMethod( IsClassWiseTranslating,
4759               "for rcwa mappings in standard rep. (RCWA)", true,
4760               [ IsRcwaMappingInStandardRep ], 0,
4761               f -> ForAll(Coefficients(f),c->IsOne(c[1]) and IsOne(c[3])) );
4762InstallMethod( IsClassWiseTranslating,
4763               "for rcwa mappings in sparse rep. (RCWA)", true,
4764               [ IsRcwaMappingInSparseRep ], 0,
4765               f -> ForAll(Coefficients(f),c->IsOne(c[3]) and IsOne(c[5])) );
4766
4767#############################################################################
4768##
4769#M  IsClassWiseOrderPreserving( <f> ) . for rcwa mappings of Z, Z^2 or Z_(pi)
4770##
4771InstallMethod( IsClassWiseOrderPreserving,
4772               "for rcwa mappings of Z or Z_(pi) in standard rep. (RCWA)",
4773               true, [ IsRcwaMappingOfZOrZ_piInStandardRep ], 0,
4774               f -> ForAll( f!.coeffs, c -> c[ 1 ] > 0 ) );
4775InstallMethod( IsClassWiseOrderPreserving,
4776               "for rcwa mappings of Z or Z_(pi) in sparse rep. (RCWA)",
4777               true, [ IsRcwaMappingOfZOrZ_piInSparseRep ], 0,
4778               f -> ForAll( f!.coeffs, c -> c[ 3 ] > 0 ) );
4779InstallMethod( IsClassWiseOrderPreserving,
4780               "for rcwa mappings of Z^2 (RCWA)", true,
4781               [ IsRcwaMappingOfZxZInStandardRep ], 0,
4782               f -> ForAll( f!.coeffs, c -> DeterminantMat( c[ 1 ] ) > 0 ) );
4783
4784#############################################################################
4785##
4786#M  ClassWiseOrderPreservingOn( <f> )   for rcwa mappings of Z, Z^2 or Z_(pi)
4787##
4788InstallMethod( ClassWiseOrderPreservingOn,
4789               "for rcwa mappings of Z or Z_(pi) in standard rep. (RCWA)",
4790               true, [ IsRcwaMappingOfZOrZ_piInStandardRep ], 0,
4791               f -> ResidueClassUnion( Source( f ), Modulus( f ),
4792                      Filtered( [ 0 .. Modulus( f ) - 1 ],
4793                                r -> Coefficients( f )[r+1][1] > 0 ) ) );
4794InstallMethod( ClassWiseOrderPreservingOn,
4795               "for rcwa mappings of Z in sparse rep. (RCWA)",
4796               true, [ IsRcwaMappingOfZInSparseRep ], 0,
4797               f -> Union( List( Filtered( f!.coeffs, c -> c[3] > 0 ),
4798                                 d -> ResidueClass( d[1], d[2] ) ) ) );
4799InstallMethod( ClassWiseOrderPreservingOn,
4800               "for rcwa mappings of Z^2 (RCWA)",
4801               true, [ IsRcwaMappingOfZxZ ], 0,
4802               f -> ResidueClassUnion( Source( f ), Modulus( f ),
4803                      AllResidues( Source( f ), Modulus( f ) )
4804                        {Filtered([ 1 .. DeterminantMat( Modulus( f ) ) ],
4805                        r -> DeterminantMat(Coefficients(f)[r][1]) > 0)} ) );
4806
4807#############################################################################
4808##
4809#M  ClassWiseOrderReversingOn( <f> ) .  for rcwa mappings of Z, Z^2 or Z_(pi)
4810##
4811InstallMethod( ClassWiseOrderReversingOn,
4812               "for rcwa mappings of Z or Z_(pi) in standard rep. (RCWA)",
4813               true, [ IsRcwaMappingOfZOrZ_piInStandardRep ], 0,
4814               f -> ResidueClassUnion( Source( f ), Modulus( f ),
4815                      Filtered( [ 0 .. Modulus( f ) - 1 ],
4816                                r -> Coefficients( f )[r+1][1] < 0 ) ) );
4817InstallMethod( ClassWiseOrderReversingOn,
4818               "for rcwa mappings of Z in sparse rep. (RCWA)",
4819               true, [ IsRcwaMappingOfZInSparseRep ], 0,
4820               f -> Union( List( Filtered( f!.coeffs, c -> c[3] < 0 ),
4821                                 d -> ResidueClass( d[1], d[2] ) ) ) );
4822InstallMethod( ClassWiseOrderReversingOn,
4823               "for rcwa mappings of Z^2 (RCWA)",
4824               true, [ IsRcwaMappingOfZxZ ], 0,
4825               f -> ResidueClassUnion( Source( f ), Modulus( f ),
4826                      AllResidues( Source( f ), Modulus( f ) )
4827                        {Filtered([ 1 .. DeterminantMat( Modulus( f ) ) ],
4828                        r -> DeterminantMat(Coefficients(f)[r][1]) < 0)} ) );
4829
4830#############################################################################
4831##
4832##M  ClassWiseConstantOn( <f> ) . . . .  for rcwa mappings of Z, Z^2 or Z_(pi)
4833##
4834InstallMethod( ClassWiseConstantOn,
4835               "for rcwa mappings of Z or Z_(pi) in standard rep. (RCWA)",
4836               true, [ IsRcwaMappingOfZOrZ_piInStandardRep ], 0,
4837               f -> ResidueClassUnion( Source( f ), Modulus( f ),
4838                      Filtered( [ 0 .. Modulus( f ) - 1 ],
4839                                r -> Coefficients( f )[r+1][1] = 0 ) ) );
4840InstallMethod( ClassWiseConstantOn,
4841               "for rcwa mappings of Z in sparse rep. (RCWA)",
4842               true, [ IsRcwaMappingOfZInSparseRep ], 0,
4843               f -> Union( List( Filtered( f!.coeffs, c -> c[3] = 0 ),
4844                                 d -> ResidueClass( d[1], d[2] ) ) ) );
4845InstallMethod( ClassWiseConstantOn,
4846               "for rcwa mappings of Z^2 (RCWA)",
4847               true, [ IsRcwaMappingOfZxZ ], 0,
4848               f -> ResidueClassUnion( Source( f ), Modulus( f ),
4849                      AllResidues( Source( f ), Modulus( f ) )
4850                        {Filtered([ 1 .. DeterminantMat( Modulus( f ) ) ],
4851                        r -> IsZero(Coefficients(f)[r][1]))} ) );
4852
4853#############################################################################
4854##
4855#P  IsSignPreserving( <f> ) . . . . . . . . . . . . .  for rcwa mappings of Z
4856##
4857InstallMethod( IsSignPreserving, "for rcwa mappings of Z (RCWA)", true,
4858               [ IsRcwaMappingOfZ ], 0,
4859
4860  function ( f )
4861
4862    local  bound, ind;
4863
4864    if not IsClassWiseOrderPreserving(f) then return false; fi;
4865    if   IsRcwaMappingStandardRep(f) then ind := 2;
4866    elif IsRcwaMappingSparseRep(f)   then ind := 4;
4867    else TryNextMethod(); fi;
4868    bound := Maximum(1,Maximum(List(Coefficients(f),c->AbsInt(c[ind]))));
4869    return Minimum([0..bound]^f) >= 0 and Maximum([-bound..-1]^f) < 0;
4870  end );
4871
4872#############################################################################
4873##
4874#M  IncreasingOn( <f> ) . . . . . . . . .  for rcwa mappings in standard rep.
4875##
4876InstallMethod( IncreasingOn, "for rcwa mappings in standard rep. (RCWA)",
4877               true, [ IsRcwaMappingInStandardRep ], 0,
4878
4879  function ( f )
4880
4881    local  R, m, c, selection;
4882
4883    R := Source(f); m := Modulus(f); c := Coefficients(f);
4884    if   IsRing(R) then
4885      selection := Filtered([1..NumberOfResidues(R,m)],
4886                            r -> NumberOfResidues(R,c[r][3])
4887                               < NumberOfResidues(R,c[r][1]));
4888    elif IsZxZ(R) then
4889      selection := Filtered([1..NumberOfResidues(R,m)],
4890                            r -> c[r][3]^2 < NumberOfResidues(R,c[r][1]));
4891    else TryNextMethod(); fi;
4892    return ResidueClassUnion(R,m,AllResidues(R,m){selection});
4893  end );
4894
4895#############################################################################
4896##
4897#M  IncreasingOn( <f> ) . . . . . . . . for rcwa mappings of Z in sparse rep.
4898##
4899InstallMethod( IncreasingOn, "for rcwa mappings of Z in sparse rep. (RCWA)",
4900               true, [ IsRcwaMappingOfZInSparseRep ], 0,
4901
4902  function ( f )
4903    if ValueOption("classes") = true then
4904      return Set(Filtered(f!.coeffs,c->AbsInt(c[3])>c[5]),
4905                 c->ResidueClass(c[1],c[2]));
4906    else
4907      return ResidueClassUnion(Integers,
4908                               List(Filtered(f!.coeffs,c->AbsInt(c[3])>c[5]),
4909                                    c->c{[1,2]}));
4910    fi;
4911  end );
4912
4913#############################################################################
4914##
4915#M  DecreasingOn( <f> ) . . . . . . . . .  for rcwa mappings in standard rep.
4916##
4917InstallMethod( DecreasingOn, "for rcwa mappings in standard rep. (RCWA)",
4918               true, [ IsRcwaMappingInStandardRep ], 0,
4919
4920  function ( f )
4921
4922    local  R, m, c, selection;
4923
4924    R := Source(f); m := Modulus(f); c := Coefficients(f);
4925    if   IsRing(R) then
4926      selection := Filtered([1..NumberOfResidues(R,m)],
4927                            r -> NumberOfResidues(R,c[r][3])
4928                               > NumberOfResidues(R,c[r][1]));
4929    elif IsZxZ(R) then
4930      selection := Filtered([1..NumberOfResidues(R,m)],
4931                            r -> c[r][3]^2 > NumberOfResidues(R,c[r][1]));
4932    else TryNextMethod(); fi;
4933    return ResidueClassUnion(R,m,AllResidues(R,m){selection});
4934  end );
4935
4936#############################################################################
4937##
4938#M  DecreasingOn( <f> ) . . . . . . . . for rcwa mappings of Z in sparse rep.
4939##
4940InstallMethod( DecreasingOn, "for rcwa mappings of Z in sparse rep. (RCWA)",
4941               true, [ IsRcwaMappingOfZInSparseRep ], 0,
4942
4943  function ( f )
4944    if ValueOption("classes") = true then
4945      return Set(Filtered(f!.coeffs,c->AbsInt(c[3])<c[5]),
4946                 c->ResidueClass(c[1],c[2]));
4947    else
4948      return ResidueClassUnion(Integers,
4949                               List(Filtered(f!.coeffs,c->AbsInt(c[3])<c[5]),
4950                                    c->c{[1,2]}));
4951    fi;
4952  end );
4953
4954#############################################################################
4955##
4956#M  ShiftsUpOn( <f> )  . . . . . . . . . . . . . . . . for rcwa mappings of Z
4957##
4958InstallMethod( ShiftsUpOn, "for rcwa mappings of Z in standard rep. (RCWA)",
4959               true, [ IsRcwaMappingOfZInStandardRep ], 0,
4960               f -> ResidueClassUnion( Integers, Modulus(f),
4961                      Filtered( [0..Modulus(f)-1],
4962                                r -> Coefficients(f)[r+1]{[1,3]} = [1,1]
4963                                 and Coefficients(f)[r+1][2] > 0 ) ) );
4964InstallMethod( ShiftsUpOn, "for rcwa mappings of Z in sparse rep. (RCWA)",
4965               true, [ IsRcwaMappingOfZInSparseRep ], 0,
4966
4967  function ( f )
4968    if ValueOption("classes") = true then
4969      return Set(Filtered(f!.coeffs,
4970                           c->c{[3,5]}=[1,1] and c[4]>0),
4971                  c->ResidueClass(c[1],c[2]));
4972    else
4973      return ResidueClassUnion(Integers,
4974                               List(Filtered(f!.coeffs,
4975                                             c->c{[3,5]}=[1,1] and c[4]>0),
4976                                    c->c{[1,2]}));
4977    fi;
4978  end );
4979
4980#############################################################################
4981##
4982#M  ShiftsDownOn( <f> )  . . . . . . . . . . . . . . . for rcwa mappings of Z
4983##
4984InstallMethod( ShiftsDownOn,
4985               "for rcwa mappings of Z in standard rep. (RCWA)",
4986               true, [ IsRcwaMappingOfZInStandardRep ], 0,
4987               f -> ResidueClassUnion( Integers, Modulus(f),
4988                      Filtered( [0..Modulus(f)-1],
4989                                r -> Coefficients(f)[r+1]{[1,3]} = [1,1]
4990                                 and Coefficients(f)[r+1][2] < 0 ) ) );
4991InstallMethod( ShiftsDownOn, "for rcwa mappings of Z in sparse rep. (RCWA)",
4992               true, [ IsRcwaMappingOfZInSparseRep ], 0,
4993
4994  function ( f )
4995    if ValueOption("classes") = true then
4996      return Set(Filtered(f!.coeffs,
4997                           c->c{[3,5]}=[1,1] and c[4]<0),
4998                  c->ResidueClass(c[1],c[2]));
4999    else
5000      return ResidueClassUnion(Integers,
5001                               List(Filtered(f!.coeffs,
5002                                             c->c{[3,5]}=[1,1] and c[4]<0),
5003                                    c->c{[1,2]}));
5004    fi;
5005  end );
5006
5007#############################################################################
5008##
5009#M  MaximalShift( <f> )  . . . . . . . . . . . . . . . for rcwa mappings of Z
5010##
5011InstallMethod( MaximalShift,
5012               "for rcwa mappings of Z in standard rep. (RCWA)",
5013               true, [ IsRcwaMappingOfZInStandardRep ], 0,
5014               f -> Maximum( List( f!.coeffs, c -> AbsInt(c[2]) ) ) );
5015InstallMethod( MaximalShift,
5016               "for rcwa mappings of Z in sparse rep. (RCWA)",
5017               true, [ IsRcwaMappingOfZInSparseRep ], 0,
5018               f -> Maximum( List( f!.coeffs, c -> AbsInt(c[4]) ) ) );
5019
5020#############################################################################
5021##
5022#M  LargestSourcesOfAffineMappings( <f> )  for rcwa mappings in standard rep.
5023##
5024InstallMethod( LargestSourcesOfAffineMappings,
5025               "for rcwa mappings in standard rep. (RCWA)",
5026               true, [ IsRcwaMappingInStandardRep ], 0,
5027
5028  function ( f )
5029
5030    local  R, m, c, r;
5031
5032    R := Source(f);
5033    m := Modulus(f);
5034    c := Coefficients(f);
5035    r := AllResidues(R,m);
5036
5037    return Set(EquivalenceClasses([1..NumberOfResidues(R,m)],i->c[i]),
5038               cl->ResidueClassUnion(R,m,r{cl}));
5039  end );
5040
5041#############################################################################
5042##
5043#M  LargestSourcesOfAffineMappings( <f> ) .  for rcwa mappings in sparse rep.
5044##
5045InstallMethod( LargestSourcesOfAffineMappings,
5046               "for rcwa mappings of Z in sparse rep. (RCWA)",
5047               true, [ IsRcwaMappingOfZInSparseRep ], 0,
5048               f -> Set(EquivalenceClasses(f!.coeffs,c->c{[3..5]}),
5049                        cl->ResidueClassUnion(Integers,
5050                                              List(cl,c->c{[1,2]}))) );
5051
5052#############################################################################
5053##
5054#M  FixedPointsOfAffinePartialMappings( <f> ) for rcwa mapping of Z or Z_(pi)
5055##
5056InstallMethod( FixedPointsOfAffinePartialMappings,
5057               "for rcwa mappings of Z or Z_(pi) (RCWA)",
5058               true, [ IsRcwaMappingOfZOrZ_pi ], 0,
5059
5060  function ( f )
5061
5062    local  m, c, fixedpoints, r;
5063
5064    if not IsRcwaMappingStandardRep(f) then f := StandardRep(f); fi;
5065
5066    m := Modulus(f); c := Coefficients(f);
5067    fixedpoints := [];
5068    for r in [1..m] do
5069      if   c[r][1] = c[r][3]
5070      then if c[r][2] = 0 then fixedpoints[r] := Rationals;
5071                          else fixedpoints[r] := []; fi;
5072      else fixedpoints[r] := [ c[r][2]/(c[r][3]-c[r][1]) ]; fi;
5073    od;
5074
5075    return fixedpoints;
5076  end );
5077
5078#############################################################################
5079##
5080#M  ImageDensity( <f> ) . . . . . . . . .  for rcwa mappings in standard rep.
5081##
5082InstallMethod( ImageDensity, "for rcwa mappings in standard rep. (RCWA)",
5083               true, [ IsRcwaMappingInStandardRep ], 0,
5084
5085  function ( f )
5086
5087    local  R, c, m;
5088
5089    R := Source(f); c := Coefficients(f);
5090    m := NumberOfResidues(R,Modulus(f));
5091    if   IsRing(R) then
5092      return Sum([1..m],r->NumberOfResidues(R,c[r][3])/
5093                           NumberOfResidues(R,c[r][1]))/m;
5094    elif IsZxZ(R) then
5095      return Sum([1..m],r->c[r][3]^2/NumberOfResidues(R,c[r][1]))/m;
5096    else TryNextMethod(); fi;
5097  end );
5098
5099#############################################################################
5100##
5101#M  ImageDensity( <f> ) . . . . . . . . . . . for rcwa mappings in sparse rep.
5102##
5103InstallMethod( ImageDensity, "for rcwa mappings of Z in sparse rep. (RCWA)",
5104               true, [ IsRcwaMappingOfZInSparseRep ], 0,
5105               f -> Sum(List(f!.coeffs,c->c[5]/(c[2]*c[3]))) );
5106
5107#############################################################################
5108##
5109#M  DensityOfSetOfFixedPoints( <f> )  for rcwa mappings of Z in standard rep.
5110##
5111InstallMethod( DensityOfSetOfFixedPoints,
5112               "for rcwa mappings in standard rep. (RCWA)",
5113               true, [ IsRcwaMappingOfZInStandardRep ], 0,
5114               f->Number(Coefficients(f),c->c=[1,0,1])/Mod(f) );
5115
5116#############################################################################
5117##
5118#M  DensityOfSetOfFixedPoints( <f> )  . for rcwa mappings of Z in sparse rep.
5119##
5120InstallMethod( DensityOfSetOfFixedPoints,
5121               "for rcwa mappings in sparse rep. (RCWA)",
5122               true, [ IsRcwaMappingOfZInSparseRep ], 0,
5123               f->Sum(List(Filtered(Coefficients(f),c->c{[3..5]}=[1,0,1]),
5124                  c->1/c[2])) );
5125
5126#############################################################################
5127##
5128#M  DensityOfSupport( <f> ) . . . . . . . . . . . . .  for rcwa mappings of Z
5129##
5130InstallMethod( DensityOfSupport, "for rcwa mappings (RCWA)",
5131               true, [ IsRcwaMappingOfZ ], 0,
5132               f -> 1 - DensityOfSetOfFixedPoints( f ) );
5133
5134#############################################################################
5135##
5136#M  Multpk( <f>, <p>, <k> ) . . . . . for rcwa mappings of Z in standard rep.
5137##
5138InstallMethod( Multpk, "for rcwa mappings of Z in standard rep. (RCWA)",
5139               true, [ IsRcwaMappingOfZInStandardRep, IsInt, IsInt ], 0,
5140
5141  function ( f, p, k )
5142
5143    local  m, c, res;
5144
5145    m := Modulus(f); c := Coefficients(f);
5146    res := Filtered([0..m-1],r->PadicValuation(c[r+1][1]/c[r+1][3],p)=k);
5147    return ResidueClassUnion(Integers,m,res);
5148  end );
5149
5150#############################################################################
5151##
5152#M  Multpk( <f>, <p>, <k> ) . . . . . . for rcwa mappings of Z in sparse rep.
5153##
5154InstallMethod( Multpk, "for rcwa mappings of Z in sparse rep. (RCWA)",
5155               true, [ IsRcwaMappingOfZInSparseRep, IsInt, IsInt ], 0,
5156
5157  function ( f, p, k )
5158    return ResidueClassUnion(Integers,
5159                             List(Filtered(f!.coeffs,
5160                                           c->PadicValuation(c[3]/c[5],p)=k),
5161                                  c->c{[1,2]}));
5162  end );
5163
5164#############################################################################
5165##
5166#M  Multpk( <f>, <p>, <k> ) . . . . . . . . . . . .  for rcwa mappings of Z^2
5167##
5168InstallMethod( Multpk, "for rcwa mappings of Z^2 (RCWA)", true,
5169               [ IsRcwaMappingOfZxZ, IsInt, IsInt ], 0,
5170
5171  function ( f, p, k )
5172
5173    local  R, m, c, r;
5174
5175    R := Source(f); m := Modulus(f); c := Coefficients(f);
5176    r := Filtered([1..NumberOfResidues(R,m)],
5177                  i->PadicValuation(DeterminantMat(c[i][1])/c[i][3]^2,p)=k);
5178    return ResidueClassUnion(R,m,AllResidues(R,m){r});
5179  end );
5180
5181#############################################################################
5182##
5183#M  MultDivType( <f> ) . . . . . . . . for rcwa mappings of Z in standard rep.
5184##
5185InstallMethod( MultDivType,
5186               "for rcwa mappings of Z in standard rep. (RCWA)",
5187               true, [ IsRcwaMappingOfZInStandardRep ], 0,
5188               f->List(Collected(List(Coefficients(f),c->c[1]/c[3])),
5189                       t->[t[1],t[2]/Mod(f)]) );
5190
5191#############################################################################
5192##
5193#M  MultDivType( <f> ) . . . . . . . .  for rcwa mappings of Z in sparse rep.
5194##
5195InstallMethod( MultDivType,
5196               "for rcwa mappings of Z in sparse rep. (RCWA)",
5197               true, [ IsRcwaMappingOfZInSparseRep ], 0,
5198               f->List(EquivalenceClasses(f!.coeffs,c->c[3]/c[5]),
5199                       cl->[cl[1][3]/cl[1][5],Sum(List(cl,c->1/c[2]))]) );
5200
5201#############################################################################
5202##
5203#M  MappedPartitions( <g> ) . . . . . . .  for rcwa mappings in standard rep.
5204##
5205InstallMethod( MappedPartitions, "for rcwa mappings in standard rep. (RCWA)",
5206               true, [ IsRcwaMappingInStandardRep ], 0,
5207
5208  function ( g )
5209
5210    local  P;
5211
5212    P := AllResidueClassesModulo( Source(g), Mod(g) );
5213    return [ List(P,Density), List(P^g,Density) ];
5214  end );
5215
5216#############################################################################
5217##
5218#M  MappedPartitions( <g> ) . . . . . . for rcwa mappings of Z in sparse rep.
5219##
5220InstallMethod( MappedPartitions,
5221               "for rcwa mappings of Z in sparse rep. (RCWA)",
5222               true, [ IsRcwaMappingOfZInSparseRep ], 0,
5223               g -> [List(g!.coeffs,c->1/c[2]),
5224                     List(g!.coeffs,c->c[5]/(c[2]*c[3]))] );
5225
5226#############################################################################
5227##
5228#M  HashValueOfRcwaMapping( <f>, <m> ) . . . . . . . . for rcwa mappings of Z
5229##
5230InstallMethod( HashValueOfRcwaMapping,
5231               "for rcwa mappings of Z (RCWA)", ReturnTrue,
5232               [ IsRcwaMappingOfZ, IsPosInt ], 0,
5233
5234  function ( f, m )
5235
5236    local  c, n, max, i;
5237
5238    if not IsRcwaMappingOfZInSparseRep(f)
5239      and not IsRcwaMappingOfZInStandardRep(f)
5240    then TryNextMethod(); fi;
5241    c := Coefficients(f); max := m^3; n := 1;
5242    for i in [1..Length(c)] do
5243      n := (n*c[i][1]+c[i][2])*c[i][3]+c[i][1];
5244      if Length(c[i]) = 5 then n := (n+c[i][4])*c[i][5]+c[i][3]; fi;
5245      if AbsInt(n) > max then break; fi;
5246    od;
5247    return n mod m + 1;
5248  end );
5249
5250#############################################################################
5251##
5252#S  The support of an rcwa mapping. /////////////////////////////////////////
5253##
5254#############################################################################
5255
5256#############################################################################
5257##
5258#M  MovedPoints( <f> ) . . . . . . . . . . for rcwa mappings in standard rep.
5259##
5260##  The set of moved points (support) of the rcwa mapping <f>.
5261##
5262InstallMethod( MovedPoints,
5263               "for rcwa mappings in standard rep. (RCWA)",
5264               true, [ IsRcwaMappingInStandardRep ], 0,
5265
5266  function ( f )
5267
5268    local  R, m, c, residues, indices,
5269           fixedpoint, fixedpoints, fixedline, fixedlines,
5270           A, b, d, mat, i, quiet;
5271
5272    R := Source(f); m := Modulus(f); c := Coefficients(f);
5273    residues := AllResidues(R,m);
5274    if   IsRcwaMappingOfZOrZ_pi(f)
5275    then indices := Filtered([1..Length(residues)],i->c[i]<>[1,0,1]);
5276    elif IsRcwaMappingOfZxZ(f)
5277    then indices := Filtered([1..Length(residues)],
5278                             i->c[i]<>[[[1,0],[0,1]],[0,0],1]);
5279    else indices := Filtered([1..Length(residues)],i->c[i]<>[1,0,1]*One(R));
5280    fi;
5281    quiet := ValueOption("BeQuiet") = true;
5282    fixedpoints := []; fixedlines := [];
5283    if IsRing(R) then
5284      for i in indices do
5285        if c[i]{[1,3]} <> [1,1] * One(R) then
5286          fixedpoint := c[i][2]/(c[i][3]-c[i][1]);
5287          if   fixedpoint in R and fixedpoint mod m = residues[i]
5288          then Add(fixedpoints,fixedpoint); fi;
5289        fi;
5290      od;
5291    elif IsZxZ(R) then
5292      for i in indices do
5293        if c[i]{[1,3]} <> [[[1,0],[0,1]],1] then
5294          A := c[i][1]; b := c[i][2]; d := c[i][3];
5295          mat := A - [[d,0],[0,d]];
5296          if DeterminantMat(mat) <> 0 then
5297            fixedpoint := -b/mat;
5298            if   fixedpoint in R and fixedpoint mod m = residues[i]
5299            then Add(fixedpoints,fixedpoint); fi;
5300          else
5301            fixedline := SolutionNullspaceIntMat(mat,-b); # (v,w): v+k*w
5302            if fixedline[1] <> fail then Add(fixedlines,fixedline); fi;
5303          fi;
5304        fi;
5305      od;
5306    else TryNextMethod(); fi;
5307    if fixedlines <> [] and not quiet then
5308      fixedlines := Set(fixedlines);
5309      Info(InfoWarning,1,"MovedPoints: Sorry -- Lines are not yet ",
5310           "implemented;\nthere are the following fixed lines ",
5311           "(as pairs (v,w): l = v+k*w):\n",fixedlines);
5312    fi;
5313    return ResidueClassUnion(R,m,residues{indices},[],fixedpoints);
5314  end );
5315
5316#############################################################################
5317##
5318#M  MovedPoints( <f> ) . . . . . . . .  for rcwa mappings of Z in sparse rep.
5319##
5320InstallMethod( MovedPoints,
5321               "for rcwa mappings of Z in sparse rep. (RCWA)",
5322               true, [ IsRcwaMappingOfZInSparseRep ], 0,
5323
5324  function ( f )
5325
5326    local  S, classes, fixedpoints, fixedpoint, c;
5327
5328    classes := []; fixedpoints := [];
5329    for c in f!.coeffs do
5330      if c{[3..5]} <> [1,0,1] then
5331        Add(classes,c{[1,2]});
5332        if c{[3,5]} <> [1,1] then
5333          fixedpoint := c[4]/(c[5]-c[3]);
5334          if   IsInt(fixedpoint) and fixedpoint mod c[2] = c[1]
5335          then Add(fixedpoints,fixedpoint); fi;
5336        fi;
5337      fi;
5338    od;
5339    S := ResidueClassUnion(Integers,classes,[],fixedpoints);
5340    return S;
5341  end );
5342
5343#############################################################################
5344##
5345#M  NrMovedPoints( <obj> ) . . . . . . . . . . . . . . . . . . default method
5346##
5347InstallOtherMethod( NrMovedPoints, "default method (RCWA)", true,
5348                    [ IsObject ], 0, obj -> Size( MovedPoints( obj ) ) );
5349
5350#############################################################################
5351##
5352#M  Support( <g> ) . . . . . . . . . . . . . . . . . . . .  for rcwa mappings
5353##
5354InstallMethod( Support, "for rcwa mappings (RCWA)", true,
5355               [ IsRcwaMapping ], 0, MovedPoints );
5356
5357#############################################################################
5358##
5359#S  Restricting an rcwa mapping to a residue class union. ///////////////////
5360##
5361#############################################################################
5362
5363#############################################################################
5364##
5365#M  RestrictedMapping( <f>, <S> ) for an rcwa mapping and a res.- class union
5366##
5367InstallMethod( RestrictedMapping,
5368               "for an rcwa mapping and a residue class union (RCWA)",
5369               ReturnTrue, [ IsRcwaMapping, IsResidueClassUnion ], 0,
5370
5371  function ( f, S )
5372
5373    local  R, mf, mS, m, resf, resS, resm, cf, cfS, fS, r, pos, idcoeff;
5374
5375    if not IsRcwaMappingStandardRep(f) then f := StandardRep(f); fi;
5376
5377    R := Source(f);
5378    if UnderlyingRing(FamilyObj(S)) <> R
5379      or IncludedElements(S) <> [] or ExcludedElements(S) <> []
5380      or not IsSubset(S,S^f)
5381    then TryNextMethod(); fi;
5382    mf := Modulus(f); mS := Modulus(S); m := Lcm(mf,mS);
5383    resf := AllResidues(R,mf); resS := Residues(S); resm := AllResidues(R,m);
5384    if   IsRing(R) then idcoeff := [1,0,1]*One(R);
5385    elif IsZxZ(R)  then idcoeff := [[[1,0],[0,1]],[0,0],1];
5386    else TryNextMethod(); fi;
5387    cf := Coefficients(f);
5388    cfS := ListWithIdenticalEntries(Length(resm),idcoeff);
5389    for pos in [1..Length(resm)] do
5390      r := resm[pos];
5391      if r mod mS in resS then
5392        cfS[pos] := cf[Position(resf,r mod mf)];
5393      fi;
5394    od;
5395    fS := RcwaMapping(R,m,cfS);
5396    return fS;
5397  end );
5398
5399#############################################################################
5400##
5401#M  RestrictedMapping( <f>, <R> ) . . for an rcwa mapping and its full source
5402##
5403InstallMethod( RestrictedMapping,
5404               "for an rcwa mapping and its full source (RCWA)",
5405               ReturnTrue, [ IsRcwaMapping, IsDomain ], 0,
5406
5407  function ( f, R )
5408    if R = Source(f) then return f; else TryNextMethod(); fi;
5409  end );
5410
5411#############################################################################
5412##
5413#M  RestrictedPerm( <g>, <S> ) . . . . . .  for an rcwa permutation and a set
5414##
5415InstallMethod( RestrictedPerm,
5416               "for an rcwa permutation and a set (RCWA)",
5417               ReturnTrue, [ IsRcwaMapping, IsListOrCollection ], 0,
5418               RestrictedMapping );
5419
5420#############################################################################
5421##
5422#S  Computing images under rcwa mappings. ///////////////////////////////////
5423##
5424#############################################################################
5425
5426#############################################################################
5427##
5428#M  ImageElm( <f>, <n> ) . . . . . .  for an rcwa mapping of Z and an integer
5429##
5430##  Returns the image of the integer <n> under the rcwa mapping <f>.
5431##
5432InstallMethod( ImageElm,
5433               "for rcwa mapping of Z in standard rep. and integer (RCWA)",
5434               true, [ IsRcwaMappingOfZInStandardRep, IsInt ], 0,
5435
5436  function ( f, n )
5437
5438    local  m, c;
5439
5440    m := f!.modulus; c := f!.coeffs[n mod m + 1];
5441    return (c[1] * n + c[2]) / c[3];
5442  end );
5443
5444#############################################################################
5445##
5446#M  ImageElm( <f>, <n> ) . . . . . .  for an rcwa mapping of Z and an integer
5447##
5448InstallMethod( ImageElm,
5449               "for rcwa mapping of Z in sparse rep. and integer (RCWA)",
5450               true, [ IsRcwaMappingOfZInSparseRep, IsInt ], 0,
5451
5452  function ( f, n )
5453
5454    local  m, c;
5455
5456    m := f!.modulus;
5457    c := First(f!.coeffs,c->n mod c[2] = c[1]);
5458    return (c[3] * n + c[4]) / c[5];
5459  end );
5460
5461#############################################################################
5462##
5463#M  ImageElm( <f>, <v> ) . . . .  for an rcwa mapping of Z^2 and a row vector
5464##
5465##  Returns the image of the vector <v> in Z^2 under the rcwa mapping <f>.
5466##
5467InstallMethod( ImageElm,
5468               "for an rcwa mapping of Z^2 and a row vector (RCWA)", true,
5469               [ IsRcwaMappingOfZxZInStandardRep, IsRowVector ], 10,
5470
5471  function ( f, v )
5472
5473    local  R, m, c;
5474
5475    R := Source(f); if not v in R then TryNextMethod(); fi;
5476    m := f!.modulus;
5477    c := f!.coeffs[PositionSorted(AllResidues(R,m),v mod m)];
5478    return (v * c[1] + c[2]) / c[3];
5479  end );
5480
5481#############################################################################
5482##
5483#M  ImageElm( <f>, <n> ) . for an rcwa mapping of Z_(pi) and an el. of Z_(pi)
5484##
5485##  Returns the image of the element <n> of the ring Z_(pi) for suitable <pi>
5486##  under the rcwa mapping <f>.
5487##
5488InstallMethod( ImageElm,
5489               "for rcwa mapping of Z_(pi) and element of Z_(pi) (RCWA)",
5490               true, [ IsRcwaMappingOfZ_piInStandardRep, IsRat ], 0,
5491
5492  function ( f, n )
5493
5494    local  m, c;
5495
5496    if not n in Source(f) then TryNextMethod(); fi;
5497    m := f!.modulus; c := f!.coeffs[n mod m + 1];
5498    return (c[1] * n + c[2]) / c[3];
5499  end );
5500
5501#############################################################################
5502##
5503#M  ImageElm( <f>, <p> ) for rcwa mapping of GF(q)[x] and element of GF(q)[x]
5504##
5505##  Returns the image of the polynomial <p> under the rcwa mapping <f>.
5506##
5507InstallMethod( ImageElm,
5508               "for rcwa mapping of GF(q)[x] and element of GF(q)[x] (RCWA)",
5509               true, [ IsRcwaMappingOfGFqxInStandardRep, IsPolynomial ], 0,
5510
5511  function ( f, p )
5512
5513    local  R, m, c, r;
5514
5515    R := Source(f); if not p in R then TryNextMethod(); fi;
5516    m := f!.modulus; r := p mod m;
5517    c := f!.coeffs[PositionSorted(AllResidues(R,m),r)];
5518    return (c[1] * p + c[2]) / c[3];
5519  end );
5520
5521#############################################################################
5522##
5523#M  \^( <n>, <f> ) . . . . . . . . . . for a ring element and an rcwa mapping
5524##
5525##  Returns the image of the ring element <n> under the rcwa mapping <f>.
5526##
5527InstallMethod( \^, "for a ring element and an rcwa mapping (RCWA)",
5528               ReturnTrue, [ IsRingElement, IsRcwaMapping ], 0,
5529               function ( n, f ) return ImageElm( f, n ); end );
5530InstallMethod( \^, "for a row vector and an rcwa mapping of Z^2 (RCWA)",
5531               ReturnTrue, [ IsRowVector, IsRcwaMappingOfZxZ ], 0,
5532               function ( v, f ) return ImageElm( f, v ); end );
5533InstallMethod( \^, "for list of row vectors and rcwa mapping of Z^2 (RCWA)",
5534               ReturnTrue, [ IsList, IsRcwaMappingOfZxZ ], 10,
5535
5536  function ( l, f )
5537    if not IsSubset( Source( f ), l ) then TryNextMethod( ); fi;
5538    return List( l, v -> ImageElm( f, v ) );
5539  end );
5540
5541#############################################################################
5542##
5543#M  ImagesElm( <f>, <n> ) . . . . . .  for an rcwa mapping and a ring element
5544##
5545##  Returns the images of the ring element <n> under the rcwa mapping <f>.
5546##  For technical purposes, only.
5547##
5548InstallMethod( ImagesElm, "for an rcwa mapping and a ring element (RCWA)",
5549               true, [ IsRcwaMapping, IsRingElement ], 0,
5550               function ( f, n ) return [ ImageElm( f, n ) ]; end );
5551InstallMethod( ImagesElm, "for rcwa mapping of Z^2 and row vector (RCWA)",
5552               true, [ IsRcwaMappingOfZxZ, IsRowVector ], 0,
5553               function ( f, n ) return [ ImageElm( f, n ) ]; end );
5554
5555#############################################################################
5556##
5557#M  ImagesSet( <f>, <S> ) . . . for an rcwa mapping and a residue class union
5558##
5559##  Returns the image of the set <S> under the rcwa mapping <f>.
5560##
5561##  The method rank offset SUM_FLAGS is to ensure that this method also gets
5562##  called for rcwa zero mappings, instead of a Library method which returns
5563##  the zero module.
5564##
5565InstallMethod( ImagesSet,
5566               "for an rcwa mapping and a residue class union (RCWA)",
5567               ReturnTrue, [ IsRcwaMappingInStandardRep,
5568                             IsCollection ], SUM_FLAGS,
5569
5570  function ( f, S )
5571
5572    local  R, c, m, cls, i;
5573
5574    R := Source(f); if not IsSubset(R,S) then TryNextMethod(); fi;
5575    if IsList(S) then return Set(List(S,n->n^f)); fi;
5576    c := Coefficients(f); m := Modulus(f);
5577    cls := AllResidueClassesModulo(R,m);
5578    return Union(List([1..Length(cls)],
5579                      i->(Intersection(S,cls[i])*c[i][1]+c[i][2])/c[i][3]));
5580  end );
5581
5582#############################################################################
5583##
5584#M  ImagesSet( <f>, <S> )  for an rcwa mapping of Z and a residue class union
5585##
5586##  Returns the image of the set <S> under the rcwa mapping <f> of Z which is
5587##  assumed to be in "sparse" representation.
5588##
5589##  For the reason of the method rank offset SUM_FLAGS, see above.
5590##
5591InstallMethod( ImagesSet,
5592               "for an rcwa mapping of Z and a residue class union (RCWA)",
5593               ReturnTrue, [ IsRcwaMappingOfZInSparseRep,
5594                             IsCollection ], SUM_FLAGS,
5595
5596  function ( f, S )
5597    if not IsSubset(Integers,S) then TryNextMethod(); fi;
5598    if IsList(S) then return Set(List(S,n->n^f)); fi;
5599    return Union(List(f!.coeffs,
5600             c->(Intersection(S,ResidueClass(c[1],c[2]))*c[3]+c[4])/c[5]));
5601  end );
5602
5603#############################################################################
5604##
5605#M  ImagesSet( <f>, <cl> ) . . . for an rcwa mapping of Z and a residue class
5606##
5607##  Returns the image of the residue class <cl> under the rcwa mapping <f>.
5608##  It is required that <f> is injective and in standard representation, and
5609##  that the modulus of <f> divides the modulus of <cl>.
5610##
5611##  The rank offset SUM_FLAGS is needed to override the default method.
5612##
5613InstallMethod( ImagesSet,
5614               "for an rcwa mapping of Z and a residue class (RCWA)",
5615               ReturnTrue, [ IsRcwaMappingOfZInStandardRep and IsInjective,
5616                             IsResidueClassUnionOfZ and IsResidueClass ],
5617               SUM_FLAGS,
5618
5619  function ( f, cl )
5620
5621    local  c;
5622
5623    if IsResidueClassUnionOfZInClassListRep(cl) then
5624      if cl!.m mod f!.modulus <> 0 then TryNextMethod(); fi;
5625      c := f!.coeffs[(cl!.cls[1][1]) mod f!.modulus + 1];
5626      return ResidueClass(Integers,c[1]*cl!.m/c[3],
5627                                  (c[1]*cl!.cls[1][1]+c[2])/c[3]);
5628    elif IsResidueClassUnionInResidueListRep(cl) then
5629      if cl!.m mod f!.modulus <> 0 then TryNextMethod(); fi;
5630      c := f!.coeffs[(cl!.r[1]) mod f!.modulus + 1];
5631      return ResidueClass(Integers,c[1]*cl!.m/c[3],
5632                                  (c[1]*cl!.r[1]+c[2])/c[3]);
5633    else TryNextMethod(); fi;
5634  end );
5635
5636#############################################################################
5637##
5638#M  ImagesSet( <f>, <cl> ) . . . for an rcwa mapping of Z and a residue class
5639##
5640##  Returns the image of the residue class <cl> under the rcwa mapping <f>
5641##  of Z. It is required that <f> is injective and in sparse representation,
5642##  and that <cl> lies in the source of one affine partial mapping of <f>.
5643##
5644##  The rank offset SUM_FLAGS is needed to override the default method.
5645##
5646InstallMethod( ImagesSet,
5647               "for an rcwa mapping of Z and a residue class (RCWA)",
5648               ReturnTrue, [ IsRcwaMappingOfZInSparseRep and IsInjective,
5649                             IsResidueClassUnionOfZ and IsResidueClass ],
5650               SUM_FLAGS,
5651
5652  function ( f, cl )
5653
5654    local  c;
5655
5656    if IsResidueClassUnionOfZInClassListRep(cl) then
5657      c := First(f!.coeffs,
5658                    c->cl!.cls[1][1] mod c[2] = c[1] and cl!.m mod c[2] = 0);
5659      if c = fail then TryNextMethod(); fi;
5660      return ResidueClass(Integers,c[3]*cl!.m/c[5],
5661                                  (c[3]*cl!.cls[1][1]+c[4])/c[5]);
5662    elif IsResidueClassUnionInResidueListRep(cl) then
5663      c := First(f!.coeffs,
5664                    c->cl!.r[1] mod c[2] = c[1] and cl!.m mod c[2] = 0);
5665      if c = fail then TryNextMethod(); fi;
5666      return ResidueClass(Integers,c[3]*cl!.m/c[5],
5667                                  (c[3]*cl!.r[1]+c[4])/c[5]);
5668    else TryNextMethod(); fi;
5669  end );
5670
5671#############################################################################
5672##
5673#M  ImagesSource( <f> ) . . . . . . . . . . . . . . . . . for an rcwa mapping
5674##
5675##  Returns the image of the rcwa mapping <f>.
5676##
5677InstallMethod( ImagesSource,
5678               "for an rcwa mapping and a residue class union (RCWA)",
5679               true, [ IsRcwaMapping ], 2 * SUM_FLAGS,
5680               f -> ImagesSet( f, Source( f ) ) );
5681
5682#############################################################################
5683##
5684#M  \^( <S>, <f> ) . . . . . for a set or class partition and an rcwa mapping
5685##
5686##  Returns the image of the set or class partition <S> under the
5687##  rcwa mapping <f>.
5688##
5689##  The argument <S> can be:
5690##
5691##  - A finite set of elements of the source of <f>.
5692##  - A residue class union of the source of <f>.
5693##  - A partition of the source of <f> into (unions of) residue classes.
5694##    In this case the <i>th element of the result is the image of <S>[<i>].
5695##
5696InstallMethod( \^,
5697               "for a set / class partition and an rcwa mapping (RCWA)",
5698               ReturnTrue, [ IsListOrCollection, IsRcwaMapping ], 20,
5699
5700  function ( S, f )
5701    if   S in Source(f)
5702    then return ImageElm(f,S);
5703    elif IsSubset(Source(f),S)
5704    then return ImagesSet(f,S);
5705    elif IsList(S) and ForAll(S,set->IsSubset(Source(f),set))
5706    then return List(S,set->set^f);
5707    else TryNextMethod(); fi;
5708  end );
5709
5710#############################################################################
5711##
5712#M  \^( <U>, <f> ) .  for union of res.-cl. with fixed rep's and rcwa mapping
5713##
5714##  Returns the image of the union <U> of residue classes of Z with fixed
5715##  representatives under the rcwa mapping <f>.
5716##
5717InstallMethod( \^,
5718               Concatenation("for a union of residue classes with fixed ",
5719                             "rep's and an rcwa mapping (RCWA)"), ReturnTrue,
5720               [ IsUnionOfResidueClassesOfZWithFixedRepresentatives,
5721                 IsRcwaMappingOfZ ], 0,
5722
5723  function ( U, f )
5724
5725    local  cls, abc, m, c, k, l;
5726
5727    if not IsRcwaMappingStandardRep(f) then f := StandardRep(f); fi;
5728    m := Modulus(f); c := Coefficients(f);
5729    k := List(Classes(U),cl->m/Gcd(m,cl[1])); l := Length(k);
5730    cls := AsListOfClasses(U);
5731    cls := List([1..l],i->RepresentativeStabilizingRefinement(cls[i],k[i]));
5732    cls := Flat(List(cls,cl->AsListOfClasses(cl)));
5733    abc := List(cls,cl->c[1 + Classes(cl)[1][2] mod m]);
5734    cls := List([1..Length(cls)],i->(abc[i][1]*cls[i]+abc[i][2])/abc[i][3]);
5735    return RepresentativeStabilizingRefinement(Union(cls),0);
5736  end );
5737
5738#############################################################################
5739##
5740#S  Computing preimages under rcwa mappings. ////////////////////////////////
5741##
5742#############################################################################
5743
5744#############################################################################
5745##
5746#M  PreImageElm( <f>, <n> ) . for a bijective rcwa mapping and a ring element
5747##
5748##  Returns the preimage of the ring element <n> under the bijective
5749##  rcwa mapping <f>.
5750##
5751InstallMethod( PreImageElm,
5752               "for a bijective rcwa mapping and a ring element (RCWA)",
5753               true, [ IsRcwaMapping and IsBijective, IsRingElement ], 0,
5754
5755  function ( f, n )
5756    return n^Inverse( f );
5757  end );
5758
5759#############################################################################
5760##
5761#M  PreImagesElm( <f>, <n> ) . . . . . for an rcwa mapping and a ring element
5762##
5763##  Returns the preimages of <n> under the rcwa mapping <f>.
5764##
5765InstallMethod( PreImagesElm,
5766               "for an rcwa mapping and a ring element (RCWA)", ReturnTrue,
5767               [ IsRcwaMappingInStandardRep, IsRingElement ], 0,
5768
5769  function ( f, n )
5770
5771    local  R, c, m, preimage, singletons, residues, n1, pre;
5772
5773    R := Source(f); if not n in R then TryNextMethod(); fi;
5774    c := f!.coeffs; m := f!.modulus;
5775    preimage := []; singletons := [];
5776    residues := AllResidues(R,m);
5777    for n1 in [1..Length(residues)] do
5778      if not IsZero(c[n1][1]) then
5779        pre := (c[n1][3] * n - c[n1][2])/c[n1][1];
5780        if   pre in R and pre mod m = residues[n1]
5781        then Add(singletons,pre); fi;
5782      else
5783        if c[n1][2] = n then
5784          if   IsOne(m) then return R;
5785          else preimage := Union(preimage,ResidueClass(R,m,residues[n1])); fi;
5786        fi;
5787      fi;
5788    od;
5789    preimage := Union(preimage,singletons);
5790    return preimage;
5791  end );
5792
5793#############################################################################
5794##
5795#M  PreImagesElm( <f>, <n> ) . . . .  for an rcwa mapping of Z and an integer
5796##
5797InstallMethod( PreImagesElm,
5798               "for an rcwa mapping of Z and an integer (RCWA)",
5799               ReturnTrue, [ IsRcwaMappingOfZInSparseRep, IsInt ], 0,
5800
5801  function ( f, n )
5802
5803    local  coeffs, preimage, singletons, c, pre;
5804
5805    coeffs := f!.coeffs;
5806    preimage := []; singletons := [];
5807    for c in coeffs do
5808      if c[3] <> 0 then
5809        pre := (c[5] * n - c[4])/c[3];
5810        if   IsInt(pre) and pre mod c[2] = c[1]
5811        then Add(singletons,pre); fi;
5812      else
5813        if c[4] = n then
5814          if   c[2] = 1 then return Integers;
5815          else preimage := Union(preimage,ResidueClass(c[1],c[2])); fi;
5816        fi;
5817      fi;
5818    od;
5819    preimage := Union(preimage,singletons);
5820    return preimage;
5821  end );
5822
5823#############################################################################
5824##
5825#M  PreImagesRepresentative( <f>, <n> ) . . for rcwa mapping and ring element
5826##
5827##  Returns a representative of the set of preimages of the integer <n> under
5828##  the rcwa mapping <f>.
5829##
5830InstallMethod( PreImagesRepresentative,
5831               "for an rcwa mapping and a ring element (RCWA)",
5832               ReturnTrue, [ IsRcwaMappingInStandardRep, IsRingElement ], 0,
5833
5834  function ( f, n )
5835
5836    local  R, c, m, residues, n1, pre;
5837
5838    R := Source(f); if not n in R then return fail; fi;
5839    c := f!.coeffs; m := f!.modulus;
5840    residues := AllResidues(R,m);
5841    for n1 in [1..Length(residues)] do
5842      if not IsZero(c[n1][1]) then
5843        pre := (n * c[n1][3] - c[n1][2])/c[n1][1];
5844        if pre in R and pre mod m = residues[n1] then return pre; fi;
5845      else
5846        if c[n1][2] = n then return residues[n1]; fi;
5847      fi;
5848    od;
5849    return fail;
5850  end );
5851
5852#############################################################################
5853##
5854#M  PreImagesRepresentative( <f>, <n> ) . . for rcwa mapping of Z and integer
5855##
5856InstallMethod( PreImagesRepresentative,
5857               "for an rcwa mapping of Z and an integer (RCWA)",
5858               ReturnTrue, [ IsRcwaMappingOfZInSparseRep, IsInt ], 0,
5859
5860  function ( f, n )
5861
5862    local  coeffs, c, pre;
5863
5864    coeffs := f!.coeffs;
5865    for c in coeffs do
5866      if c[3] <> 0 then
5867        pre := (n * c[5] - c[4])/c[3];
5868        if IsInt(pre) and pre mod c[2] = c[1] then return pre; fi;
5869      else
5870        if c[4] = n then return c[1]; fi;
5871      fi;
5872    od;
5873    return fail;
5874  end );
5875
5876#############################################################################
5877##
5878#M  PreImagesSet( <f>, <R> ) . .  for an rcwa mapping and its underlying ring
5879##
5880##  Returns the source of the rcwa mapping <f>.
5881##  For technical purposes, only.
5882##
5883InstallMethod( PreImagesSet,
5884               "for an rcwa mapping and its underlying ring (RCWA)", true,
5885               [ IsRcwaMapping, IsRing ], 0,
5886
5887  function ( f, R )
5888    if   R = UnderlyingRing( FamilyObj( f ) )
5889    then return R; else TryNextMethod( ); fi;
5890  end );
5891
5892#############################################################################
5893##
5894#M  PreImagesSet( <f>, <l> ) . . . . . . for an rcwa mapping and a finite set
5895##
5896##  Returns the preimage of the finite set <l> under the rcwa mapping <f>.
5897##
5898InstallMethod( PreImagesSet,
5899               "for an rcwa mapping and a finite set (RCWA)",
5900               true, [ IsRcwaMapping, IsList ], 0,
5901
5902  function ( f, l )
5903    return Union( List( Set( l ), n -> PreImagesElm( f, n ) ) );
5904  end );
5905
5906#############################################################################
5907##
5908#M  PreImagesSet( <f>, <S> ) .  for an rcwa mapping and a residue class union
5909##
5910##  Returns the preimage of the residue class union <S> under the
5911##  rcwa mapping <f>.
5912##
5913InstallMethod( PreImagesSet,
5914               "for an rcwa mapping and a residue class union (RCWA)",
5915               ReturnTrue, [ IsRcwaMapping, IsResidueClassUnion ], 0,
5916
5917  function ( f, S )
5918
5919    local  R, preimage, parts, premod, preres, rump,
5920           pre, pre2, im, diff, excluded, n;
5921
5922    R := Source(f); if not IsSubset(R,S) then TryNextMethod(); fi;
5923    rump := ResidueClassUnion( R, Modulus(S), Residues(S) );
5924    premod := Modulus(f) * Divisor(f) * Modulus(S);
5925    preres := Filtered( AllResidues( R, premod ), n -> n^f in rump );
5926    parts := [ ResidueClassUnion( R, premod, preres ) ];
5927    Append( parts, List( IncludedElements(S), n -> PreImagesElm( f, n ) ) );
5928    preimage := Union( parts );
5929    excluded := ExcludedElements(S);
5930    for n in excluded do
5931      pre  := PreImagesElm( f, n );
5932      im   := ImagesSet( f, pre );
5933      pre2 := PreImagesSet( f, Difference( im, excluded ) );
5934      diff := Difference( pre, pre2 );
5935      if   not IsEmpty( diff )
5936      then preimage := Difference( preimage, diff ); fi;
5937    od;
5938    return preimage;
5939  end );
5940
5941#############################################################################
5942##
5943#M  PreImagesSet( <f>, <S> ) .  for rcwa mapping of Z and residue class union
5944##
5945InstallMethod( PreImagesSet,
5946               "for an rcwa mapping of Z and a residue class union (RCWA)",
5947               ReturnTrue, [ IsRcwaMappingOfZInSparseRep,
5948                             IsResidueClassUnionOfZ ], 0,
5949
5950  function ( f, S )
5951
5952    local  coeffs, c, preimage, img, pre;
5953
5954    coeffs   := f!.coeffs;
5955    preimage := [];
5956
5957    for c in coeffs do
5958      if c[3] <> 0 then
5959        img := ResidueClass(Integers,c[3]*c[2]/c[5],(c[3]*c[1]+c[4])/c[5]);
5960        img := Intersection(img,S);
5961        pre := (c[5]*img-c[4])/c[3];
5962      else
5963        if c[4] in S then pre := ResidueClass(c[1],c[2]); else pre := []; fi;
5964      fi;
5965      preimage := Union(preimage,pre);
5966    od;
5967
5968    return preimage;
5969  end );
5970
5971#############################################################################
5972##
5973#M  PreImagesSet( <f>, <U> ) . . . . as above, but with fixed representatives
5974##
5975##  Returns the preimage of the union <U> of residue classes of Z with fixed
5976##  representatives under the rcwa mapping <f>.
5977##
5978InstallMethod( PreImagesSet,
5979               Concatenation("for an rcwa mapping of Z and a union of ",
5980                             "residue classes with fixed rep's (RCWA)"),
5981               ReturnTrue,
5982               [ IsRcwaMappingOfZ,
5983                 IsUnionOfResidueClassesOfZWithFixedRepresentatives ], 0,
5984
5985  function ( f, U )
5986
5987    local  preimage, cls, rep, m, minv, clm, k, l;
5988
5989    if not IsRcwaMappingStandardRep(f) then f := StandardRep(f); fi;
5990
5991    m := Modulus(f); minv := Multiplier(f) * m;
5992    k := List(Classes(U),cl->minv/Gcd(minv,cl[1])); l := Length(k);
5993    cls := AsListOfClasses(U);
5994    cls := List([1..l],i->RepresentativeStabilizingRefinement(cls[i],k[i]));
5995    cls := Flat(List(cls,cl->AsListOfClasses(cl)));
5996    rep := List(cls,cl->PreImagesElm(f,Classes(cl)[1][2]));
5997    cls := List(cls,cl->PreImagesSet(f,AsOrdinaryUnionOfResidueClasses(cl)));
5998    clm := AllResidueClassesModulo(Integers,m);
5999    cls := List(cls,cl1->List(clm,cl2->Intersection(cl1,cl2)));
6000    cls := List(cls,list->Filtered(list,cl->cl<>[]));
6001    cls := List([1..Length(cls)],
6002                i->List(cls[i],cl->[Modulus(cl),
6003                                    Intersection(rep[i],cl)[1]]));
6004    cls := Concatenation(cls);
6005    preimage := UnionOfResidueClassesWithFixedRepresentatives(Integers,cls);
6006
6007    return RepresentativeStabilizingRefinement(preimage,0);
6008  end );
6009
6010#############################################################################
6011##
6012#S  Testing an rcwa mapping for injectivity and surjectivity. ///////////////
6013##
6014#############################################################################
6015
6016#############################################################################
6017##
6018#M  IsInjective( <f> ) . . . . . . . . . . . for rcwa mappings of Z or Z_(pi)
6019##
6020InstallMethod( IsInjective,
6021               "for rcwa mappings of Z or Z_(pi) (RCWA)", true,
6022               [ IsRcwaMappingOfZOrZ_piInStandardRep ], 0,
6023
6024  function ( f )
6025
6026    local  c, cInv, m, mInv, n, t, tm, tn, Classes, cl;
6027
6028    if IsZero(Multiplier(f)) then return false; fi;
6029    if Product(PrimeSet(f)) > 30 then
6030      if Length(Set(List([-100..100],n->n^f))) < 201
6031      then return false; fi;
6032      if Length(Set(List([-1000..1000],n->n^f))) < 2001
6033      then return false; fi;
6034    fi;
6035    c := f!.coeffs; m := f!.modulus;
6036    cInv := [];
6037    mInv := Multiplier( f ) * m / Gcd( m, Gcd( List( c, t -> t[3] ) ) );
6038    for n in [ 1 .. m ] do
6039      t := [c[n][3], -c[n][2], c[n][1]]; if t[3] = 0 then return false; fi;
6040      tm := StandardAssociate(Source(f),c[n][1]) * m / Gcd(m,c[n][3]);
6041      tn := ((n - 1) * c[n][1] + c[n][2]) / c[n][3] mod tm;
6042      Classes := List([1 .. mInv/tm], i -> (i - 1) * tm + tn);
6043      for cl in Classes do
6044        if IsBound(cInv[cl + 1]) and cInv[cl + 1] <> t then return false; fi;
6045        cInv[cl + 1] := t;
6046      od;
6047    od;
6048    return true;
6049  end );
6050
6051#############################################################################
6052##
6053#M  IsInjective( <f> ) . . . . . . . .  for rcwa mappings of Z in sparse rep.
6054##
6055InstallMethod( IsInjective,
6056               "for rcwa mappings of Z in sparse rep. (RCWA)", true,
6057               [ IsRcwaMappingOfZInSparseRep ], 0,
6058
6059  function ( f )
6060
6061    local  coeffs, imgs, c;
6062
6063    if Multiplier(f) = 0 then return false; fi;
6064
6065    coeffs := f!.coeffs;
6066    imgs   := List(coeffs,c->[(c[3]*c[1]+c[4])/c[5],c[3]*c[2]/c[5]]);
6067    return ForAll(Combinations(imgs,2),
6068                  c->(c[1][1]-c[2][1]) mod Gcd(c[1][2],c[2][2]) <> 0);
6069  end );
6070
6071#############################################################################
6072##
6073#M  IsInjective( <f> ) . . . . . . . . . . . .  for rcwa mappings of GF(q)[x]
6074##
6075InstallMethod( IsInjective,
6076               "for rcwa mappings of GF(q)[x] (RCWA)", true,
6077               [ IsRcwaMappingOfGFqxInStandardRep ], 0,
6078
6079  function ( f )
6080
6081    local  c, cInv, m, mInv, d, dInv, R, q, x, respols, res, resInv, r, n,
6082           t, tm, tr, tn, Classes, cl, pos;
6083
6084    if IsZero(Multiplier(f)) then return false; fi;
6085    R := UnderlyingRing(FamilyObj(f));
6086    q := Size(CoefficientsRing(R));
6087    x := IndeterminatesOfPolynomialRing(R)[1];
6088    c := f!.coeffs; m := f!.modulus;
6089    cInv := [];
6090    mInv := StandardAssociate( R,
6091              Multiplier( f ) * m / Gcd( m, Gcd( List( c, t -> t[3] ) ) ) );
6092    if mInv = 0 then return false; fi;
6093    d := DegreeOfLaurentPolynomial(m);
6094    dInv := DegreeOfLaurentPolynomial(mInv);
6095    res := AllGFqPolynomialsModDegree(q,d,x);
6096    respols := List([0..dInv], d -> AllGFqPolynomialsModDegree(q,d,x));
6097    resInv := respols[dInv + 1];
6098    for n in [ 1 .. Length(res) ] do
6099      r := res[n];
6100      t := [c[n][3], -c[n][2], c[n][1]];
6101      if IsZero(t[3]) then return false; fi;
6102      tm := StandardAssociate(Source(f),c[n][1]) * m / Gcd(m,c[n][3]);
6103      tr := (r * c[n][1] + c[n][2]) / c[n][3] mod tm;
6104      Classes := List(respols[DegreeOfLaurentPolynomial(mInv/tm) + 1],
6105                      p -> p * tm + tr);
6106      for cl in Classes do
6107        pos := Position(resInv,cl);
6108        if IsBound(cInv[pos]) and cInv[pos] <> t then return false; fi;
6109        cInv[pos] := t;
6110      od;
6111    od;
6112    return true;
6113  end );
6114
6115#############################################################################
6116##
6117#M  IsInjective( <f> ) . . . . . . . . . . . . . . . for rcwa mappings of Z^2
6118##
6119InstallMethod( IsInjective,
6120               "for rcwa mappings of Z^2 (RCWA)", true,
6121               [ IsRcwaMappingOfZxZ ], 0,
6122
6123  function ( f )
6124
6125    local  R, c, m, det, imgs;
6126
6127    R := Source(f); c := Coefficients(f); m := Modulus(f);
6128
6129    if   DeterminantMat(Multiplier(f)) = 0 or ImageDensity(f) > 1
6130    then return false; fi;
6131
6132    det := DeterminantMat(m);
6133    if  Length(Cartesian([0..RootInt(det,2)-1],[0..RootInt(det,2)-1])^f)
6134      < RootInt(det,2)^2
6135    then return false; fi;
6136
6137    if ImageDensity(f) = 1 and IsSurjective(f) then return true; fi;
6138
6139    imgs := LargestSourcesOfAffineMappings(f)^f;
6140    return ForAll( Combinations(imgs,2), pair -> Intersection(pair) = [] );
6141
6142    return true;
6143  end );
6144
6145#############################################################################
6146##
6147#M  IsSurjective( <f> ) . . . . . . . . . .  for rcwa mappings of Z or Z_(pi)
6148##
6149InstallMethod( IsSurjective,
6150               "for rcwa mappings of Z or Z_(pi) (RCWA)", true,
6151               [ IsRcwaMappingOfZOrZ_piInStandardRep ], 0,
6152
6153  function ( f )
6154
6155    local  c, cInv, m, mInv, n, t, tm, tn, Classes, cl;
6156
6157    c := f!.coeffs; m := f!.modulus;
6158    cInv := [];
6159    if ForAll(c, t -> t[1] = 0) then return false; fi;
6160    mInv := AbsInt(Lcm(Filtered(List(c,t->StandardAssociate(Source(f),t[1])),
6161                                k -> k <> 0 )))
6162                 * m / Gcd(m,Gcd(List(c,t->t[3])));
6163    for n in [1 .. m] do
6164      t := [c[n][3], -c[n][2], c[n][1]];
6165      if t[3] <> 0 then
6166        tm := StandardAssociate(Source(f),c[n][1]) * m / Gcd(m,c[n][3]);
6167        tn := ((n - 1) * c[n][1] + c[n][2]) / c[n][3] mod tm;
6168        Classes := List([1 .. mInv/tm], i -> (i - 1) * tm + tn);
6169        for cl in Classes do cInv[cl + 1] := t; od;
6170      fi;
6171    od;
6172    return ForAll([1..mInv], i -> IsBound(cInv[i]));
6173  end );
6174
6175#############################################################################
6176##
6177#M  IsSurjective( <f> ) . . . . . . . . for rcwa mappings of Z in sparse rep.
6178##
6179InstallMethod( IsSurjective,
6180               "for rcwa mappings of Z in sparse rep. (RCWA)", true,
6181               [ IsRcwaMappingOfZInSparseRep ], 0,
6182
6183  function ( f )
6184
6185    local  coeffs, imgs;
6186
6187    coeffs := f!.coeffs;
6188    if IsInjective(f) then
6189      return Sum(List(coeffs,c->c[5]/(c[2]*AbsInt(c[3])))) = 1;
6190    else
6191      imgs := List(Filtered(coeffs,c->c[3]<>0),
6192                   c->ResidueClass((c[3]*c[1]+c[4])/c[5],(c[3]*c[2])/c[5]));
6193      return IsIntegers(Union(imgs));
6194    fi;
6195  end );
6196
6197#############################################################################
6198##
6199#M  IsSurjective( <f> ) . . . . . . . . . . . . for rcwa mappings of GF(q)[x]
6200##
6201InstallMethod( IsSurjective,
6202               "for rcwa mappings of GF(q)[x] (RCWA)", true,
6203               [ IsRcwaMappingOfGFqxInStandardRep ], 0,
6204
6205  function ( f )
6206
6207    local  c, cInv, m, mInv, d, dInv, R, q, x,
6208           respols, res, resInv, r, n, t, tm, tr, tn, Classes, cl, pos;
6209
6210    R := UnderlyingRing(FamilyObj(f));
6211    q := Size(CoefficientsRing(R));
6212    x := IndeterminatesOfPolynomialRing(R)[1];
6213    c := f!.coeffs; m := f!.modulus;
6214    cInv := [];
6215    if ForAll( c, t -> IsZero(t[1]) ) then return false; fi;
6216    mInv := Lcm(Filtered(List(c, t -> StandardAssociate(Source(f),t[1])),
6217                         k -> not IsZero(k) ))
6218          * m / Gcd(m,Gcd(List(c,t->t[3])));
6219    d := DegreeOfLaurentPolynomial(m);
6220    dInv := DegreeOfLaurentPolynomial(mInv);
6221    res := AllGFqPolynomialsModDegree(q,d,x);
6222    respols := List([0..dInv], d -> AllGFqPolynomialsModDegree(q,d,x));
6223    resInv := respols[dInv + 1];
6224    for n in [ 1 .. Length(res) ] do
6225      r := res[n];
6226      t := [c[n][3], -c[n][2], c[n][1]];
6227      if not IsZero(t[3]) then
6228        tm := StandardAssociate(Source(f),c[n][1]) * m / Gcd(m,c[n][3]);
6229        tr := (r * c[n][1] + c[n][2]) / c[n][3] mod tm;
6230        Classes := List(respols[DegreeOfLaurentPolynomial(mInv/tm) + 1],
6231                        p -> p * tm + tr);
6232        for cl in Classes do cInv[Position(resInv,cl)] := t; od;
6233      fi;
6234    od;
6235    return ForAll([1..Length(resInv)], i -> IsBound(cInv[i]));
6236  end );
6237
6238############################################################################
6239##
6240#F  InjectiveAsMappingFrom( <f> ) . . . .  some set on which <f> is injective
6241##
6242InstallGlobalFunction( InjectiveAsMappingFrom,
6243
6244  function ( f )
6245
6246    local  R, m, base, pre, im, cl, imcl, overlap;
6247
6248    if not IsRcwaMappingStandardRep(f) then f := StandardRep(f); fi;
6249    R := Source(f); if IsBijective(f) then return R; fi;
6250    m := Modulus(f); base := AllResidueClassesModulo(R,m);
6251    pre := R; im := [];
6252    for cl in base do
6253      imcl    := cl^f;
6254      overlap := Intersection(im,imcl);
6255      im      := Union(im,imcl);
6256      pre     := Difference(pre,Intersection(PreImagesSet(f,overlap),cl));
6257    od;
6258    return pre;
6259  end );
6260
6261#############################################################################
6262##
6263#M  IsUnit( <f> ) . . . . . . . . . . . . . . . . . . . . . for rcwa mappings
6264##
6265InstallOtherMethod( IsUnit,
6266                    "for rcwa mappings (RCWA)",
6267                    true, [ IsRcwaMapping ], 0, IsBijective );
6268
6269#############################################################################
6270##
6271#S  Computing pointwise sums of rcwa mappings. //////////////////////////////
6272##
6273#############################################################################
6274
6275#############################################################################
6276##
6277#M  \+( <f>, <g> ) . . . . . . . . . . . for two rcwa mappings of Z or Z_(pi)
6278##
6279##  Returns the pointwise sum of the rcwa mappings <f> and <g>.
6280##
6281InstallMethod( \+,
6282               "for two rcwa mappings of Z or Z_(pi) (RCWA)",
6283               IsIdenticalObj,
6284               [ IsRcwaMappingOfZOrZ_piInStandardRep,
6285                 IsRcwaMappingOfZOrZ_piInStandardRep ], 0,
6286
6287  function ( f, g )
6288
6289    local  c1, c2, c3, m1, m2, m3, n, n1, n2, pi;
6290
6291    c1 := f!.coeffs;  c2 := g!.coeffs;
6292    m1 := f!.modulus; m2 := g!.modulus;
6293    m3 := Lcm(m1, m2);
6294
6295    c3 := [];
6296    for n in [0 .. m3 - 1] do
6297      n1 := n mod m1 + 1;
6298      n2 := n mod m2 + 1;
6299      Add(c3, [ c1[n1][1] * c2[n2][3] + c1[n1][3] * c2[n2][1],
6300                c1[n1][2] * c2[n2][3] + c1[n1][3] * c2[n2][2],
6301                c1[n1][3] * c2[n2][3] ]);
6302    od;
6303
6304    if   IsRcwaMappingOfZ( f )
6305    then return RcwaMappingNC( c3 );
6306    else pi := NoninvertiblePrimes( Source( f ) );
6307         return RcwaMappingNC( pi, c3 );
6308    fi;
6309  end );
6310
6311#############################################################################
6312##
6313#M  \+( <f>, <g> ) . . . . . . . .  for two rcwa mappings of Z in sparse rep.
6314##
6315##  Returns the pointwise sum of the rcwa mappings <f> and <g>.
6316##
6317InstallMethod( \+,
6318               "for two rcwa mappings of Z in sparse rep. (RCWA)",
6319               IsIdenticalObj,
6320               [ IsRcwaMappingOfZInSparseRep,
6321                 IsRcwaMappingOfZInSparseRep ], 0,
6322
6323  function ( f, g )
6324
6325    local  coeffs1, coeffs2, coeffs3, c1, c2, c3, r;
6326
6327    coeffs1 := f!.coeffs; coeffs2 := g!.coeffs; coeffs3 := [];
6328    for c1 in coeffs1 do
6329      for c2 in coeffs2 do
6330        if (c1[1] - c2[1]) mod Gcd(c1[2],c2[2]) = 0 then
6331          r := ChineseRem([c1[2],c2[2]],[c1[1],c2[1]]);
6332          c3 := [ r, Lcm(c1[2],c2[2]),
6333                  c1[3]*c2[5] + c1[5]*c2[3],
6334                  c1[4]*c2[5] + c1[5]*c2[4],
6335                  c1[5]*c2[5] ];
6336          Add(coeffs3,c3);
6337        fi;
6338      od;
6339    od;
6340
6341    return RcwaMappingNC( coeffs3 );
6342  end );
6343
6344#############################################################################
6345##
6346#M  \+( <f>, <g> ) . . .  for rcwa mappings of Z: sparse rep. + standard rep.
6347#M  \+( <f>, <g> ) . . .  for rcwa mappings of Z: standard rep. + sparse rep.
6348##
6349##  Returns the pointwise sum of the rcwa mappings <f> and <g>.
6350##
6351InstallMethod( \+,
6352               "for rcwa mappings of Z: sparse rep. + standard rep. (RCWA)",
6353               IsIdenticalObj, [ IsRcwaMappingOfZInSparseRep,
6354                                 IsRcwaMappingOfZInStandardRep ], 0,
6355               function ( f, g ) return StandardRepresentation(f) + g; end );
6356
6357InstallMethod( \+,
6358               "for rcwa mappings of Z: standard rep. + sparse rep. (RCWA)",
6359               IsIdenticalObj, [ IsRcwaMappingOfZInStandardRep,
6360                                 IsRcwaMappingOfZInSparseRep ], 0,
6361               function ( f, g ) return f + StandardRepresentation(g); end );
6362
6363#############################################################################
6364##
6365#M  \+( <f>, <g> ) . . . . . . . . . . . .  for two rcwa mappings of GF(q)[x]
6366##
6367##  Returns the pointwise sum of the rcwa mappings <f> and <g>.
6368##
6369InstallMethod( \+,
6370               "for two rcwa mappings of GF(q)[x] (RCWA)",
6371               IsIdenticalObj,
6372               [ IsRcwaMappingOfGFqxInStandardRep,
6373                 IsRcwaMappingOfGFqxInStandardRep ], 0,
6374
6375  function ( f, g )
6376
6377    local c, m, d, R, q, x, res, r, n1, n2;
6378
6379    c := [f!.coeffs, g!.coeffs, []];
6380    m := [f!.modulus, g!.modulus, Lcm(f!.modulus,g!.modulus)];
6381    d := List(m, DegreeOfLaurentPolynomial);
6382    R := UnderlyingRing(FamilyObj(f));
6383    q := Size(CoefficientsRing(R));
6384    x := IndeterminatesOfPolynomialRing(R)[1];
6385    res := List(d, deg -> AllGFqPolynomialsModDegree(q,deg,x));
6386
6387    for r in res[3] do
6388      n1 := Position(res[1], r mod m[1]);
6389      n2 := Position(res[2], r mod m[2]);
6390      Add(c[3], [ c[1][n1][1] * c[2][n2][3] + c[1][n1][3] * c[2][n2][1],
6391                  c[1][n1][2] * c[2][n2][3] + c[1][n1][3] * c[2][n2][2],
6392                  c[1][n1][3] * c[2][n2][3] ]);
6393    od;
6394
6395    return RcwaMappingNC( q, m[3], c[3] );
6396  end );
6397
6398#############################################################################
6399##
6400#M  AdditiveInverseOp( <f> ) . . . . . . . . . . . . . . .  for rcwa mappings
6401#M  AdditiveInverseOp( <f> ) . . . . .  for rcwa mappings of Z in sparse rep.
6402##
6403##  Returns the pointwise additive inverse of rcwa mapping <f>.
6404##
6405InstallMethod( AdditiveInverseOp,
6406               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
6407               f -> f * RcwaMappingNC( Source(f), One(Source(f)),
6408                                       [[-1,0,1]] * One(Source(f)) ) );
6409
6410InstallMethod( AdditiveInverseOp,
6411               "for rcwa mappings of Z in sparse rep. (RCWA)", true,
6412               [ IsRcwaMappingOfZInSparseRep ], 0,
6413               f -> RcwaMappingNC(List(f!.coeffs,
6414                                       c->[c[1],c[2],-c[3],-c[4],c[5]])) );
6415
6416#############################################################################
6417##
6418#M  \+( <f>, <n> ) . . . . . . . . . . for an rcwa mapping and a ring element
6419#M  \+( <f>, <n> ) . . for an rcwa mapping of Z in sparse rep. and an integer
6420#M  \+( <n>, <f> ) . . . . . . . . . . for a ring element and an rcwa mapping
6421##
6422##  Returns the pointwise sum of the rcwa mapping <f> and the constant
6423##  rcwa mapping with value <n>.
6424##
6425InstallMethod( \+,
6426               "for an rcwa mapping and a ring element (RCWA)",
6427               ReturnTrue, [ IsRcwaMapping, IsRingElement ], 0,
6428
6429  function ( f, n )
6430
6431    local  R;
6432
6433    R := Source(f);
6434    if not n in R then TryNextMethod(); fi;
6435    return f + RcwaMapping(R,One(R),[[0,n,1]]*One(R));
6436  end );
6437
6438InstallMethod( \+,
6439               "for rcwa mapping of Z in sparse rep. and integer (RCWA)",
6440               ReturnTrue, [ IsRcwaMappingOfZInSparseRep, IsInt ], 0,
6441
6442  function ( f, n )
6443    return RcwaMappingNC(List(f!.coeffs,
6444                              c->[c[1],c[2],c[3],c[4]+n*c[5],c[5]]));
6445  end );
6446
6447InstallMethod( \+, "for a ring element and an rcwa mapping (RCWA)",
6448               ReturnTrue, [ IsRingElement, IsRcwaMapping ], 0,
6449               function ( n, f ) return f + n; end );
6450
6451#############################################################################
6452##
6453#M  \+( <f>, <v> ) . . . . . for rcwa mappings of Z^2, addition of a constant
6454##
6455InstallMethod( \+, "for an rcwa mappings of Z^2 and a row vector (RCWA)",
6456               ReturnTrue, [ IsRcwaMappingOfZxZ, IsRowVector ], 0,
6457
6458  function ( f, v )
6459
6460    local  coeffs, sum;
6461
6462    if not v in Source(f) then TryNextMethod(); fi;
6463
6464    coeffs := List(Coefficients(f),c->[c[1],c[2]+c[3]*v,c[3]]);
6465    sum    := RcwaMapping(Source(f),Modulus(f),coeffs);
6466
6467    if   HasIsInjective(f) and IsInjective(f)
6468    then SetIsInjective(sum,true); fi;
6469    if   HasIsSurjective(f) and IsSurjective(f)
6470    then SetIsSurjective(sum,true); fi;
6471
6472    return sum;
6473  end );
6474
6475InstallMethod( \+, "for a row vector and an rcwa mapping (RCWA)",
6476               ReturnTrue, [ IsRowVector, IsRcwaMappingOfZxZ ], 0,
6477               function ( v, f ) return f + v; end );
6478
6479#############################################################################
6480##
6481#S  Multiplying rcwa mappings. //////////////////////////////////////////////
6482##
6483#############################################################################
6484
6485#############################################################################
6486##
6487#M  CompositionMapping2( <g>, <f> ) . .  for two rcwa mappings of Z or Z_(pi)
6488##
6489##  Returns the product (composition) of the rcwa mappings <f> and <g>.
6490##  The mapping <f> is applied first.
6491##
6492InstallMethod( CompositionMapping2,
6493               "for two rcwa mappings of Z or Z_(pi) (RCWA)",
6494               IsIdenticalObj,
6495               [ IsRcwaMappingOfZOrZ_piInStandardRep,
6496                 IsRcwaMappingOfZOrZ_piInStandardRep ], SUM_FLAGS,
6497
6498  function ( g, f )
6499
6500    local  fg, c1, c2, c3, m1, m2, m3, n, n1, n2, pi;
6501
6502    if   ValueOption( "sparse" ) = true and Multiplier( f ) <> 0
6503    then TryNextMethod(); fi;
6504
6505    c1 := f!.coeffs;  c2 := g!.coeffs;
6506    m1 := f!.modulus; m2 := g!.modulus;
6507    m3 := Gcd( Lcm( m1, m2 ) * Divisor( f ), m1 * m2 );
6508
6509    if   ValueOption("RMPROD_NO_EXPANSION") = true
6510    then m3 := Maximum(m1,m2); fi;
6511
6512    c3 := [];
6513    for n in [0 .. m3 - 1] do
6514      n1 := n mod m1 + 1;
6515      n2 := (c1[n1][1] * n + c1[n1][2])/c1[n1][3] mod m2 + 1;
6516      Add(c3, [ c1[n1][1] * c2[n2][1],
6517                c1[n1][2] * c2[n2][1] + c1[n1][3] * c2[n2][2],
6518                c1[n1][3] * c2[n2][3] ]);
6519    od;
6520
6521    if   IsRcwaMappingOfZ(f)
6522    then fg := RcwaMappingNC(c3);
6523    else pi := NoninvertiblePrimes(Source(f));
6524         fg := RcwaMappingNC(pi,c3);
6525    fi;
6526
6527    if    HasIsInjective(f) and IsInjective(f)
6528      and HasIsInjective(g) and IsInjective(g)
6529    then SetIsInjective(fg,true); fi;
6530
6531    if    HasIsSurjective(f) and IsSurjective(f)
6532      and HasIsSurjective(g) and IsSurjective(g)
6533    then SetIsSurjective(fg,true); fi;
6534
6535    return fg;
6536  end );
6537
6538#############################################################################
6539##
6540#M  CompositionMapping2( <g>, <f> ) . for two rcwa mappings of Z, sparse rep.
6541##
6542##  Returns the product (composition) of the rcwa mappings <f> and <g>.
6543##  The mapping <f> is applied first.
6544##
6545InstallMethod( CompositionMapping2,
6546               "for two rcwa mappings of Z, sparse rep. (RCWA)",
6547               IsIdenticalObj,
6548               [ IsRcwaMappingOfZInSparseRep,
6549                 IsRcwaMappingOfZInSparseRep ], SUM_FLAGS,
6550
6551  function ( g, f )
6552
6553    local  fg, coeffs, c1, c2, c, img, int, pre;
6554
6555    coeffs := [];
6556    for c1 in f!.coeffs do
6557      img := [ (c1[3]*c1[1] + c1[4])/c1[5], c1[3]*c1[2]/c1[5] ];
6558      for c2 in g!.coeffs do
6559        if img[2] <> 0 then
6560          if ( c2[1] - img[1] ) mod Gcd( c2[2], img[2] ) = 0 then
6561            int := [ ChineseRem( [ c2[2], img[2] ], [ c2[1], img[1] ] ),
6562                     Lcm( c2[2], img[2] ) ];
6563            pre := [ (int[1]*c1[5] - c1[4])/c1[3], int[2]*c1[5]/c1[3] ];
6564            c := [ pre[1], pre[2], c1[3]*c2[3],
6565                                   c1[4]*c2[3] + c1[5]*c2[4],
6566                                   c1[5]*c2[5] ];
6567            Add(coeffs,c);
6568          fi;
6569        elif img[1] mod c2[2] = c2[1] then
6570          pre := [ c1[1], c1[2] ];
6571          c := [ pre[1], pre[2], c1[3]*c2[3],
6572                                 c1[4]*c2[3] + c1[5]*c2[4],
6573                                 c1[5]*c2[5] ];
6574          Add(coeffs,c);
6575        fi;
6576      od;
6577    od;
6578
6579    fg := RcwaMappingNC(coeffs);
6580
6581    if    HasIsInjective(f) and IsInjective(f)
6582      and HasIsInjective(g) and IsInjective(g)
6583    then SetIsInjective(fg,true); fi;
6584
6585    if    HasIsSurjective(f) and IsSurjective(f)
6586      and HasIsSurjective(g) and IsSurjective(g)
6587    then SetIsSurjective(fg,true); fi;
6588
6589    return fg;
6590  end );
6591
6592#############################################################################
6593##
6594#M  CompositionMapping2( <g>, <f> ) . . . . . . . sparse rep. * standard rep.
6595#M  CompositionMapping2( <g>, <f> ) . . . . . . . standard rep. * sparse rep.
6596##
6597##  Returns the product (composition) of the rcwa mappings <f> and <g>.
6598##  The mapping <f> is applied first.
6599##
6600InstallMethod( CompositionMapping2,
6601               "for rcwa mappings of Z, sparse rep. * standard rep. (RCWA)",
6602               IsIdenticalObj,
6603               [ IsRcwaMappingOfZInStandardRep,
6604                 IsRcwaMappingOfZInSparseRep ], SUM_FLAGS,
6605               function ( g, f )
6606                 return CompositionMapping2(g,StandardRepresentation(f));
6607               end );
6608
6609InstallMethod( CompositionMapping2,
6610               "for rcwa mappings of Z, standard rep. * sparse rep. (RCWA)",
6611               IsIdenticalObj,
6612               [ IsRcwaMappingOfZInSparseRep,
6613                 IsRcwaMappingOfZInStandardRep ], SUM_FLAGS,
6614               function ( g, f )
6615                 return CompositionMapping2(StandardRepresentation(g),f);
6616               end );
6617
6618#############################################################################
6619##
6620#M  CompositionMapping2( <g>, <f> ) . . . . . .  for two rcwa mappings of Z^2
6621##
6622##  Returns the product (composition) of the rcwa mappings <f> and <g>.
6623##  The mapping <f> is applied first.
6624##
6625InstallMethod( CompositionMapping2,
6626               "for two rcwa mappings of Z^2 (RCWA)",
6627               IsIdenticalObj,
6628               [ IsRcwaMappingOfZxZInStandardRep,
6629                 IsRcwaMappingOfZxZInStandardRep ], SUM_FLAGS,
6630
6631  function ( g, f )
6632
6633    local  R, fg, c1, c2, c, m1, m2, m,
6634           res1, res2, res, r1, r2, r, i1, i2, i;
6635
6636    if   ValueOption( "sparse" ) = true and not IsZero( Multiplier( f ) )
6637    then TryNextMethod(); fi;
6638
6639    R := Source(f);
6640
6641    c1 := Coefficients(f);  c2 := Coefficients(g);
6642    m1 := Modulus(f);       m2 := Modulus(g);
6643
6644    m := List(c1,t->m2*t[1]^-1);
6645    for i in [1..Length(m)] do
6646      m[i] := m[i] * Lcm(List(Flat(m[i]),DenominatorRat));
6647    od;
6648    m := Lcm(m1,Lcm(m)) * Divisor(f);
6649
6650    res1 := AllResidues(R,m1);
6651    res2 := AllResidues(R,m2);
6652    res  := AllResidues(R,m);
6653
6654    c := [];
6655    for r in res do
6656      r1 := r mod m1;
6657      i1 := Position(res1,r1);
6658      r2 := (r * c1[i1][1] + c1[i1][2])/c1[i1][3] mod m2;
6659      i2 := Position(res2,r2);
6660      Add(c, [ c1[i1][1] * c2[i2][1],
6661               c1[i1][2] * c2[i2][1] + c1[i1][3] * c2[i2][2],
6662               c1[i1][3] * c2[i2][3] ]);
6663    od;
6664
6665    fg := RcwaMapping(R,m,c); # ... NC, once tested
6666
6667    if    HasIsInjective(f) and IsInjective(f)
6668      and HasIsInjective(g) and IsInjective(g)
6669    then SetIsInjective(fg,true); fi;
6670
6671    if    HasIsSurjective(f) and IsSurjective(f)
6672      and HasIsSurjective(g) and IsSurjective(g)
6673    then SetIsSurjective(fg,true); fi;
6674
6675    return fg;
6676  end );
6677
6678#############################################################################
6679##
6680#M  CompositionMapping2( <g>, <f> ) . . . . for two rcwa mappings of GF(q)[x]
6681##
6682##  Returns the product (composition) of the rcwa mappings <f> and <g>.
6683##  The mapping <f> is applied first.
6684##
6685InstallMethod( CompositionMapping2,
6686               "for two rcwa mappings of GF(q)[x] (RCWA)",
6687               IsIdenticalObj,
6688               [ IsRcwaMappingOfGFqxInStandardRep,
6689                 IsRcwaMappingOfGFqxInStandardRep ], SUM_FLAGS,
6690
6691  function ( g, f )
6692
6693    local  fg, c, m, d, R, q, x, res, r, n1, n2;
6694
6695    if   ValueOption( "sparse" ) = true and not IsZero( Multiplier( f ) )
6696    then TryNextMethod(); fi;
6697
6698    c := [f!.coeffs, g!.coeffs, []];
6699    m := [f!.modulus, g!.modulus];
6700    m[3] := Minimum( Lcm( m[1], m[2] ) * Divisor( f ), m[1] * m[2] );
6701    d := List(m, DegreeOfLaurentPolynomial);
6702    R := UnderlyingRing(FamilyObj(f));
6703    q := Size(CoefficientsRing(R));
6704    x := IndeterminatesOfPolynomialRing(R)[1];
6705    res := List(d, deg -> AllGFqPolynomialsModDegree(q,deg,x));
6706
6707    for r in res[3] do
6708      n1 := Position(res[1], r mod m[1]);
6709      n2 := Position(res[2],
6710                     (c[1][n1][1] * r + c[1][n1][2])/c[1][n1][3] mod m[2]);
6711      Add(c[3], [ c[1][n1][1] * c[2][n2][1],
6712                  c[1][n1][2] * c[2][n2][1] + c[1][n1][3] * c[2][n2][2],
6713                  c[1][n1][3] * c[2][n2][3] ]);
6714    od;
6715
6716    fg := RcwaMappingNC( q, m[3], c[3] );
6717
6718    if    HasIsInjective(f) and IsInjective(f)
6719      and HasIsInjective(g) and IsInjective(g)
6720    then SetIsInjective(fg,true); fi;
6721
6722    if    HasIsSurjective(f) and IsSurjective(f)
6723      and HasIsSurjective(g) and IsSurjective(g)
6724    then SetIsSurjective(fg,true); fi;
6725
6726    return fg;
6727  end );
6728
6729#############################################################################
6730##
6731#M  CompositionMapping2( <g>, <f> ) . . . . . for two rcwa mappings of a ring
6732##
6733##  Returns the product (composition) of the rcwa mappings <f> and <g>.
6734##  The mapping <f> is applied first. The multiplier of <f> must not be zero.
6735##
6736##  This method performs better than the standard methods above if <f> and
6737##  <g> have only few different affine partial mappings. It is used in place
6738##  of the standard methods if the option "sparse" is set.
6739##
6740##  This method presently does not work for Z^2, thus for this case there is
6741##  a separate "sparse" method (see below).
6742##
6743InstallMethod( CompositionMapping2,
6744               "for two rcwa mappings of a ring (sparse case method) (RCWA)",
6745               IsIdenticalObj, [ IsRcwaMappingInStandardRep,
6746                                 IsRcwaMappingInStandardRep ], 0,
6747
6748  function ( g, f )
6749
6750    local  fg, R, mf, mg, m, resf, resg, res, cf, cg, c,
6751           affs_f, affs_g, affs, Pf, Pg, Pf_img, P, cl, cl1, cl2,
6752           aff, pre, img, rj, mj, rjpre, mjpre, rjimg, mjimg, pos, i, j, k;
6753
6754    if IsZero(Multiplier(f)) then TryNextMethod(); fi;
6755
6756    R := Source(f);
6757    if not IsRing(R) then TryNextMethod(); fi;
6758
6759    mf   := Modulus(f);        mg   := Modulus(g);
6760    resf := AllResidues(R,mf); resg := AllResidues(R,mg);
6761    cf   := Coefficients(f);   cg   := Coefficients(g);
6762
6763    Pf := LargestSourcesOfAffineMappings(f);
6764    Pg := LargestSourcesOfAffineMappings(g);
6765
6766    affs_f := List(Pf,S->cf[First([1..Length(resf)],i->resf[i] in S)]);
6767    affs_g := List(Pg,S->cg[First([1..Length(resg)],i->resg[i] in S)]);
6768
6769    Pf := List(Pf,AsUnionOfFewClasses); Pg := List(Pg,AsUnionOfFewClasses);
6770
6771    Pf_img := [];
6772    for i in [1..Length(Pf)] do
6773      Pf_img[i] := [];
6774      for cl in Pf[i] do
6775        rj := Residue(cl); mj := Modulus(cl);
6776        rjimg := (rj*affs_f[i][1]+affs_f[i][2])/affs_f[i][3];
6777        mjimg := affs_f[i][1]*mj/affs_f[i][3];
6778        img   := ResidueClass(R,mjimg,rjimg);
6779        Add(Pf_img[i],img);
6780      od;
6781    od;
6782
6783    P := []; affs := [];
6784    for i in [1..Length(Pf_img)] do
6785      for cl1 in Pf_img[i] do
6786        for j in [1..Length(Pg)] do
6787          for cl2 in Pg[j] do
6788            cl  := Intersection(cl1,cl2);
6789            if cl = [] then continue; fi;
6790            aff := [affs_f[i][1]*affs_g[j][1],
6791                    affs_f[i][2]*affs_g[j][1]+affs_f[i][3]*affs_g[j][2],
6792                    affs_f[i][3]*affs_g[j][3]];
6793            pos := Position(affs,aff);
6794            if pos = fail then
6795              Add(affs,aff); Add(P,[]);
6796              pos := Length(affs);
6797            fi;
6798            rj := Residue(cl); mj := Modulus(cl);
6799            rjpre := (rj*affs_f[i][3]-affs_f[i][2])/affs_f[i][1];
6800            mjpre := affs_f[i][3]*mj/affs_f[i][1];
6801            pre   := ResidueClass(R,mjpre,rjpre);
6802            Add(P[pos],pre);
6803          od;
6804        od;
6805      od;
6806    od;
6807
6808    m   := Lcm(List(Flat(P),Modulus));
6809    res := AllResidues(R,m);
6810    c   := ListWithIdenticalEntries(Length(res),[1,0,1]*One(R));
6811
6812    for i in [1..Length(P)] do
6813      aff := affs[i];
6814      for cl in P[i] do
6815        rj := Residue(cl); mj := Modulus(cl);
6816        for k in [1..Length(res)] do
6817          if res[k] mod mj = rj then c[k] := aff; fi;
6818        od;
6819      od;
6820    od;
6821
6822    fg := RcwaMappingNC(R,m,c);
6823
6824    if    HasIsInjective(f) and IsInjective(f)
6825      and HasIsInjective(g) and IsInjective(g)
6826    then SetIsInjective(fg,true); fi;
6827
6828    if    HasIsSurjective(f) and IsSurjective(f)
6829      and HasIsSurjective(g) and IsSurjective(g)
6830    then SetIsSurjective(fg,true); fi;
6831
6832    return fg;
6833  end );
6834
6835#############################################################################
6836##
6837#M  CompositionMapping2( <g>, <f> ) . . . . . .  for two rcwa mappings of Z^2
6838##
6839##  Returns the product (composition) of the rcwa mappings <f> and <g>.
6840##  The mapping <f> is applied first. The multiplier of <f> must not be zero.
6841##
6842##  This is the equivalent for rcwa mappings of Z^2 of the "sparse" method
6843##  above.
6844##
6845InstallMethod( CompositionMapping2,
6846               "for two rcwa mappings of a ring (sparse case method) (RCWA)",
6847               IsIdenticalObj, [ IsRcwaMappingOfZxZInStandardRep,
6848                                 IsRcwaMappingOfZxZInStandardRep ], 0,
6849
6850  function ( g, f )
6851
6852    local  fg, R, mf, mg, m, resf, resg, res, cf, cg, c,
6853           affs_f, affs_g, affs, Pf, Pg, Pf_img, P, S1, S2, I,
6854           aff, pre, ri, mi, pos, i, j;
6855
6856    if IsZero(Multiplier(f)) then TryNextMethod(); fi;
6857
6858    R := Source(f);
6859
6860    mf   := Modulus(f);        mg   := Modulus(g);
6861    resf := AllResidues(R,mf); resg := AllResidues(R,mg);
6862    cf   := Coefficients(f);   cg   := Coefficients(g);
6863
6864    Pf := LargestSourcesOfAffineMappings(f);
6865    Pg := LargestSourcesOfAffineMappings(g);
6866
6867    affs_f := List(Pf,S->cf[First([1..Length(resf)],i->resf[i] in S)]);
6868    affs_g := List(Pg,S->cg[First([1..Length(resg)],i->resg[i] in S)]);
6869
6870    Pf_img := List(Pf,S->S^f);
6871
6872    P := []; affs := [];
6873    for i in [1..Length(Pf_img)] do
6874      S1 := Pf_img[i];
6875      for j in [1..Length(Pg)] do
6876        S2 := Pg[j];
6877        I := Intersection(S1,S2);
6878        if IsEmpty(I) then continue; fi;
6879        aff := [affs_f[i][1]*affs_g[j][1],
6880                affs_f[i][2]*affs_g[j][1]+affs_f[i][3]*affs_g[j][2],
6881                affs_f[i][3]*affs_g[j][3]];
6882        pos := Position(affs,aff);
6883        if pos = fail then
6884          Add(affs,aff); Add(P,[]);
6885          pos := Length(affs);
6886        fi;
6887        pre := (I*affs_f[i][3]-affs_f[i][2])*affs_f[i][1]^-1;
6888        P[pos] := Union(P[pos],pre);
6889      od;
6890    od;
6891
6892    m   := Lcm(List(P,Modulus));
6893    res := AllResidues(R,m);
6894    c   := ListWithIdenticalEntries(Length(res),Zero(R));
6895
6896    for i in [1..Length(P)] do
6897      aff := affs[i]; mi := Modulus(P[i]);
6898      for ri in Residues(P[i]) do
6899        for j in [1..Length(res)] do
6900          if res[j] mod mi = ri then c[j] := aff; fi;
6901        od;
6902      od;
6903    od;
6904
6905    fg := RcwaMappingNC(R,m,c);
6906
6907    if    HasIsInjective(f) and IsInjective(f)
6908      and HasIsInjective(g) and IsInjective(g)
6909    then SetIsInjective(fg,true); fi;
6910
6911    if    HasIsSurjective(f) and IsSurjective(f)
6912      and HasIsSurjective(g) and IsSurjective(g)
6913    then SetIsSurjective(fg,true); fi;
6914
6915    return fg;
6916  end );
6917
6918#############################################################################
6919##
6920#M  \*( <f>, <g> ) . . . . . . . . . . . . . . . . . .  for two rcwa mappings
6921##
6922##  Product (composition) of the rcwa mappings <f> and <g>.
6923##  The mapping <f> is applied first.
6924##
6925InstallMethod( \*,
6926               "for two rcwa mappings (RCWA)",
6927               IsIdenticalObj, [ IsRcwaMapping, IsRcwaMapping ], 0,
6928
6929  function ( f, g )
6930    return CompositionMapping( g, f );
6931  end );
6932
6933#############################################################################
6934##
6935#M  \*( <n>, <f> ) . . . . . . . . . . for a ring element and an rcwa mapping
6936#M  \*( <n>, <f> ) . .  for an integer and an rcwa mapping of Z in sparse rep.
6937#M  \*( <f>, <n> ) . . . . . . . . . . for an rcwa mapping and a ring element
6938##
6939InstallMethod( \*,
6940               "for rcwa mappings, multiplication by a constant (RCWA)",
6941               ReturnTrue, [ IsRingElement, IsRcwaMappingInStandardRep ], 0,
6942
6943  function ( n, f )
6944    if not n in Source(f) then TryNextMethod(); fi;
6945    return RcwaMapping(Source(f),Modulus(f),
6946                       List(Coefficients(f),c->[n*c[1],n*c[2],c[3]]));
6947  end );
6948
6949InstallMethod( \*,
6950               "for an rcwa mapping of Z in sparse rep. & a constant (RCWA)",
6951               ReturnTrue, [ IsInt, IsRcwaMappingOfZInSparseRep ], 0,
6952
6953  function ( n, f )
6954    if not n in Source(f) then TryNextMethod(); fi;
6955    return RcwaMapping(List(f!.coeffs,c->[c[1],c[2],n*c[3],n*c[4],c[5]]));
6956  end );
6957
6958InstallMethod( \*, "for rcwa mappings, multiplication by a constant (RCWA)",
6959               ReturnTrue, [ IsRcwaMapping, IsRingElement ], 0,
6960
6961  function ( f, n )
6962    if not n in Source(f) then TryNextMethod(); fi;
6963    return n * f;
6964  end );
6965
6966#############################################################################
6967##
6968#M  \*( <f>, <mat> ) . . for rcwa mappings of Z^2, multiplication by a matrix
6969##
6970InstallMethod( \*,
6971               "for rcwa mappings of Z^2, multiplication by a matrix (RCWA)",
6972               ReturnTrue, [ IsRcwaMappingOfZxZ, IsMatrix ], 0,
6973
6974  function ( f, mat )
6975
6976    local  coeffs, product;
6977
6978    if   DimensionsMat(mat) <> [2,2] or not ForAll(Flat(mat),IsInt)
6979    then TryNextMethod(); fi;
6980
6981    coeffs  := List(Coefficients(f),c->[c[1]*mat,c[2]*mat,c[3]]);
6982    product := RcwaMapping(Source(f),Modulus(f),coeffs);
6983
6984    if   DeterminantMat(mat) <> 0 and HasIsInjective(f) and IsInjective(f)
6985    then SetIsInjective(product,true); fi;
6986
6987    return product;
6988  end );
6989
6990#############################################################################
6991##
6992#M  \*( <n>, <f> ) . . for rcwa mappings of Z^2, multiplication by an integer
6993##
6994InstallMethod( \*,
6995               "for rcwa mappings of Z^2, multiplication by integer (RCWA)",
6996               ReturnTrue, [ IsInt, IsRcwaMappingOfZxZ ], 0,
6997               function ( n, f ) return f * [ [ n, 0 ], [ 0, n ] ]; end );
6998
6999#############################################################################
7000##
7001#S  Technical functions for deriving names of powers from names of bases. ///
7002##
7003#############################################################################
7004
7005#############################################################################
7006##
7007#F  NAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER( <name>, <n>, <order> )
7008##
7009##  Appends ^<n> to <name>, or multiplies an existing exponent by <n>.
7010##  Reduces the exponent modulo <order>, if known (i.e. <> fail).
7011##
7012BindGlobal( "NAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER",
7013
7014  function ( name, n, order )
7015
7016    local  strings, e;
7017
7018    strings := SplitString(name,"^");
7019    if not IsSubset("-0123456789",strings[Length(strings)]) then
7020      e    := n;
7021    else
7022      e    := Int(strings[Length(strings)]) * n;
7023      name := JoinStringsWithSeparator(strings{[1..Length(strings)-1]},"^");
7024    fi;
7025    if order = fail or order = infinity then
7026      return Concatenation(name,"^",String(e));
7027    elif e mod order = 1 then
7028      return name;
7029    elif e mod order = 0 then
7030      return fail;
7031    else
7032      return Concatenation(name,"^",String(e mod order));
7033    fi;
7034  end );
7035
7036#############################################################################
7037##
7038#F  LATEXNAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER( <name>, <n>, <order> )
7039##
7040##  Appends ^{<n>} to <name>, if <name> does not already include an exponent.
7041##  Reduces the exponent <n> modulo <order>, if known (i.e. <> fail).
7042##
7043BindGlobal( "LATEXNAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER",
7044
7045  function ( name, n, order )
7046
7047    local  strings, e;
7048
7049    strings := SplitString(name,"^");
7050    if not IsSubset("-0123456789{}",strings[Length(strings)]) then
7051      e    := n;
7052    else
7053      e    := Int(Filtered(strings[Length(strings)],
7054                           ch->ch in "-0123456789")) * n;
7055      name := JoinStringsWithSeparator(strings{[1..Length(strings)-1]},"^");
7056    fi;
7057    if   Position(name,'_') <> fail and name[1] <> '{'
7058    then name := Concatenation("{",name,"}"); fi;
7059    if order = fail or order = infinity then
7060      if e in [2..9] then return Concatenation(name,"^",String(e));
7061                     else return Concatenation(name,"^{",String(e),"}"); fi;
7062    elif e mod order = 1 then
7063      return name;
7064    elif e mod order = 0 then
7065      return fail;
7066    else
7067      e := e mod order;
7068      if e in [2..9] then return Concatenation(name,"^",String(e));
7069                     else return Concatenation(name,"^{",String(e),"}"); fi;
7070    fi;
7071  end );
7072
7073#############################################################################
7074##
7075#S  Computing inverses of rcwa permutations. ////////////////////////////////
7076##
7077#############################################################################
7078
7079#############################################################################
7080##
7081#M  InverseOp( <f> ) . . . . . . . . . . . . for rcwa mappings of Z or Z_(pi)
7082##
7083##  Returns the inverse mapping of the bijective rcwa mapping <f>.
7084##
7085InstallMethod( InverseOp,
7086               "for rcwa mappings of Z or Z_(pi) (RCWA)",
7087               true, [ IsRcwaMappingOfZOrZ_piInStandardRep ], 0,
7088
7089  function ( f )
7090
7091    local  Result, order, c, cInv, m, mInv, n, t, tm, tn, Classes, cl, pi;
7092
7093    if HasOrder(f) and Order(f) = 2 then return f; fi;
7094
7095    c := f!.coeffs; m := f!.modulus;
7096    cInv := [];
7097    mInv := Multiplier( f ) * m / Gcd( List( c, t -> t[3] ) );
7098    for n in [ 1 .. m ] do
7099      t := [c[n][3], -c[n][2], c[n][1]]; if t[3] = 0 then return fail; fi;
7100      tm := StandardAssociate(Source(f),c[n][1]) * m / c[n][3];
7101      tn := ((n - 1) * c[n][1] + c[n][2]) / c[n][3] mod tm;
7102      Classes := List([1 .. mInv/tm], i -> (i - 1) * tm + tn);
7103      for cl in Classes do
7104        if IsBound(cInv[cl + 1]) and cInv[cl + 1] <> t then return fail; fi;
7105        cInv[cl + 1] := t;
7106      od;
7107    od;
7108
7109    if not ForAll([1..mInv], i -> IsBound(cInv[i])) then return fail; fi;
7110
7111    if   IsRcwaMappingOfZ( f )
7112    then Result := RcwaMappingNC( cInv );
7113    else pi := NoninvertiblePrimes( Source( f ) );
7114         Result := RcwaMappingNC( pi, cInv );
7115    fi;
7116
7117    SetInverse(f,Result); SetInverse(Result,f);
7118    SetIsBijective(f,true); SetIsBijective(Result,true);
7119
7120    if HasOrder(f) then SetOrder(Result,Order(f)); order := Order(f);
7121                   else order := fail; fi;
7122    if HasBaseRoot(f) then
7123      SetBaseRoot(Result,BaseRoot(f));
7124      SetPowerOverBaseRoot(Result,-PowerOverBaseRoot(f));
7125    fi;
7126    if HasSmallestRoot(f) then
7127      SetSmallestRoot(Result,SmallestRoot(f));
7128      SetPowerOverSmallestRoot(Result,-PowerOverSmallestRoot(f));
7129    fi;
7130    if HasName(f) then
7131      SetName(Result,NAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER(
7132                       Name(f),-1,order));
7133    fi;
7134    if HasLaTeXString(f) then
7135      SetLaTeXString(Result,LATEXNAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER(
7136                            LaTeXString(f),-1,order));
7137    fi;
7138
7139    return Result;
7140  end );
7141
7142#############################################################################
7143##
7144#M  InverseOp( <f> ) . . . . . . . . .  for rcwa mappings of Z in sparse rep.
7145##
7146##  Returns the inverse mapping of the bijective rcwa mapping <f>.
7147##
7148InstallMethod( InverseOp,
7149               "for rcwa mappings of Z in sparse rep. (RCWA)",
7150               true, [ IsRcwaMappingOfZInSparseRep ], 0,
7151
7152  function ( f )
7153
7154    local  inverse, order;
7155
7156    if not IsBijective(f) then return fail; fi;
7157    if HasOrder(f) and Order(f) = 2 then return f; fi;
7158
7159    inverse := RcwaMappingNC(List(f!.coeffs,c->
7160                 [(c[3]*c[1]+c[4])/c[5],c[3]*c[2]/c[5],c[5],-c[4],c[3]]));
7161
7162    SetInverse(f,inverse); SetInverse(inverse,f);
7163    SetIsBijective(f,true); SetIsBijective(inverse,true);
7164
7165    if HasOrder(f) then SetOrder(inverse,Order(f)); order := Order(f);
7166                   else order := fail; fi;
7167    if HasBaseRoot(f) then
7168      SetBaseRoot(inverse,BaseRoot(f));
7169      SetPowerOverBaseRoot(inverse,-PowerOverBaseRoot(f));
7170    fi;
7171    if HasSmallestRoot(f) then
7172      SetSmallestRoot(inverse,SmallestRoot(f));
7173      SetPowerOverSmallestRoot(inverse,-PowerOverSmallestRoot(f));
7174    fi;
7175    if HasName(f) then
7176      SetName(inverse,NAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER(
7177                      Name(f),-1,order));
7178    fi;
7179    if HasLaTeXString(f) then
7180      SetLaTeXString(inverse,LATEXNAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER(
7181                            LaTeXString(f),-1,order));
7182    fi;
7183
7184    return inverse;
7185  end );
7186
7187#############################################################################
7188##
7189#M  InverseOp( <f> ) . . . . . . . . . . . . . . . . for rcwa mappings of Z^2
7190##
7191##  Returns the inverse mapping of the bijective rcwa mapping <f>.
7192##
7193InstallMethod( InverseOp,
7194               "for rcwa mappings of Z^2 (RCWA)", true,
7195               [ IsRcwaMappingOfZxZInStandardRep ], 0,
7196
7197  function ( f )
7198
7199    local  R, result, m, c, mInv, cInv, res, clsimg, indimg, t, order, i, j;
7200
7201    if HasOrder(f) and Order(f) = 2 then return f; fi;
7202    if not IsBijective(f) then return fail; fi;
7203
7204    R := Source(f); m := Modulus(f); c := Coefficients(f);
7205
7206    clsimg := AllResidueClassesModulo(R,m)^f;
7207    mInv   := Lcm(List(clsimg,Modulus));
7208    res    := AllResidues(R,mInv);
7209    cInv   := [];
7210
7211    for i in [1..Length(clsimg)] do
7212      t := [c[i][3]*c[i][1]^-1,-c[i][2]*c[i][1]^-1,1];
7213      t := t * Lcm(List(Flat(t),DenominatorRat));
7214      indimg := Filtered([1..Length(res)],j->res[j] in clsimg[i]);
7215      for j in indimg do cInv[j] := t; od;
7216    od;
7217
7218    result := RcwaMapping(R,mInv,cInv); # ... NC, once tested
7219
7220    SetInverse(f,result); SetInverse(result,f);
7221    SetIsBijective(f,true); SetIsBijective(result,true);
7222
7223    if HasOrder(f) then SetOrder(result,Order(f)); order := Order(f);
7224                   else order := fail; fi;
7225    if HasName(f) then
7226      SetName(result,NAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER(
7227                       Name(f),-1,order));
7228    fi;
7229    if HasLaTeXString(f) then
7230      SetLaTeXString(result,LATEXNAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER(
7231                            LaTeXString(f),-1,order));
7232    fi;
7233
7234    return result;
7235  end );
7236
7237#############################################################################
7238##
7239#M  InverseOp( <f> ) . . . . . . . . . . . . .  for rcwa mappings of GF(q)[x]
7240##
7241##  Returns the inverse mapping of the bijective rcwa mapping <f>.
7242##
7243InstallMethod( InverseOp,
7244               "for rcwa mappings of GF(q)[x] (RCWA)", true,
7245               [ IsRcwaMappingOfGFqxInStandardRep ], 0,
7246
7247  function ( f )
7248
7249    local  Result, order, c, cInv, m, mInv, d, dInv, R, q, x,
7250           respols, res, resInv, r, n, t, tm, tr, tn, Classes, cl, pos;
7251
7252    if HasOrder(f) and Order(f) = 2 then return f; fi;
7253
7254    R := UnderlyingRing(FamilyObj(f));
7255    q := Size(CoefficientsRing(R));
7256    x := IndeterminatesOfPolynomialRing(R)[1];
7257    c := f!.coeffs; m := f!.modulus;
7258    cInv := [];
7259    mInv := StandardAssociate( R,
7260              Multiplier( f ) * m / Gcd( m, Gcd( List( c, t -> t[3] ) ) ) );
7261    d := DegreeOfLaurentPolynomial(m);
7262    dInv := DegreeOfLaurentPolynomial(mInv);
7263    res := AllGFqPolynomialsModDegree(q,d,x);
7264    respols := List([0..dInv], d -> AllGFqPolynomialsModDegree(q,d,x));
7265    resInv := respols[dInv + 1];
7266
7267    for n in [ 1 .. Length(res) ] do
7268      r := res[n];
7269      t := [c[n][3], -c[n][2], c[n][1]];
7270      if IsZero(t[3]) then return fail; fi;
7271      tm := StandardAssociate(Source(f),c[n][1]) * m / c[n][3];
7272      tr := (r * c[n][1] + c[n][2]) / c[n][3] mod tm;
7273      Classes := List(respols[DegreeOfLaurentPolynomial(mInv/tm) + 1],
7274                      p -> p * tm + tr);
7275      for cl in Classes do
7276        pos := Position(resInv,cl);
7277        if IsBound(cInv[pos]) and cInv[pos] <> t then return fail; fi;
7278        cInv[pos] := t;
7279      od;
7280    od;
7281
7282    if   not ForAll([1..Length(resInv)], i -> IsBound(cInv[i]))
7283    then return fail; fi;
7284
7285    Result := RcwaMappingNC( q, mInv, cInv );
7286
7287    SetInverse(f,Result); SetInverse(Result,f);
7288    SetIsBijective(f,true); SetIsBijective(Result,true);
7289
7290    if HasOrder(f) then SetOrder(Result,Order(f)); order := Order(f);
7291                   else order := fail; fi;
7292    if HasName(f) then
7293      SetName(Result,NAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER(
7294                       Name(f),-1,order));
7295    fi;
7296    if HasLaTeXString(f) then
7297      SetLaTeXString(Result,LATEXNAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER(
7298                            LaTeXString(f),n,order));
7299    fi;
7300
7301    return Result;
7302  end );
7303
7304#############################################################################
7305##
7306#M  InverseGeneralMapping( <f> ) . . . . . . . . . . . . .  for rcwa mappings
7307##
7308##  Returns the inverse mapping of the bijective rcwa mapping <f>.
7309##
7310InstallMethod( InverseGeneralMapping,
7311               "for rcwa mappings (RCWA)",
7312               true, [ IsRcwaMapping ], 0,
7313
7314  function ( f )
7315    if IsBijective(f) then return Inverse(f); else TryNextMethod(); fi;
7316  end );
7317
7318#############################################################################
7319##
7320#S  Computing right inverses of injective rcwa mappings. ////////////////////
7321##
7322#############################################################################
7323
7324#############################################################################
7325##
7326#M  RightInverse( <f> ) . . . . . . . . . . . . . for injective rcwa mappings
7327##
7328InstallMethod( RightInverse,
7329               "for injective rcwa mappings (RCWA)",
7330               true, [ IsRcwaMapping ], 0,
7331
7332  function ( f )
7333
7334    local  R, mf, cf, resf, inv, minv, cinv, resinv, imgs,
7335           r1, r2, pos1, pos2, idcoeff;
7336
7337    if not IsInjective(f) then return fail; fi;
7338    if not IsRcwaMappingStandardRep(f) then f := StandardRep(f); fi;
7339
7340    R := Source(f);
7341    if   IsRing(R) then idcoeff := [1,0,1] * One(R);
7342    elif IsZxZ(R)  then idcoeff := [[[1,0],[0,1]],[0,0],1];
7343    else TryNextMethod(); fi;
7344    mf := Modulus(f); cf := Coefficients(f);
7345    imgs := AllResidueClassesModulo(R,mf)^f;
7346    minv := Lcm(List(imgs,Modulus));
7347    cinv := ListWithIdenticalEntries(NumberOfResidues(R,minv),idcoeff);
7348    resf := AllResidues(R,mf); resinv := AllResidues(R,minv);
7349    for r1 in resf do
7350      pos1 := PositionSorted(resf,r1);
7351      for r2 in AsList(Intersection(resinv,imgs[pos1])) do
7352        pos2 := PositionSorted(resinv,r2);
7353        if   IsRing(R)
7354        then cinv[pos2] := [cf[pos1][3],-cf[pos1][2],cf[pos1][1]];
7355        else cinv[pos2] := [cf[pos1][3]*cf[pos1][1]^-1,
7356                           -cf[pos1][2]*cf[pos1][1]^-1,1];
7357             cinv[pos2] := cinv[pos2] * Lcm(List(Flat(cinv[pos2]),
7358                                                 DenominatorRat));
7359        fi;
7360      od;
7361    od;
7362    inv := RcwaMapping(R,minv,cinv);
7363    return inv;
7364  end );
7365
7366#############################################################################
7367##
7368#M  RightInverse( <f> ) . . . . . . . . . .  for injective rcwa mappings of Z
7369##
7370InstallMethod( RightInverse,
7371               "for injective rcwa mappings of Z (RCWA)",
7372               true, [ IsRcwaMappingOfZ ], 0,
7373
7374  function ( f )
7375
7376    local  inv, mf, cf, minv, cinv, imgs, r1, r2;
7377
7378    if not IsInjective(f) then return fail; fi;
7379    if not IsRcwaMappingStandardRep(f) then f := StandardRep(f); fi;
7380
7381    mf := Modulus(f); cf := Coefficients(f);
7382    imgs := AllResidueClassesModulo(mf)^f;
7383    minv := Lcm(List(imgs,Modulus));
7384    cinv := List([1..minv],r->[1,0,1]);
7385    for r1 in [1..mf] do
7386      for r2 in Intersection([0..minv-1],imgs[r1]) do
7387        cinv[r2+1] := [cf[r1][3],-cf[r1][2],cf[r1][1]];
7388      od;
7389    od;
7390    inv := RcwaMapping(cinv);
7391    return inv;
7392  end );
7393
7394#############################################################################
7395##
7396#M  CommonRightInverse( <l>, <r> ) . . . . . . . . for two rcwa mappings of Z
7397##
7398InstallMethod( CommonRightInverse,
7399               "for two rcwa mappings of Z (RCWA)", true,
7400               [ IsRcwaMappingOfZ, IsRcwaMappingOfZ ], 0,
7401
7402  function ( l, r )
7403
7404    local  d, imgl, imgr, coeffs, m, c, r1, r2;
7405
7406    if not ForAll([l,r],IsInjective) or Intersection(Image(l),Image(r)) <> []
7407       or Union(Image(l),Image(r)) <> Integers
7408    then return fail; fi;
7409
7410    if not IsRcwaMappingStandardRep(l) then l := StandardRep(l); fi;
7411    if not IsRcwaMappingStandardRep(l) then r := StandardRep(r); fi;
7412
7413    imgl := AllResidueClassesModulo(Modulus(l))^l;
7414    imgr := AllResidueClassesModulo(Modulus(r))^r;
7415
7416    m := Lcm(List(Concatenation(imgl,imgr),Modulus));
7417
7418    coeffs := List([0..m-1],r1->[1,0,1]);
7419
7420    for r1 in [0..Length(imgl)-1] do
7421      c := Coefficients(l)[r1+1];
7422      for r2 in Intersection(imgl[r1+1],[0..m-1]) do
7423        coeffs[r2+1] := [ c[3], -c[2], c[1] ];
7424      od;
7425    od;
7426
7427    for r1 in [0..Length(imgr)-1] do
7428      c := Coefficients(r)[r1+1];
7429      for r2 in Intersection(imgr[r1+1],[0..m-1]) do
7430        coeffs[r2+1] := [ c[3], -c[2], c[1] ];
7431      od;
7432    od;
7433
7434    d := RcwaMapping(coeffs);
7435    return d;
7436
7437  end );
7438
7439#############################################################################
7440##
7441#S  Computing conjugates and powers of rcwa mappings. ///////////////////////
7442##
7443#############################################################################
7444
7445#############################################################################
7446##
7447#M  \^( <g>, <h> ) . . . . . . . . . . . . . . . . . .  for two rcwa mappings
7448##
7449##  Returns the conjugate of the rcwa mapping <g> under <h>,
7450##  i.e. <h>^-1*<g>*<h>.
7451##
7452InstallMethod( \^,
7453               "for two rcwa mappings (RCWA)",
7454               IsIdenticalObj, [ IsRcwaMapping, IsRcwaMapping ], 0,
7455
7456  function ( g, h )
7457
7458    local  f;
7459
7460    if IsOne(h) then return g; fi;
7461    f := h^-1 * g * h;
7462    if f = g then return g; fi;
7463    if HasOrder (g) then SetOrder (f,Order (g)); fi;
7464    if HasIsTame(g) then SetIsTame(f,IsTame(g)); fi;
7465    if   HasStandardConjugate(g)
7466    then SetStandardConjugate(f,StandardConjugate(g)); fi;
7467    if   HasStandardizingConjugator(g)
7468    then SetStandardizingConjugator(f,h^-1*StandardizingConjugator(g)); fi;
7469    if HasSmallestRoot(g) and AbsInt(PowerOverSmallestRoot(g)) > 1 then
7470      SetSmallestRoot(f,SmallestRoot(g)^h);
7471      SetPowerOverSmallestRoot(f,PowerOverSmallestRoot(g));
7472    fi;
7473    return f;
7474  end );
7475
7476#############################################################################
7477##
7478#M  IsCommuting( <g>, <h> ) . . . . . . . . . . . . . . . . for rcwa mappings
7479##
7480InstallMethod( IsCommuting,
7481               "for rcwa mappings (RCWA)", IsIdenticalObj,
7482               [ IsRcwaMapping, IsRcwaMapping ], 0,
7483
7484  function ( g, h )
7485
7486    local  R, a, b;
7487
7488    if g = h then return true; fi;
7489    if HasInverse(g) and h = Inverse(g) then return true; fi;
7490
7491    R := Source(g);
7492    if   NumberOfResidues(R,Mod(h)) > NumberOfResidues(R,Mod(g))
7493    then a := g; b := h; else a := h; b := g; fi;
7494    if   not ForAll(AllResidues(R,Mod(a)),r->(r^a)^b=(r^b)^a)
7495    then return false; fi;
7496
7497    return g*h = h*g;
7498  end );
7499
7500#############################################################################
7501##
7502#M  \^( <perm>, <g> ) . . . . . .  for a permutation and an rcwa mapping of Z
7503##
7504##  Returns the conjugate of the GAP permutation <perm> under the
7505##  rcwa permutation <g>. The rcwa permutation <g> must not move any point
7506##  in the support of <perm> to a negative integer.
7507##
7508InstallMethod( \^,
7509               "for a permutation and an rcwa mapping of Z (RCWA)",
7510               ReturnTrue, [ IsPerm, IsRcwaMappingOfZ ], 0,
7511
7512  function ( perm, g )
7513
7514    local  cycs, cyc, h, i;
7515
7516    if not IsBijective(g) then
7517      Error("<g> must be bijective.\n");
7518      return fail;
7519    fi;
7520    if not ForAll(MovedPoints(perm)^g,IsPosInt) then
7521      Info(InfoWarning,1,
7522           "Warning: GAP permutations can only move positive integers!");
7523      TryNextMethod();
7524    fi;
7525    cycs := List(Cycles(perm,MovedPoints(perm)),cyc->OnTuples(cyc,g));
7526    h := ();
7527    for cyc in cycs do
7528      for i in [2..Length(cyc)] do h := h * (cyc[1],cyc[i]); od;
7529    od;
7530    return h;
7531  end );
7532
7533#############################################################################
7534##
7535#M  \^( <f>, <n> ) . . . . . . . . . . . . for an rcwa mapping and an integer
7536##
7537##  Returns the <n>-th power of the rcwa mapping <f>.
7538##
7539InstallMethod( \^,
7540               "for an rcwa mapping and an integer (RCWA)",
7541               ReturnTrue, [ IsRcwaMapping, IsInt ], 0,
7542
7543  function ( f, n )
7544
7545    local  pow, e, name, latex;
7546
7547    if ValueOption("UseKernelPOW") = true then TryNextMethod(); fi;
7548
7549    if HasOrder(f) and Order(f) <> infinity then n := n mod Order(f); fi;
7550
7551    if   n = 0 then return One(f);
7552    elif n = 1 then return f;
7553    else if n > 1 then pow := POW(f,n:UseKernelPOW);
7554                  else pow := POW(Inverse(f),-n:UseKernelPOW); fi;
7555    fi;
7556
7557    if HasIsTame(f) then SetIsTame(pow,IsTame(f)); fi;
7558
7559    if HasBaseRoot(f) then
7560      SetBaseRoot(pow,BaseRoot(f));
7561      SetPowerOverBaseRoot(pow,PowerOverBaseRoot(f)*n);
7562    fi;
7563
7564    if HasSmallestRoot(f) then
7565      SetSmallestRoot(pow,SmallestRoot(f));
7566      SetPowerOverSmallestRoot(pow,PowerOverSmallestRoot(f)*n);
7567    fi;
7568
7569    if HasOrder(f) then
7570      if Order(f) = infinity then SetOrder(pow,infinity); else
7571        SetOrder(pow,Order(f)/Gcd(Order(f),n));
7572      fi;
7573      if HasName(f) and HasIsTame(f) and IsTame(f) then
7574        name := NAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER(Name(f),n,Order(f));
7575        if name <> fail then SetName(pow,name); fi;
7576      fi;
7577      if HasLaTeXString(f) then
7578        latex := LATEXNAME_OF_POWER_BY_NAME_EXPONENT_AND_ORDER(
7579                   LaTeXString(f),n,Order(f));
7580        if latex <> fail then SetLaTeXString(pow,latex); fi;
7581      fi;
7582    fi;
7583
7584    if   HasIsClassShift(f) and IsClassShift(f) and not IsOne(pow)
7585    then SetIsPowerOfClassShift(pow,true); fi;
7586
7587    return pow;
7588  end );
7589
7590#############################################################################
7591##
7592#S  Testing an rcwa mapping for tameness, and respected partitions. /////////
7593##
7594#############################################################################
7595
7596#############################################################################
7597##
7598#M  IsTame( <f> ) . . . . . . . . . . . . . . . . . . . . . for rcwa mappings
7599##
7600InstallMethod( IsTame,
7601               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
7602
7603  function ( f )
7604
7605    local  gamma, delta, C, r, m, coeffs, cl, img,
7606           starttime, k, pow, exp, e;
7607
7608    Info(InfoRCWA,3,"`IsTame' for an rcwa mapping <f> of ",
7609                    RingToString(Source(f)),".");
7610
7611    if IsBijective(f) and HasOrder(f) and Order(f) <> infinity then
7612      Info(InfoRCWA,3,"IsTame: <f> has finite order, hence is tame.");
7613      return true;
7614    fi;
7615
7616    if IsIntegral(f) then
7617      Info(InfoRCWA,3,"IsTame: <f> is integral, hence tame.");
7618      return true;
7619    fi;
7620
7621    if IsRing(Source(f))
7622      and not IsSubset(Factors(Multiplier(f)),Factors(Divisor(f)))
7623    then
7624      Info(InfoRCWA,3,"IsTame: <f> is wild, by balancedness criterion.");
7625      if IsBijective(f) then SetOrder(f,infinity); fi;
7626      return false;
7627    fi;
7628
7629    if IsBijective(f) and not IsBalanced(f) then
7630      Info(InfoRCWA,3,"IsTame: <f> is wild, by balancedness criterion.");
7631      SetOrder(f,infinity); return false;
7632    fi;
7633
7634    if IsSurjective(f) and not IsInjective(f) then
7635      Info(InfoRCWA,3,"IsTame: <f> is surjective and not ",
7636                      "injective, hence wild.");
7637      return false;
7638    fi;
7639
7640    if IsRcwaMappingOfZOrZ_pi(f) and IsBijective(f) then
7641      Info(InfoRCWA,3,"IsTame: sources-and-sinks criterion.");
7642      gamma := TransitionGraph(f,Modulus(f));
7643      for r in [1..Modulus(f)] do RemoveSet(gamma.adjacencies[r],r); od;
7644      delta := UnderlyingGraph(gamma);
7645      C := ConnectedComponents(delta);
7646      if Position(List(C,V->Diameter(InducedSubgraph(gamma,V))),-1) <> fail
7647      then
7648        Info(InfoRCWA,3,"IsTame: <f> is wild, ",
7649                        "by sources-and-sinks criterion.");
7650        SetOrder(f,infinity); return false;
7651      fi;
7652    fi;
7653
7654    if IsBijective(f) then
7655      Info(InfoRCWA,3,"IsTame: loop criterion.");
7656      m := Modulus(f);
7657      if IsRcwaMappingOfZ(f) then
7658        starttime := Runtime();
7659        k := 1; pow := f;
7660        repeat
7661          if Loops(pow) <> [] then
7662            Info(InfoRCWA,3,"IsTame: <f>^",k," has loops, thus <f> ",
7663                            "is wild by loop criterion.");
7664            SetOrder(f,infinity); return false;
7665          fi;
7666          if IsIntegral(pow) then
7667            Info(InfoRCWA,3,"IsTame: <f>^",k," is integral, ",
7668                            "thus <f> is tame.");
7669            return true;
7670          fi;
7671          if   Mod(pow) < m or LogInt(Int(Mod(pow)/m),2) < k/6 - 1
7672          then break; fi;
7673          if Runtime() - starttime > 10 * m then break; fi;
7674          starttime := Runtime();
7675          k := k + 1; pow := pow * f;
7676        until false;
7677      else
7678        for cl in AllResidueClassesModulo(Source(f),m) do
7679          img := cl^f;
7680          if img <> cl and Intersection(cl,img) <> [] then
7681            Info(InfoRCWA,3,"IsTame: <f> is wild, by loop criterion.");
7682            SetOrder(f,infinity); return false;
7683          fi;
7684        od;
7685      fi;
7686    fi;
7687
7688    Info(InfoRCWA,3,"IsTame: `finite order or integral power' criterion.");
7689    pow := f;
7690    exp := [2,2,3,5,2,7,3,2,11,13,5,3,17,2,19,2,2,3,5,7,11,2,23]; e := 1;
7691    for e in exp do
7692      pow := pow^e;
7693      if IsIntegral(pow) then
7694        Info(InfoRCWA,3,"IsTame: <f> has a power which is integral, ",
7695                        "hence is tame.");
7696        return true;
7697      fi;
7698      if       IsRcwaMappingOfZOrZ_pi(f)
7699           and Modulus(pow) > Minimum(Modulus(f)^2,2^16)
7700        or IsRcwaMappingOfGFqx(f)
7701           and   DegreeOfLaurentPolynomial(Modulus(pow))
7702               > DegreeOfLaurentPolynomial(Modulus(f)) + 2
7703      then break; fi;
7704    od;
7705
7706    if IsBijective(f) and Order(f) <> infinity then
7707      Info(InfoRCWA,3,"IsTame: <f> has finite order, hence is tame.");
7708      return true;
7709    fi;
7710
7711    if HasIsTame(f) then return IsTame(f); fi;
7712
7713    Info(InfoRCWA,3,"IsTame: Giving up.");
7714    TryNextMethod();
7715
7716  end );
7717
7718#############################################################################
7719##
7720#M  RespectedPartition( <sigma> ) . . . . .  for tame bijective rcwa mappings
7721##
7722InstallMethod( RespectedPartition,
7723               "for tame bijective rcwa mappings (RCWA)", true,
7724               [ IsRcwaMapping ], 0,
7725
7726  function ( sigma )
7727    if not IsBijective(sigma) then return fail; fi;
7728    return RespectedPartition( Group( sigma ) );
7729  end );
7730
7731#############################################################################
7732##
7733#M  RespectsPartition( <sigma>, <P> ) . .  for rcwa mappings in standard rep.
7734##
7735InstallMethod( RespectsPartition,
7736               "for rcwa mappings in standard rep. (RCWA)",
7737               ReturnTrue, [ IsRcwaMappingInStandardRep, IsList ], 0,
7738
7739  function ( sigma, P )
7740
7741    local  R, cl, c, c_rest, pos, res, r, m;
7742
7743    R := Source(sigma);
7744    if   not ForAll(P,cl->IsResidueClass(cl) and IsSubset(R,cl))
7745      or Union(P) <> R or Sum(List(P,Density)) <> 1
7746    then TryNextMethod(); fi;
7747    if Permutation(sigma,P) = fail then return false; fi;
7748    c   := Coefficients(sigma);
7749    res := AllResidues(R,Modulus(sigma));
7750    for cl in P do
7751      r      := Residue(cl);
7752      m      := Modulus(cl);
7753      pos    := Filtered([1..Length(res)],i->res[i] mod m = r);
7754      c_rest := c{pos};
7755      if Length(Set(c_rest)) > 1 then return false; fi;
7756    od;
7757    return true;
7758  end );
7759
7760#############################################################################
7761##
7762#M  RespectsPartition( <sigma>, <P> ) . for rcwa mappings of Z in sparse rep.
7763##
7764InstallMethod( RespectsPartition,
7765               "for rcwa mappings of Z in sparse rep. (RCWA)",
7766               ReturnTrue, [ IsRcwaMappingOfZInSparseRep, IsList ], 0,
7767
7768  function ( sigma, P )
7769
7770    if   not ForAll(P,cl->IsResidueClass(cl) and IsSubset(Integers,cl))
7771      or Union(P) <> Integers or Sum(List(P,Density)) <> 1
7772    then TryNextMethod(); fi;
7773
7774    if Permutation(sigma,P) = fail then return false; fi;
7775
7776    return ForAll(P,cl->Length(Set(Filtered(sigma!.coeffs,
7777                  c -> (c[1]-Residue(cl)) mod Gcd(c[2],Modulus(cl)) = 0),
7778                  d -> d{[3..5]})) = 1);
7779  end );
7780
7781#############################################################################
7782##
7783#M  PermutationOpNC( <sigma>, <P>, <act> ) . .  for rcwa map. and resp. part.
7784##
7785##  This method serves only as a placeholder for something better.
7786##
7787InstallMethod( PermutationOpNC,
7788               "for an rcwa mapping and a respected partition (RCWA)",
7789               ReturnTrue, [ IsRcwaMapping, IsList, IsFunction ], 0,
7790
7791  function ( sigma, P, act )
7792    return PermutationOp(sigma,P,act);
7793  end );
7794
7795#############################################################################
7796##
7797#M  PermutationOpNC( <sigma>, <P>, <act> ) for rcwa map. of Z and resp. part.
7798##
7799InstallMethod( PermutationOpNC,
7800               "for an rcwa mapping of Z and a respected partition (RCWA)",
7801               ReturnTrue, [ IsRcwaMappingOfZInSparseRep,
7802                             IsList, IsFunction ], 0,
7803
7804  function ( sigma, P, act )
7805
7806    local  cls, imgs, coeffs, conj, cl, img, c, i;
7807
7808    if Length(P) = 1 then TryNextMethod(); fi;
7809    cls    := List(P,cl->[cl!.r[1],cl!.m]);
7810    coeffs := sigma!.coeffs;
7811    conj   := SortingPerm(cls)^-1;
7812    cls    := Set(cls);
7813    imgs   := [];
7814    for i in [1..Length(cls)] do
7815      cl      := cls[i];
7816      c       := First(coeffs,c->cl[1] mod c[2] = c[1]);
7817      img     := [(c[3]*cl[1]+c[4])/c[5],c[3]*cl[2]/c[5]];
7818      img[1]  := img[1] mod img[2];
7819      imgs[i] := PositionSorted(cls,img);
7820    od;
7821    return PermList(imgs)^conj;
7822  end );
7823
7824#############################################################################
7825##
7826#M  PermutationOpNC( <sigma>, <P>, <act> ) for rcwa map. of Z and resp. part.
7827##
7828InstallMethod( PermutationOpNC,
7829               "for an rcwa mapping of Z and a respected partition (RCWA)",
7830               ReturnTrue, [ IsRcwaMappingOfZInStandardRep,
7831                             IsList, IsFunction ], 0,
7832
7833  function ( sigma, P, act )
7834
7835    local  cls, imgs, m, coeffs, conj, cl, img, c, i;
7836
7837    if Length(P) = 1 then TryNextMethod(); fi;
7838    cls    := List(P,cl->[cl!.r[1],cl!.m]);
7839    m      := sigma!.modulus;
7840    coeffs := sigma!.coeffs;
7841    conj   := SortingPerm(cls)^-1;
7842    cls    := Set(cls);
7843    imgs   := [];
7844    for i in [1..Length(cls)] do
7845      cl      := cls[i];
7846      c       := coeffs[cl[1] mod m + 1];
7847      img     := [(c[1]*cl[1]+c[2])/c[3],c[1]*cl[2]/c[3]];
7848      img[1]  := img[1] mod img[2];
7849      imgs[i] := PositionSorted(cls,img);
7850    od;
7851    return PermList(imgs)^conj;
7852  end );
7853
7854#############################################################################
7855##
7856#M  Permuted( <l>, <perm> ) . . . . . . . . . . . . . . . . . fallback method
7857##
7858##  This method is used in particular in the case that <perm> is an rcwa
7859##  permutation and <l> is a respected partition of <perm>.
7860##
7861InstallOtherMethod( Permuted,
7862                    "fallback method (RCWA)", ReturnTrue,
7863                    [ IsList, IsMapping ], 0,
7864
7865  function ( l, perm )
7866    return Permuted( l, Permutation( perm, l ) );
7867  end );
7868
7869#############################################################################
7870##
7871#M  CompatibleConjugate( <g>, <h> ) . . . . . . .  for two rcwa mappings of Z
7872##
7873InstallMethod( CompatibleConjugate,
7874               "for two rcwa mappings of Z (RCWA)", true,
7875               [ IsRcwaMappingOfZ, IsRcwaMappingOfZ ], 0,
7876
7877  function ( g, h )
7878
7879    local DividedPartition, Pg, Ph, PgNew, PhNew, lg, lh, l, tg, th,
7880          remg, remh, cycg, cych, sigma, c, m, i, r;
7881
7882    DividedPartition := function ( P, g, t )
7883
7884      local  PNew, rem, cyc, m, r;
7885
7886      PNew := []; rem := P;
7887      while rem <> [] do
7888        cyc := Cycle(g,rem[1]);
7889        rem := Difference(rem,cyc);
7890        m := Modulus(cyc[1]); r := Residues(cyc[1])[1];
7891        PNew := Union(PNew,
7892                      Flat(List([0..t-1],
7893                                k->Cycle(g,ResidueClass(Integers,
7894                                                        t*m,k*m+r)))));
7895      od;
7896      return PNew;
7897    end;
7898
7899    if   not ForAll([g,h],f->IsBijective(f) and IsTame(f))
7900    then return fail; fi;
7901    Pg := RespectedPartition(g); Ph := RespectedPartition(h);
7902    lg := Length(Pg); lh := Length(Ph);
7903    l := Lcm(lg,lh); tg := l/lg; th := l/lh;
7904    PgNew := DividedPartition(Pg,g,tg); PhNew := DividedPartition(Ph,h,th);
7905    c := []; m := Lcm(List(PhNew,Modulus));
7906    for i in [1..l] do
7907      for r in Filtered([0..m-1],s->s mod Modulus(PhNew[i])
7908                                        = Residues(PhNew[i])[1]) do
7909        c[r+1] := [  Modulus(PgNew[i]),
7910                     Modulus(PhNew[i])*Residues(PgNew[i])[1]
7911                   - Modulus(PgNew[i])*Residues(PhNew[i])[1],
7912                     Modulus(PhNew[i]) ];
7913      od;
7914    od;
7915    sigma := RcwaMapping(c);
7916    if   IsRcwaMappingSparseRep(g) and IsRcwaMappingSparseRep(h)
7917    then sigma := SparseRep(sigma); fi;
7918    return h^sigma;
7919  end );
7920
7921#############################################################################
7922##
7923#S  Computing the order of an rcwa permutation. /////////////////////////////
7924##
7925#############################################################################
7926
7927#############################################################################
7928##
7929#M  Order( <g> ) . . . . . . . . . . . . . . . .  for bijective rcwa mappings
7930##
7931InstallMethod( Order,
7932               "for bijective rcwa mappings (RCWA)",
7933               true, [ IsRcwaMapping ], 0,
7934
7935  function ( g )
7936
7937    local  P, k, p, gtilde, e, e_old, e_max, l, l_max, stabiter,
7938           loopcheckbound, n0, n, b1, b2, m1, m2, r, cycs, pow, exp, c, i;
7939
7940    if   not IsBijective(g)
7941    then Error("Order: <rcwa mapping> must be bijective"); fi;
7942
7943    Info(InfoRCWA,3,"`Order' for an rcwa permutation <g> of ",
7944                    RingToString(Source(g)),".");
7945
7946    if IsOne(g) then return 1; fi;
7947
7948    if HasIsTame(g) and not IsTame(g) then
7949      Info(InfoRCWA,3,"Order: <g> is wild, hence has infinite order.");
7950      return infinity;
7951    fi;
7952
7953    if IsRcwaMappingOfZ(g) then
7954      if IsClassWiseOrderPreserving(g) and Determinant(g) <> 0 then
7955        Info(InfoRCWA,3,"Order: <g> is class-wise order-preserving, ",
7956                        "but not in ker det.");
7957        Info(InfoRCWA,3,"       Hence <g> has infinite order.");
7958        return infinity;
7959      fi;
7960      if not IsIntegral(g) then
7961        loopcheckbound := ValueOption("loopcheckbound");
7962        if loopcheckbound = fail then
7963          loopcheckbound := RootInt(Mod(g),2) + 5; # necessary AT LEAST
7964        fi;
7965        pow := g;
7966        for k in [1..loopcheckbound] do
7967          if Loops(pow) <> [] then
7968            Info(InfoRCWA,3,"Order: <g>^",k," has loops, hence <g> ",
7969                            "has infinite order.");
7970            SetIsTame(g,false);
7971            return infinity;
7972          fi;
7973          if k < loopcheckbound then pow := pow * g; fi;
7974          if IsIntegral(pow) then break; fi;
7975        od;
7976      fi;
7977    fi;
7978
7979    if not IsBalanced(g) then
7980      Info(InfoRCWA,3,"Order: <g> has infinite order ",
7981                      "by the balancedness criterion.");
7982      SetIsTame(g,false); return infinity;
7983    fi;
7984
7985    if IsRcwaMappingOfZOrZ_piInStandardRep(g) then
7986
7987      m1 := Mod(g); pow := g;
7988      exp := [2,2,3,5,2,7,3,2,11,13,5,3,17,19,2];
7989      for e in exp do
7990        c := Coefficients(pow); m2 := Modulus(pow);
7991        if m2 > 6 * m1 then break; fi;
7992        r := First([1..m2],i -> c[i] <> [1,0,1] and c[i]{[1,3]} = [1,1]
7993                            and c[i][2] mod m2 = 0);
7994        if r <> fail then
7995          Info(InfoRCWA,3,"Order: <g> has infinite order ",
7996                          "by the arithmetic progression criterion.");
7997          return infinity;
7998        fi;
7999        pow := pow^e; if IsOne(pow) then break; fi;
8000        if Loops(pow) <> [] then
8001          Info(InfoRCWA,3,"Order: <g>^",e," has loops, ",
8002                          "thus <g> has infinite order.");
8003          SetIsTame(g,false);
8004          return infinity;
8005        fi;
8006      od;
8007
8008      Info(InfoRCWA,3,"Order: Looking for finite cycles ... ");
8009
8010      e := 1; l_max := 2 * Mod(g)^2; e_max := 2^Mod(g); stabiter := 0;
8011      b1 := 2^64-1; b2 := b1^2;
8012      repeat
8013        n0 := Random(-b1,b1); n := n0; l := 0;
8014        repeat
8015          n := n^g;
8016          l := l + 1;
8017        until n = n0 or AbsInt(n) > b2 or l > l_max;
8018        if n = n0 then
8019          e_old := e; e := Lcm(e,l);
8020          if e > e_old then stabiter := 0; else stabiter := stabiter + 1; fi;
8021        else break; fi;
8022      until stabiter = 64 or e > e_max;
8023
8024      if e <= e_max and stabiter = 64 then
8025        c := Reversed(CoefficientsQadic(e,2)); pow := g;
8026        for i in [2..Length(c)] do
8027          pow := pow^2;
8028          if Mod(pow) > Mod(g)^2 then break; fi;
8029          if c[i] = 1 then pow := pow * g; fi;
8030          if Mod(pow) > Mod(g)^2 then break; fi;
8031        od;
8032        if IsOne(pow) then SetIsTame(g,true); return e; fi;
8033      fi;
8034
8035    else # for rcwa permutations of rings other than Z or Z_(pi)
8036
8037      cycs := ShortCycles(g,AllResidues(Source(g),Modulus(g)),
8038                            NumberOfResidues(Source(g),Modulus(g)));
8039      if cycs <> [] then
8040        e := Lcm(List(cycs,Length));
8041        pow := g^e;
8042        if IsIntegral(pow) then SetIsTame(g,true); fi;
8043        if IsOne(pow) then SetIsTame(g,true); return e; fi;
8044      fi;
8045
8046    fi;
8047
8048    if IsRcwaMappingOfZxZ(g) then TryNextMethod(); fi;
8049
8050    if not IsRcwaMappingOfZ(g) and not IsTame(g) then
8051      Info(InfoRCWA,3,"Order: <g> is wild, thus has infinite order.");
8052      return infinity;
8053    fi;
8054
8055    Info(InfoRCWA,3,"Order: determine a respected partition <P> of <g>,");
8056    Info(InfoRCWA,3,"       and compute the order <k> of the permutation");
8057    Info(InfoRCWA,3,"       induced by <g> on <P> as well as the order");
8058    Info(InfoRCWA,3,"       of <g>^<k>.");
8059
8060    P := RespectedPartition(g);
8061
8062    k      := Order(Permutation(g,P));
8063    gtilde := g^k;
8064
8065    if IsOne(gtilde) then return k; fi;
8066
8067    if IsRcwaMappingOfZOrZ_pi(g) then
8068      if   not IsClassWiseOrderPreserving(gtilde) and IsOne(gtilde^2)
8069      then return 2*k; else return infinity; fi;
8070    fi;
8071
8072    if IsRcwaMappingOfGFqxInStandardRep(g) then
8073      e := Lcm(List(Coefficients(gtilde),c->Order(c[1])));
8074      gtilde := gtilde^e;
8075      if IsOne(gtilde) then return k * e; fi;
8076      p := Characteristic(Source(g));
8077      gtilde := gtilde^p;
8078      if IsOne(gtilde) then return k * e * p; fi;
8079    fi;
8080
8081    if IsRcwaMappingOfGFqxInSparseRep(g) then
8082      e := Lcm(List(Coefficients(gtilde),c->Order(c[3])));
8083      gtilde := gtilde^e;
8084      if IsOne(gtilde) then return k * e; fi;
8085      p := Characteristic(Source(g));
8086      gtilde := gtilde^p;
8087      if IsOne(gtilde) then return k * e * p; fi;
8088    fi;
8089
8090    if IsRcwaMappingOfZxZInStandardRep(g) then
8091      if ForAny(Coefficients(gtilde),c->Order(c[1])=infinity) then
8092        return infinity;
8093      else
8094        e := Lcm(List(Coefficients(gtilde),c->Order(c[1])));
8095        gtilde := gtilde^e;
8096        if IsOne(gtilde) then return k * e; else return infinity; fi;
8097      fi;
8098    fi;
8099
8100    Info(InfoRCWA,3,"Order: Giving up.");
8101    TryNextMethod();
8102
8103  end );
8104
8105#############################################################################
8106##
8107#M  Order( <g> ) . . . . . . . . . . . . . . . . . . .  for elements of CT(Z)
8108##
8109InstallMethod( Order,
8110               "for elements of CT(Z) (RCWA)", true, [ IsRcwaMappingOfZ ], 5,
8111
8112  function ( g )
8113
8114    local  cycs, density, lastdensity, pow, partsbound, modbound, lngbound, n;
8115
8116    if   not IsRcwaMappingOfZ(g) or not IsBijective(g)
8117      or not IsSignPreserving(g) or ValueOption("new_order") <> true
8118    then TryNextMethod(); fi;
8119
8120    if IsOne(g) then return 1; fi;
8121    if IsIntegral(g) then
8122      SetIsTame(g,true);
8123      return Lcm(List(Cycles(g,[0..Mod(g)-1]),Length));
8124    fi;
8125    if Loops(g) <> [] then
8126      SetIsTame(g,false); return infinity;
8127    fi;
8128
8129    if not IsRcwaMappingInSparseRep(g) then g := SparseRep(g); fi;
8130
8131    n := 1; pow := g; lastdensity := 0;
8132    partsbound := 16; modbound := 32; lngbound := 16;
8133    repeat
8134      n := n + 1;
8135      pow := pow * g;
8136      if IsOne(pow) then SetIsTame(g,true); return n; fi;
8137      if Loops(pow) <> [] then SetIsTame(g,false); return infinity; fi;
8138      if Length(Coefficients(pow)) > partsbound
8139        or n mod (RootInt(Mod(g),2) + 5) = 0
8140      then
8141        cycs := ShortResidueClassCycles(g,modbound,lngbound);
8142        density := Sum(Flat(cycs),Density);
8143        if density = 1 then
8144          SetIsTame(g,true);
8145          return Lcm(List(cycs,Length));
8146        fi;
8147        if density > lastdensity then
8148          partsbound := partsbound * 2;
8149          modbound := modbound * Maximum(PrimeSet(g));
8150        else
8151          partsbound := 4 * Length(Coefficients(pow));
8152          modbound := modbound * Minimum(PrimeSet(g));
8153        fi;
8154        lngbound := lngbound * 2;
8155        lastdensity := density;
8156      fi;
8157    until false;
8158
8159  end );
8160
8161#############################################################################
8162##
8163#S  Transition matrices and transition graphs. //////////////////////////////
8164##
8165#############################################################################
8166
8167#############################################################################
8168##
8169#M  TransitionMatrix( <f>, <m> ) .  for rcwa mapping and nonzero ring element
8170##
8171InstallMethod( TransitionMatrix,
8172               "for an rcwa mapping and an element<>0 of its source (RCWA)",
8173               ReturnTrue, [ IsRcwaMapping, IsRingElement ], 0,
8174
8175  function ( f, m )
8176
8177    local  T, R, mTest, Resm, ResmTest, n, i, j;
8178
8179    if IsZero(m) or not m in Source(f) then
8180      Error("usage: TransitionMatrix( <f>, <m> ),\nwhere <f> is an ",
8181            "rcwa mapping and <m> <> 0 lies in the source of <f>.\n");
8182    fi;
8183    R := Source(f); Resm := AllResidues(R,m);
8184    mTest := Modulus(f) * Lcm(m,Divisor(f));
8185    ResmTest := AllResidues(R,mTest);
8186    T := NullMat(Length(Resm),Length(Resm));
8187    for n in ResmTest do
8188      i := Position(Resm,n   mod m);
8189      j := Position(Resm,n^f mod m);
8190      T[i][j] := T[i][j] + 1;
8191    od;
8192    return List(T,l->l/Sum(l));
8193  end );
8194
8195#############################################################################
8196##
8197#M  TransitionMatrix( <g> ) .  for rcwa permutations of Z, single-arg. method
8198##
8199InstallMethod( TransitionMatrix,
8200               "for rcwa permutations of Z, single-argument method (RCWA)",
8201               true, [ IsRcwaMappingOfZ and IsBijective ], 0,
8202
8203  function ( g )
8204
8205    local  cls, img, pre, int, M, d, i, j;
8206
8207    if not IsRcwaMappingOfZInSparseRep(g) then g := SparseRep(g); fi;
8208    cls := List(Filtered(Coefficients(g),c->c{[3..5]}<>[1,0,1]),
8209                c->ResidueClass(c[1],c[2]));
8210    d := Length(cls);
8211    M := NullMat(d,d);
8212    for i in [1..d] do
8213      img := cls[i]^g;
8214      for j in [1..d] do
8215        int := Intersection(img,cls[j]);
8216        if int <> [] then
8217          pre := int^(g^-1);
8218          M[i][j] := Density(pre)/Density(cls[i]);
8219        fi;
8220      od;
8221    od;
8222    return M;
8223  end );
8224
8225#############################################################################
8226##
8227#F  TransitionSets( <f>, <m> ) . . . . . . . . . . . .  set transition matrix
8228##
8229InstallGlobalFunction( TransitionSets,
8230
8231  function ( f, m )
8232
8233    local  M, R, res, cl, im, r, i, j;
8234
8235    R   := Source(f);
8236    res := AllResidues(R,m);
8237    cl  := List(res,r->ResidueClass(R,m,r));
8238    M   := [];
8239    for i in [1..Length(res)] do
8240      im   := cl[i]^f;
8241      M[i] := List([1..Length(res)],j->Intersection(im,cl[j]));
8242    od;
8243    return M;
8244  end );
8245
8246#############################################################################
8247##
8248#M  TransitionGraph( <f>, <m> ) . . . . . .  for rcwa mappings of Z or Z_(pi)
8249##
8250##  Returns the transition graph of <f> for modulus <m> as a GRAPE graph.
8251##
8252##  The vertices are labelled by 1..<m> instead of 0..<m>-1 (0 is identified
8253##  with 1, etc.) because in {\GAP}, permutations cannot move 0.
8254##
8255InstallMethod( TransitionGraph,
8256               "for rcwa mappings of Z or Z_(pi) (RCWA)",
8257               true, [ IsRcwaMappingOfZOrZ_pi, IsPosInt ], 0,
8258
8259  function ( f, m )
8260
8261    local  M;
8262
8263    M := TransitionMatrix(f,m);
8264    return Graph(Group(()), [1..m], OnPoints,
8265                 function(i,j) return M[i][j] <> 0; end, true);
8266  end );
8267
8268#############################################################################
8269##
8270#M  TransitionGraph( <f> )  .  for rcwa mappings of Z, single-argument method
8271##
8272InstallMethod( TransitionGraph,
8273               "for rcwa mappings of Z, single-argument method (RCWA)",
8274               true, [ IsRcwaMappingOfZ ], 0,
8275
8276  function ( f )
8277
8278    local  M, m;
8279
8280    M := TransitionMatrix(f); m := Length(M);
8281    return Graph(Group(()), [1..m], OnPoints,
8282                 function(i,j) return M[i][j] <> 0; end, true);
8283  end );
8284
8285#############################################################################
8286##
8287#M  OrbitsModulo( <f>, <m> ) . . . . . . . . for rcwa mappings of Z or Z_(pi)
8288##
8289InstallMethod( OrbitsModulo,
8290               "for rcwa mappings of Z or Z_(pi) (RCWA)", true,
8291               [ IsRcwaMappingOfZOrZ_pi, IsPosInt ], 0,
8292
8293  function ( f, m )
8294
8295    local  gamma, delta, C, r;
8296
8297    gamma := TransitionGraph(f,m);
8298    for r in [1..m] do RemoveSet(gamma.adjacencies[r],r); od;
8299    delta := UnderlyingGraph(gamma);
8300    C := ConnectedComponents(delta);
8301    return Set(List(C,c->List(c,r->r-1)));
8302  end );
8303
8304#############################################################################
8305##
8306#M  FactorizationOnConnectedComponents( <f>, <m> )
8307##
8308InstallMethod( FactorizationOnConnectedComponents,
8309               "for rcwa mappings of Z or Z_(pi) (RCWA)", true,
8310               [ IsRcwaMappingOfZOrZ_pi, IsPosInt ], 0,
8311
8312  function ( f, m )
8313
8314    local  factors, c, comps, comp, coeff, m_f, m_res, r;
8315
8316    if not IsRcwaMappingStandardRep(f) then f := StandardRep(f); fi;
8317    c := Coefficients(f);
8318    comps := OrbitsModulo(f,m);
8319    m_f := Modulus(f); m_res := Lcm(m,m_f);
8320    factors := [];
8321    for comp in comps do
8322      coeff := List([1..m_res],i->[1,0,1]);
8323      for r in [0..m_res-1] do
8324        if r mod m in comp then coeff[r+1] := c[r mod m_f + 1]; fi;
8325      od;
8326      Add(factors,RcwaMapping(coeff));
8327    od;
8328    return Set(Filtered(factors,f->not IsOne(f)));
8329  end );
8330
8331############################################################################
8332##
8333#M  Sources( <f> ) . . . . . . . . . . . . . . . . . . . . for rcwa mappings
8334##
8335InstallMethod( Sources,
8336               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
8337
8338  function ( f )
8339
8340    local  sources, comps, adj, res;
8341
8342    res   := AllResidues(Source(f),Modulus(f));
8343    adj   := TransitionGraph(f,Modulus(f)).adjacencies;
8344    comps := List(STRONGLY_CONNECTED_COMPONENTS_DIGRAPH(adj),
8345                  comp->ResidueClassUnion(Source(f),Modulus(f),res{comp}));
8346    sources := Filtered(comps,comp ->    IsSubset(comp,PreImagesSet(f,comp))
8347                                     and IsSubset(comp^f,comp)
8348                                     and comp^f <> comp);
8349    return sources;
8350  end );
8351
8352############################################################################
8353##
8354#M  Sinks( <f> ) . . . . . . . . . . . . . . . . . . . . . for rcwa mappings
8355##
8356InstallMethod( Sinks,
8357               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
8358
8359  function ( f )
8360
8361    local  sinks, comps, adj, res;
8362
8363    res   := AllResidues(Source(f),Modulus(f));
8364    adj   := TransitionGraph(f,Modulus(f)).adjacencies;
8365    comps := List(STRONGLY_CONNECTED_COMPONENTS_DIGRAPH(adj),
8366                  comp->ResidueClassUnion(Source(f),Modulus(f),res{comp}));
8367    sinks := Filtered(comps,comp ->    IsSubset(PreImagesSet(f,comp),comp)
8368                                   and IsSubset(comp,comp^f)
8369                                   and comp <> comp^f);
8370    return sinks;
8371  end );
8372
8373############################################################################
8374##
8375#M  Loops( <f> ) . . . . . . . . . . . . . . . . . . . . . for rcwa mappings
8376##
8377InstallMethod( Loops,
8378               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
8379
8380  function ( f )
8381
8382    local  cls, cl, img, loops;
8383
8384    cls   := AllResidueClassesModulo(Source(f),Modulus(f));
8385    loops := [];
8386    for cl in cls do
8387      img := cl^f;
8388      if img <> cl and Intersection(img,cl) <> [] then Add(loops,cl); fi;
8389    od;
8390    return loops;
8391  end );
8392
8393############################################################################
8394##
8395#M  Loops( <f> ) . . . . . for bijective rcwa mappings of Z in standard rep.
8396##
8397InstallMethod( Loops,
8398               "for bijective rcwa mappings of Z in standard rep. (RCWA)",
8399               true, [ IsRcwaMappingOfZInStandardRep and IsBijective ], 0,
8400
8401  function ( f )
8402
8403    local  c, i, r, m, r_img, m_img, loops;
8404
8405    m     := Mod(f);
8406    c     := Coefficients(f);
8407    loops := [];
8408    for r in [0..Mod(f)-1] do
8409      m_img := AbsInt(c[r+1][1]/c[r+1][3])*m;
8410      r_img := ((c[r+1][1]*r+c[r+1][2])/c[r+1][3]) mod m_img;
8411      if   [r_img,m_img] <> [r,m] and (r_img - r) mod Gcd(m,m_img) = 0
8412      then Add(loops,ResidueClass(r,m)); fi;
8413    od;
8414    if loops <> [] then SetIsTame(f,false); fi;
8415    return loops;
8416  end );
8417
8418############################################################################
8419##
8420#M  Loops( <f> ) . . . . . . for bijective rcwa mappings of Z in sparse rep.
8421##
8422InstallMethod( Loops,
8423               "for bijective rcwa mappings of Z in standard rep. (RCWA)",
8424               true, [ IsRcwaMappingOfZInSparseRep and IsBijective ], 0,
8425
8426  function ( f )
8427
8428    local  coeffs, modulus, c, r, m, r_img, m_img, loops;
8429
8430    modulus := Mod(f);
8431    coeffs  := f!.coeffs;
8432    loops   := [];
8433    for c in coeffs do
8434      r := c[1]; m := c[2];
8435      m_img := AbsInt(c[3]*c[2]/c[5]);
8436      r_img := ((c[3]*r+c[4])/c[5]) mod m_img;
8437      if    [r_img,m_img] <> [r,m]
8438        and (r_img - r) mod Gcd(m,m_img) = 0
8439      then Add(loops,ResidueClass(r,m)); fi;
8440    od;
8441    if loops <> [] then SetIsTame(f,false); fi;
8442    return loops;
8443  end );
8444
8445############################################################################
8446##
8447#M  Loops( <f> ) . . . . . . . . . . for bijective rcwa mappings of GF(q)[x]
8448##
8449InstallMethod( Loops,
8450               "for bijective rcwa mappings of GF(q)[x] (RCWA)", true,
8451               [ IsRcwaMappingOfGFqxInStandardRep and IsBijective ], 0,
8452
8453  function ( f )
8454
8455    local  R, m, c, res, r, r_img, m_img, loops, i;
8456
8457    R     := Source(f);
8458    m     := Mod(f);
8459    c     := Coefficients(f);
8460    res   := AllResidues(R,m);
8461    loops := [];
8462    for i in [1..Length(res)] do
8463      r     := res[i];
8464      m_img := (c[i][1]/c[i][3])*m;
8465      r_img := ((c[i][1]*r+c[i][2])/c[i][3]) mod m_img;
8466      if   [r_img,m_img] <> [r,m] and IsZero((r_img - r) mod Gcd(R,m,m_img))
8467      then Add(loops,ResidueClass(R,m,r)); fi;
8468    od;
8469    if loops <> [] then SetIsTame(f,false); fi;
8470    return loops;
8471  end );
8472
8473#############################################################################
8474##
8475#S  Trajectories. ///////////////////////////////////////////////////////////
8476##
8477#############################################################################
8478
8479#############################################################################
8480##
8481#M  Trajectory( <f>, <n>, <length> )
8482##
8483InstallOtherMethod( Trajectory,
8484                    "for function, start point and length (RCWA)",
8485                    ReturnTrue, [ IsFunction, IsObject, IsPosInt ], 0,
8486
8487  function ( f, n, length )
8488
8489    local  l, i;
8490
8491    l := [n];
8492    for i in [2..length] do
8493      n := f(n);
8494      Add(l,n);
8495    od;
8496    return l;
8497  end );
8498
8499#############################################################################
8500##
8501#M  Trajectory( <f>, <n>, <terminal> )
8502##
8503InstallOtherMethod( Trajectory,
8504                    "for function, start point and terminal set (RCWA)",
8505                    ReturnTrue, [ IsFunction, IsObject, IsListOrCollection ],
8506                    0,
8507
8508  function ( f, n, terminal )
8509
8510    local  l, i;
8511
8512    l := [n];
8513    while not n in terminal do
8514      n := f(n);
8515      Add(l,n);
8516    od;
8517    return l;
8518  end );
8519
8520#############################################################################
8521##
8522#M  Trajectory( <f>, <n>, <halt> )
8523##
8524InstallOtherMethod( Trajectory,
8525                    "for function, start point and halting criterion (RCWA)",
8526                    ReturnTrue, [ IsFunction, IsObject, IsFunction ], 0,
8527
8528  function ( f, n, halt )
8529
8530    local  l, i;
8531
8532    l := [n];
8533    while not halt(n) do
8534      n := f(n);
8535      Add(l,n);
8536    od;
8537    return l;
8538  end );
8539
8540#############################################################################
8541##
8542#M  Trajectory( <f>, <n>, <length> ) . . . . . . . . . for rcwa mappings, (1)
8543##
8544InstallMethod( Trajectory,
8545               "for an rcwa mapping, given number of iterates (RCWA)",
8546               ReturnTrue, [ IsRcwaMapping, IsObject, IsPosInt ], 0,
8547
8548  function ( f, n, length )
8549
8550    local  seq, step, action;
8551
8552    if   not (n in Source(f) or IsSubset(Source(f),n))
8553    then TryNextMethod(); fi;
8554    action := GetOption("Action",OnPoints,IsFunction);
8555    seq := [n];
8556    for step in [1..length-1] do
8557      n := action(n,f);
8558      Add(seq,n);
8559    od;
8560    return seq;
8561  end );
8562
8563#############################################################################
8564##
8565#M  Trajectory( <f>, <n>, <length>, <m> ) . . . . . .  for rcwa mappings, (2)
8566##
8567InstallMethod( Trajectory,
8568               Concatenation("for an rcwa mapping, given number of ",
8569                             "iterates (mod <m>) (RCWA)"), ReturnTrue,
8570               [ IsRcwaMapping, IsObject, IsPosInt, IsRingElement ], 0,
8571
8572  function ( f, n, length, m )
8573
8574    local  seq, step, action;
8575
8576    if   not (n in Source(f) or IsSubset(Source(f),n)) or IsZero(m)
8577    then TryNextMethod(); fi;
8578    action := GetOption("Action",OnPoints,IsFunction);
8579    seq := [n mod m];
8580    for step in [1..length-1] do
8581      n := action(n,f);
8582      Add(seq,n mod m);
8583    od;
8584    return seq;
8585  end );
8586
8587#############################################################################
8588##
8589#M  Trajectory( <f>, <n>, <terminal> ) . . . . . . . . for rcwa mappings, (3)
8590##
8591InstallMethod( Trajectory,
8592               "for an rcwa mapping, until a given set is entered (RCWA)",
8593               ReturnTrue,
8594               [ IsRcwaMapping, IsObject, IsListOrCollection ], 0,
8595
8596  function ( f, n, terminal )
8597
8598    local  seq, action;
8599
8600    if   not (n in Source(f) or IsSubset(Source(f),n))
8601    then TryNextMethod(); fi;
8602    action := GetOption("Action",OnPoints,IsFunction);
8603    seq := [n];
8604    if   IsListOrCollection(n) or not IsListOrCollection(terminal)
8605    then terminal := [terminal]; fi;
8606    while not n in terminal do
8607      n := action(n,f);
8608      Add(seq,n);
8609    od;
8610    return seq;
8611  end );
8612
8613#############################################################################
8614##
8615#M  Trajectory( <f>, <n>, <terminal>, <m> ) . . . . .  for rcwa mappings, (4)
8616##
8617InstallMethod( Trajectory,
8618               Concatenation("for an rcwa mapping, until a given set i",
8619                             "s entered, (mod <m>) (RCWA)"), ReturnTrue,
8620               [ IsRcwaMapping, IsObject,
8621                 IsListOrCollection, IsRingElement ], 0,
8622
8623  function ( f, n, terminal, m )
8624
8625    local  seq, action;
8626
8627    if   not (n in Source(f) or IsSubset(Source(f),n)) or IsZero(m)
8628    then TryNextMethod(); fi;
8629    action := GetOption("Action",OnPoints,IsFunction);
8630    seq := [n mod m];
8631    if   IsListOrCollection(n) or not IsListOrCollection(terminal)
8632    then terminal := [terminal]; fi;
8633    while not n in terminal do
8634      n := action(n,f);
8635      Add(seq,n mod m);
8636    od;
8637    return seq;
8638  end );
8639
8640############################################################################
8641##
8642#M  Trajectory( <f>, <n>, <length>,   <whichcoeffs> ) for rcwa mappings, (5)
8643#M  Trajectory( <f>, <n>, <terminal>, <whichcoeffs> ) for rcwa mappings, (6)
8644##
8645InstallMethod( Trajectory,
8646               "for an rcwa mapping, coefficients (RCWA)", ReturnTrue,
8647               [ IsRcwaMapping, IsRingElement, IsObject, IsString ], 0,
8648
8649  function ( f, n, lngterm, whichcoeffs )
8650
8651    local  coeffs, triple, traj, length, terminal,
8652           c, m, d, pos, res, r, deg, R, q, x;
8653
8654    if not IsRcwaMappingStandardRep(f) then f := StandardRep(f); fi;
8655    if   IsPosInt(lngterm)           then length   := lngterm;
8656    elif IsListOrCollection(lngterm) then terminal := lngterm;
8657    else TryNextMethod(); fi;
8658    if   not n in Source(f) or not whichcoeffs in ["AllCoeffs","LastCoeffs"]
8659    then TryNextMethod(); fi;
8660    c := Coefficients(f); m := Modulus(f);
8661    traj := [n mod m];
8662    if   IsBound(length)
8663    then for pos in [2..length]  do n := n^f; Add(traj,n mod m); od;
8664    else while not n in terminal do n := n^f; Add(traj,n mod m); od; fi;
8665    if IsRcwaMappingOfGFqx(f) then
8666      deg := DegreeOfLaurentPolynomial(m);
8667      R   := Source(f);
8668      q   := Size(CoefficientsRing(R));
8669      x   := IndeterminatesOfPolynomialRing(R)[1];
8670      res := AllGFqPolynomialsModDegree(q,deg,x);
8671    else res := [0..m-1]; fi;
8672    triple := [1,0,1] * One(Source(f)); coeffs := [triple];
8673    for pos in [1..Length(traj)-1] do
8674      r := Position(res,traj[pos]);
8675      triple := [ c[r][1] * triple[1],
8676                  c[r][1] * triple[2] + c[r][2] * triple[3],
8677                  c[r][3] * triple[3] ];
8678      triple := triple/Gcd(triple);
8679      if whichcoeffs = "AllCoeffs" then Add(coeffs,triple); fi;
8680    od;
8681    if whichcoeffs = "AllCoeffs" then return coeffs; else return triple; fi;
8682  end );
8683
8684#############################################################################
8685##
8686#F  GluckTaylorInvariant( <l> ) . .  Gluck-Taylor invariant of trajectory <l>
8687##
8688InstallGlobalFunction( GluckTaylorInvariant,
8689
8690  function ( l )
8691    if not IsList(l) or not ForAll(l,IsInt) then return fail; fi;
8692    return Sum([1..Length(l)],i->l[i]*l[i mod Length(l) + 1])/(l*l);
8693  end );
8694
8695#############################################################################
8696##
8697#F  TraceTrajectoriesOfClasses( <f>, <S>, <maxlength> )
8698##
8699InstallGlobalFunction( TraceTrajectoriesOfClasses,
8700
8701  function ( f, S, maxlength )
8702
8703    local  l, k, starttime, timeout;
8704
8705    l := [[S]]; k := 1;
8706    starttime := Runtime();
8707    timeout := GetOption("timeout",infinity,IsPosInt);
8708    repeat
8709      Add(l,Flat(List(l[k],cl->AsUnionOfFewClasses(cl^f))));
8710      k := k + 1;
8711      Info(InfoRCWA,2,"k = ",k,": ",ViewString(l[k]));
8712    until Length(l) >= maxlength or Runtime() - starttime >= timeout
8713       or l[k] in l{[1..k-1]};
8714    return l;
8715  end );
8716
8717#############################################################################
8718##
8719#S  Probabilistic guesses concerning the behaviour of trajectories. /////////
8720##
8721#############################################################################
8722
8723#############################################################################
8724##
8725#M  GuessedDivergence( <f> ) . . . . . . . . . . . . . . .  for rcwa mappings
8726##
8727InstallMethod( GuessedDivergence,
8728               "for rcwa mappings (RCWA)", true, [ IsRcwaMapping ], 0,
8729
8730  function ( f )
8731
8732    local  R, pow, m, c, M, approx, prev, facts, p, NrRes, exp, eps, prec;
8733
8734    Info(InfoWarning,1,"Warning: GuessedDivergence: no particular return ",
8735                       "value is guaranteed.");
8736    if not IsRcwaMappingStandardRep(f) then f := StandardRep(f); fi;
8737    R := Source(f);
8738    prec := 10^8; eps := Float(1/prec);
8739    pow := f; exp := 1; approx := Float(0);
8740    repeat
8741      m := Modulus(pow); NrRes := Length(AllResidues(R,m));
8742      c := Coefficients(pow);
8743      facts := List(c,t->Float(Length(AllResidues(R,t[1]))/
8744                               Length(AllResidues(R,t[3]))));
8745      Info(InfoRCWA,4,"Factors = ",facts);
8746      M := TransitionMatrix(pow,m);
8747      p := List(TransposedMat(M),l->Float(Sum(l)/NrRes));
8748      Info(InfoRCWA,4,"p = ",p);
8749      prev := approx;
8750      approx := Product(List([1..NrRes],i->facts[i]^p[i]))^Float(1/exp);
8751      Info(InfoRCWA,2,"Approximation = ",approx);
8752      pow := pow * f; exp := exp + 1;
8753    until AbsoluteValue(approx-prev) < eps;
8754    return approx;
8755  end );
8756
8757#############################################################################
8758##
8759#M  LikelyContractionCentre( <f>, <maxn>, <bound> ) .  for rcwa mappings of Z
8760##
8761InstallMethod( LikelyContractionCentre,
8762               "for rcwa mapping of Z and two positive integers (RCWA)",
8763               true, [ IsRcwaMappingOfZ, IsPosInt, IsPosInt ], 0,
8764
8765  function ( f, maxn, bound )
8766
8767    local  ReducedSetOfStartingValues, S0, S, n, n_i, i, seq;
8768
8769    ReducedSetOfStartingValues := function ( S, f, lng )
8770
8771      local  n, min, max, traj;
8772
8773      min := Minimum(S); max := Maximum(S);
8774      for n in [min..max] do
8775        if n in S then
8776          traj := Set(Trajectory(f,n,lng){[2..lng]});
8777          if not n in traj then S := Difference(S,traj); fi;
8778        fi;
8779      od;
8780      return S;
8781    end;
8782
8783    Info(InfoWarning,1,"Warning: `LikelyContractionCentre' is highly ",
8784                       "probabilistic.\nThe returned result can only be ",
8785                       "regarded as a rough guess.\n",
8786                       "See ?LikelyContractionCentre for more information.");
8787    if IsBijective(f) then return fail; fi;
8788    S := ReducedSetOfStartingValues([-maxn..maxn],f,8);
8789    Info(InfoRCWA,1,"#Remaining values to be examined after first ",
8790                    "reduction step: ",Length(S));
8791    S := ReducedSetOfStartingValues(S,f,64);
8792    Info(InfoRCWA,1,"#Remaining values to be examined after second ",
8793                    "reduction step: ",Length(S));
8794    S0 := [];
8795    for n in S do
8796      seq := []; n_i := n;
8797      while AbsInt(n_i) <= bound do
8798        if n_i in S0 then break; fi;
8799        if n_i in seq then
8800          S0 := Union(S0,Cycle(f,n_i));
8801          Info(InfoRCWA,1,"|S0| = ",Length(S0));
8802          break;
8803        fi;
8804        AddSet(seq,n_i);
8805        n_i := n_i^f;
8806        if   AbsInt(n_i) > bound
8807        then Info(InfoRCWA,3,"Given bound exceeded, start value ",n); fi;
8808      od;
8809      if n >= maxn then break; fi;
8810    od;
8811    return S0;
8812  end );
8813
8814#############################################################################
8815##
8816#S  Finite cycles of an rcwa permutation. ///////////////////////////////////
8817##
8818#############################################################################
8819
8820#############################################################################
8821##
8822#M  ShortCycles( <sigma>, <S>, <maxlng> ) for bij. rcwa map., set & pos. int.
8823##
8824InstallMethod( ShortCycles,
8825               Concatenation("for a bijective rcwa mapping, a set and ",
8826                             "a positive integer (RCWA)"),
8827               ReturnTrue,
8828               [ IsRcwaMapping, IsListOrCollection, IsPosInt ], 0,
8829
8830  function ( sigma, S, maxlng )
8831
8832    local  cycs, absvals, minpos, i;
8833
8834    if   not IsBijective(sigma) or not IsSubset(Source(sigma),S)
8835    then TryNextMethod(); fi;
8836    cycs := List(ShortOrbits(Group(sigma),S,maxlng),
8837                 orb->Cycle(sigma,Minimum(orb)));
8838    if IsRcwaMappingOfZ(sigma) then
8839      SortParallel(List(cycs,cyc->AbsInt(cyc[1])),cycs);
8840      for i in [1..Length(cycs)] do
8841        if not ForAll(cycs[i],IsPosInt) then
8842          absvals := List(cycs[i],AbsInt);
8843          minpos  := Position(absvals,Minimum(absvals));
8844          cycs[i] := Concatenation(cycs[i]{[minpos..Length(cycs[i])]},
8845                                   cycs[i]{[1..minpos-1]});
8846        fi;
8847      od;
8848    fi;
8849    return cycs;
8850  end );
8851
8852#############################################################################
8853##
8854#M  ShortCycles( <sigma>, <S>, <maxlng>, <maxn> )
8855##
8856InstallMethod( ShortCycles,
8857               Concatenation("for a bijective rcwa mapping of Z, ",
8858                             "a set and two positive integers (RCWA)"),
8859               ReturnTrue, [ IsRcwaMappingOfZ, IsListOrCollection,
8860                             IsPosInt, IsPosInt ], 0,
8861
8862  function ( sigma, S, maxlng, maxn )
8863
8864    local  cycs, cyc, done, min, max, lng, absvals, minpos, n, m, i, j;
8865
8866    if   not IsBijective(sigma) or not ForAll(S,IsInt)
8867    then TryNextMethod(); fi;
8868
8869    min := Minimum(S); max := Maximum(S);
8870
8871    cycs := [];
8872    done := List(S,n->false);
8873    while false in done do
8874      i := Position(done,false); done[i] := true;
8875      n := S[i]; m := n; cyc := []; lng := 0;
8876      repeat
8877        Add(cyc,m); m := m^sigma; lng := lng + 1;
8878        if m >= min and m <= max then
8879          j := PositionSorted(S,m);
8880          if j <> fail then done[j] := true; fi;
8881        fi;
8882      until m = n or lng >= maxlng or AbsInt(m) > maxn;
8883      if m = n then Add(cycs,cyc); fi;
8884    od;
8885
8886    SortParallel(List(cycs,cyc->AbsInt(cyc[1])),cycs);
8887    if min < 0 then
8888      for i in [1..Length(cycs)] do
8889        if not ForAll(cycs[i],IsPosInt) then
8890          absvals := List(cycs[i],AbsInt);
8891          minpos  := Position(absvals,Minimum(absvals));
8892          cycs[i] := Concatenation(cycs[i]{[minpos..Length(cycs[i])]},
8893                                   cycs[i]{[1..minpos-1]});
8894        fi;
8895      od;
8896    fi;
8897    return cycs;
8898  end );
8899
8900#############################################################################
8901##
8902#M  ShortCycles( <f>, <maxlng> )  for rcwa mapping of Z or Z_(pi) & pos. int.
8903##
8904InstallMethod( ShortCycles,
8905               Concatenation("for an rcwa mapping of Z or Z_(pi) and ",
8906                             "a positive integer (RCWA)"),
8907               ReturnTrue, [ IsRcwaMappingOfZOrZ_pi, IsPosInt ], 0,
8908
8909  function ( f, maxlng )
8910
8911    local  R, cycles, cyclesbuf, cycs, cyc, fp, pow, exp,
8912           m, min, minshift, l, i;
8913
8914    R := Source(f); cycles := []; pow := One(f);
8915    for exp in [1..maxlng] do
8916      pow  := pow * f;
8917      m    := Modulus(pow);
8918      fp   := FixedPointsOfAffinePartialMappings(pow);
8919      cycs := List(Filtered(TransposedMat([AllResidueClassesModulo(R,m),fp]),
8920                            s->IsSubset(s[1],s[2]) and not IsEmpty(s[2])),
8921                   t->t[2]);
8922      cycs := List(cycs,ShallowCopy);
8923      for cyc in cycs do
8924        for i in [1..exp-1] do Add(cyc,cyc[i]^f); od;
8925      od;
8926      cycles := Concatenation(cycles,cycs);
8927    od;
8928    cycles := Filtered(cycles,cyc->Length(Set(cyc))=Length(cyc));
8929    cyclesbuf := ShallowCopy(cycles); cycles := [];
8930    for i in [1..Length(cyclesbuf)] do
8931      if not Set(cyclesbuf[i]) in List(cyclesbuf{[1..i-1]},AsSet) then
8932        cyc := cyclesbuf[i]; l := Length(cyc);
8933        min := Minimum(cyc); minshift := l - Position(cyc,min) + 1;
8934        cyc := Permuted(cyc,SortingPerm(Concatenation([2..l],[1]))^minshift);
8935        Add(cycles,cyc);
8936      fi;
8937    od;
8938    return cycles;
8939  end );
8940
8941#############################################################################
8942##
8943#M  CycleRepresentativesAndLengths( <g>, <S> ) . . . . . . . . default method
8944##
8945InstallMethod( CycleRepresentativesAndLengths,
8946               "default method (RCWA)",
8947               ReturnTrue, [ IsRcwaMapping, IsListOrCollection ], 0,
8948
8949  function ( g, S )
8950
8951    local  replng, rem, cyc, rep, n, i, j;
8952
8953    replng := [];
8954    rem    := List(S,n->n^g<>n);
8955    for i in [1..Length(S)] do
8956      if rem[i] = false then continue; fi;
8957      rep := S[i];
8958      cyc := ComputeCycleLength(g,rep:small:=S);
8959      if not cyc.aborted then Add(replng,[rep,cyc.length]);
8960                         else Add(replng,[rep,fail]); fi;
8961      for n in cyc.smallpoints do rem[PositionSorted(S,n)] := false; od;
8962    od;
8963    return replng;
8964  end );
8965
8966#############################################################################
8967##
8968#F  ComputeCycleLength( <g>, <n> )
8969##
8970InstallGlobalFunction( ComputeCycleLength,
8971
8972  function ( g, n0 )
8973
8974    local  n, steps, max, maxpos, smallpoints, result,
8975           notify, small, abortat, aborted, quiet;
8976
8977    n := n0; steps := 0; max := n; maxpos := 1; smallpoints := [n0];
8978    notify  := ValueOption("notify");
8979    small   := ValueOption("small");
8980    abortat := ValueOption("abortat");
8981    quiet   := ValueOption("quiet") = true;
8982    if   not IsPosInt(notify) and not notify in [0,infinity]
8983    then notify := 10000; fi;
8984    aborted := false;
8985    repeat
8986      n := n^g;
8987      steps := steps + 1;
8988      if n > max then
8989        max    := n;
8990        maxpos := steps;
8991      fi;
8992      if IsList(small) and n in small then Add(smallpoints,n); fi;
8993      if not quiet and not notify in [0,infinity]
8994        and steps mod notify = 0
8995      then
8996        Print("n = ",n0,": after ",steps," steps, the iterate has ",
8997              LogInt(AbsInt(n),2)+1," binary digits.\n");
8998      fi;
8999      if IsPosInt(abortat) and steps >= abortat then
9000        aborted := true; break;
9001      fi;
9002    until n = n0;
9003    result := rec( g       := g,
9004                   n       := n0,
9005                   length  := steps,
9006                   maximum := max,
9007                   maxpos  := maxpos,
9008                   aborted := aborted );
9009    if IsList(small) then result.smallpoints := Set(smallpoints); fi;
9010    return result;
9011  end );
9012
9013#############################################################################
9014##
9015#M  ShortResidueClassCycles( <g>, <modbound>, <maxlng> )
9016##
9017##  Probably now obsolete; superseded by method below.
9018##
9019InstallMethod( ShortResidueClassCycles,
9020               "for an rcwa permutation of Z and 2 positive integers (RCWA)",
9021               ReturnTrue, [ IsRcwaMappingOfZ, IsPosInt, IsPosInt ], 0,
9022
9023  function ( g, modbound, maxlng )
9024
9025    local  cycles, cycle, cycs, lngs, lng, startpts, cycbound,
9026           cl, S, affsrc, divs, c, m, r;
9027
9028    if not IsRcwaMappingStandardRep(g) then g := StandardRep(g); fi;
9029
9030    affsrc := LargestSourcesOfAffineMappings(g);
9031    divs := Difference(DivisorsInt(modbound),[1]);
9032
9033    S := Intersection([0..modbound-1],Support(g));
9034    cycbound := ValueOption("cycbound");
9035    if   cycbound = fail
9036    then Info(InfoRCWA,2,"ShortResidueClassCycles: option cycbound not set");
9037         cycs := ShortCycles(g,S,maxlng);
9038    else cycs := ShortCycles(g,S,maxlng,cycbound); fi;
9039    lngs := Set(List(cycs,Length));
9040
9041    cycles := List(AsUnionOfFewClasses(Difference(Integers,Support(g))),
9042                   cl->[cl]);
9043
9044    for lng in lngs do
9045      Info(InfoRCWA,2,"ShortResidueClassCycles: checking cycle length ",lng);
9046      startpts := List(Filtered(cycs,cyc->Length(cyc)=lng),l->l[1]);
9047      for m in divs do
9048        for r in Filtered(startpts,s->s<m) do
9049          if IsSubset(startpts,[r,r+m..modbound-m+r]) and
9050             not ForAny(cycles,
9051                        cyc->ForAny(cyc,cl->(r-cl!.r[1]) mod Gcd(m,cl!.m)=0))
9052          then
9053            cycle := []; cl := ResidueClass(r,m);
9054            if  m mod Mod(g) <> 0
9055              and Number(affsrc,src->Intersection(src,cl)<>[]) > 1
9056            then continue; fi;
9057            repeat
9058              Add(cycle,cl);
9059              if cl!.m mod g!.modulus <> 0 then cl := cl^g; else
9060                c := g!.coeffs[cl!.r[1] mod g!.modulus + 1];
9061                cl := ResidueClassUnionNC(Integers,cl!.m*c[1]/c[3],
9062                                          [(cl!.r[1]*c[1]+c[2])/c[3]]);
9063              fi;
9064            until not IsResidueClass(cl) or cl = cycle[1]
9065                  or Length(cycle) > maxlng;
9066            if cl = cycle[1] then
9067              Add(cycles,cycle);
9068            fi;
9069          fi;
9070        od;
9071      od;
9072    od;
9073
9074    return cycles;
9075  end );
9076
9077#############################################################################
9078##
9079#M  ShortResidueClassCycles( <g>, <modbound>, <maxlng> )
9080##
9081InstallMethod( ShortResidueClassCycles,
9082               "for an rcwa permutation of Z and 2 positive integers (RCWA)",
9083               ReturnTrue, [ IsRcwaMappingOfZ, IsPosInt, IsPosInt ], 5,
9084
9085  function ( g, modbound, maxlng )
9086
9087    local  cycles, orbits;
9088
9089    orbits := ShortResidueClassOrbits(Group(g),modbound,maxlng);
9090    cycles := Set(List(orbits,orb->Cycle(g,orb[1])));
9091    return cycles;
9092  end );
9093
9094#############################################################################
9095##
9096#M  FixedResidueClasses( <g>, <maxmod> )
9097##
9098InstallMethod( FixedResidueClasses,
9099               "for an rcwa mapping of Z and a positive integer (RCWA)",
9100               ReturnTrue, [ IsRcwaMappingOfZ, IsPosInt ], 0,
9101
9102  function ( g, maxmod )
9103
9104    local  cls, fixed, cl, r, m;
9105
9106    cls := Concatenation(List([2..maxmod],
9107                              m->AllResidueClassesModulo(Integers,m)));
9108    fixed := [];
9109    for cl in cls do
9110      r := Residue(cl); m := Modulus(cl);
9111      if ForAny([r,r+m..r+15*m],n->n^g mod m <> r) then continue; fi;
9112      if cl^g = cl then Add(fixed,cl); fi;
9113    od;
9114    return fixed;
9115  end );
9116
9117#############################################################################
9118##
9119#S  Restriction monomorphisms and induction epimorphisms. ///////////////////
9120##
9121#############################################################################
9122
9123#############################################################################
9124##
9125#M  Restriction( <g>, <f> ) . . . . . . . . . . . . . . . . for rcwa mappings
9126##
9127InstallMethod( Restriction, "for rcwa mappings (RCWA)", IsIdenticalObj,
9128               [ IsRcwaMapping, IsRcwaMapping ], 0,
9129
9130  function ( g, f )
9131
9132    local  gf;
9133
9134    if not IsInjective(f) then return fail; fi;
9135
9136    gf := RestrictedPerm( RightInverse(f) * g * f, Image(f) );
9137
9138    Assert(4,g*f=f*gf,"Restriction: Diagram does not commute.\n");
9139
9140    if HasIsInjective(g)  then SetIsInjective(gf,IsInjective(g)); fi;
9141    if HasIsSurjective(g) then SetIsSurjective(gf,IsSurjective(g)); fi;
9142    if HasIsTame(g)       then SetIsTame(gf,IsTame(g)); fi;
9143    if HasOrder(g)        then SetOrder(gf,Order(g)); fi;
9144
9145    return gf;
9146  end );
9147
9148#############################################################################
9149##
9150#M  Induction( <g>, <f> ) . . . . . . . . . . . . . . . . . for rcwa mappings
9151##
9152InstallMethod( Induction, "for rcwa mappings (RCWA)", IsIdenticalObj,
9153               [ IsRcwaMapping, IsRcwaMapping ], 0,
9154
9155  function ( g, f )
9156
9157    local  gf;
9158
9159    if    not IsInjective(f) or not IsSubset(Image(f),MovedPoints(g))
9160       or not IsSubset(Image(f),MovedPoints(g)^g) then return fail; fi;
9161
9162    gf := f * g * RightInverse(f);
9163
9164    Assert(4,gf*f=f*g,"Induction: Diagram does not commute.\n");
9165
9166    if HasIsInjective(g)  then SetIsInjective(gf,IsInjective(g)); fi;
9167    if HasIsSurjective(g) then SetIsSurjective(gf,IsSurjective(g)); fi;
9168    if HasIsTame(g)       then SetIsTame(gf,IsTame(g)); fi;
9169    if HasOrder(g)        then SetOrder(gf,Order(g)); fi;
9170
9171    return gf;
9172  end );
9173
9174#############################################################################
9175##
9176#S  Extracting roots of rcwa permutations. //////////////////////////////////
9177##
9178#############################################################################
9179
9180#############################################################################
9181##
9182#M  Root( <sigma>, <k> ) . . . . . .  for an element of CT(Z) of finite order
9183##
9184InstallMethod( Root,
9185               "for an element of CT(Z) of finite order (RCWA)",
9186               ReturnTrue, [ IsRcwaMappingOfZ, IsPosInt ], 10,
9187
9188  function ( sigma, k )
9189
9190    local  root, regroot, k_reg, k_sing, order, P, remaining, cycle, l, i, j;
9191
9192    if k = 1 then return sigma; fi;
9193    if not IsClassWiseOrderPreserving(sigma) or not IsTame(sigma)
9194      or Order(sigma) = infinity
9195      or not ForAll(Factorization(sigma),IsClassTransposition)
9196    then TryNextMethod(); fi;
9197    order     := Order(sigma);
9198    k_sing    := Product(Filtered(Factors(k),p->order mod p = 0));
9199    k_reg     := k/k_sing;
9200    regroot   := sigma^(1/k_reg mod order);
9201    if k_sing = 1 then return regroot; fi;
9202    P         := RespectedPartition(regroot);
9203    remaining := ShallowCopy(P);
9204    root      := One(sigma);
9205    repeat
9206      cycle     := Cycle(regroot,remaining[1]);
9207      l         := Length(cycle);
9208      remaining := Difference(remaining,cycle);
9209      cycle     := List(cycle,cl->SplittedClass(cl,k_sing));
9210      for i in [1..l] do
9211        for j in [1..k_sing] do
9212          if [i,j] <> [1,1] then
9213            root := root * ClassTransposition(cycle[1][1],cycle[i][j]);
9214          fi;
9215        od;
9216      od;
9217    until IsEmpty(remaining);
9218    return root;
9219  end );
9220
9221#############################################################################
9222##
9223#M  Root( <sigma>, <k> ) . . .  for a cwop. rcwa mapping of Z of finite order
9224##
9225InstallMethod( Root,
9226               "for a cwop. rcwa mapping of Z of finite order (RCWA)",
9227               ReturnTrue, [ IsRcwaMappingOfZ, IsPosInt ], 0,
9228
9229  function ( sigma, k )
9230
9231    local  root, g, x;
9232
9233    if k = 1 then return sigma; fi;
9234    if IsOne(sigma) then
9235      root := Product([1..k-1],r->ClassTransposition(0,k,r,k));
9236      SetOrder(root,k); return root;
9237    fi;
9238    if not IsClassWiseOrderPreserving(sigma) or not IsTame(sigma)
9239      or Order(sigma) = infinity
9240    then TryNextMethod(); fi;
9241    g    := Product(Filtered(Factorization(sigma),IsClassTransposition));
9242    x    := RepresentativeAction(RCWA(Integers),g,sigma);
9243    root := Root(g,k)^x;
9244    return root;
9245  end );
9246
9247#############################################################################
9248##
9249#S  Factoring an rcwa permutation into class shifts, ////////////////////////
9250#S  class reflections and class transpositions. /////////////////////////////
9251##
9252#############################################################################
9253
9254#############################################################################
9255##
9256#M  CTCSCRSplit( <g> ) . . . . . . . . . . . . . . for rcwa permutations of Z
9257##
9258InstallMethod( CTCSCRSplit,
9259               "for bijective rcwa mappings of Z (RCWA)", true,
9260               [ IsRcwaMappingOfZ ], 0,
9261
9262  function ( g )
9263
9264    local  coeffs, rev, reverts, revert, shifts, shift,
9265           cl, img, c, r, m, e, stdrep, listfactors;
9266
9267    if not IsBijective(g) then TryNextMethod(); fi;
9268    listfactors := ValueOption("ListFactors");
9269    stdrep := IsRcwaMappingOfZInStandardRep(g);
9270    g := SparseRep(g);
9271    if not IsClassWiseOrderPreserving(g) then
9272       rev     := ClassWiseOrderReversingOn(g);
9273       reverts := List(AsUnionOfFewClasses(rev^g),
9274                       cl->SparseRep(ClassReflection(cl)));
9275       revert  := Product(reverts);
9276       g := g * revert^-1;
9277    else reverts := []; revert := One(g); fi;
9278    shifts := [];
9279    coeffs := Coefficients(g);
9280    for c in coeffs do
9281      cl  := ResidueClassWithFixedRep(c[2],c[1]);
9282      img := Classes(cl^g)[1];
9283      m := img[1]; r := img[2];
9284      e := (r - r mod m)/m;
9285      if e <> 0 then Add(shifts,SparseRep(ClassShift(r,m)^e)); fi;
9286    od;
9287    if shifts <> [] then
9288      shift := Product(shifts);
9289    else
9290      shift := One(g);
9291    fi;
9292    g := g * shift^-1;
9293    if stdrep then
9294      g      := StandardRep(g);
9295      shift  := StandardRep(shift);
9296      revert := StandardRep(revert);
9297      if listfactors = true then
9298        reverts := List(reverts,StandardRep);
9299        shifts  := List(shifts,StandardRep);
9300      fi;
9301    fi;
9302    if listfactors <> true then
9303      return [ g, shift, revert ];
9304    else
9305      return [ g, shifts, reverts ];
9306    fi;
9307  end );
9308
9309#############################################################################
9310##
9311#M  FactorizationIntoCSCRCT( <g> ) . . . . . for bijective rcwa mappings of Z
9312##
9313InstallMethod( FactorizationIntoCSCRCT,
9314               "for bijective rcwa mappings of Z (RCWA)", true,
9315               [ IsRcwaMappingOfZ ], 0,
9316
9317  function ( g )
9318
9319    local  DivideBy, SaveState, RevertDirectionAndJumpBack, StateInfo,
9320           facts, gbuf, leftfacts, leftfactsbuf, rightfacts, rightfactsbuf,
9321           elm, direction, sgn, log, loop, rev, revert, affsrc, P, oldP,
9322           newP, parts, block, h, cycs, cyc, gfixP, cl, rest, c, m, r,
9323           multfacts, divfacts, p, q, Smult, Sdiv, clSmult, clSdiv,
9324           pairs, pair, diffs, largeprimes, splitpair, splittedpairs,
9325           splittedpair, d, dpos, disjoint, ctchunk,
9326           multswitches, divswitches, kmult, kdiv, i, j, k;
9327
9328    StateInfo := function ( )
9329      Info(InfoRCWA,1,"Modulus(<g>) = ",Modulus(g),
9330                      ", Multiplier(<g>) = ",Multiplier(g),
9331                      ", Divisor(<g>) = ",Divisor(g));
9332      Info(InfoRCWA,2,"MappedPartitions(<g>) = ",MappedPartitions(g));
9333    end;
9334
9335    SaveState := function ( )
9336      gbuf          := g;
9337      leftfactsbuf  := ShallowCopy(leftfacts);
9338      rightfactsbuf := ShallowCopy(rightfacts);
9339    end;
9340
9341    RevertDirectionAndJumpBack := function ( )
9342      if   direction  = "from the right"
9343      then direction := "from the left";
9344      else direction := "from the right"; fi;
9345      Info(InfoRCWA,1,"Jumping back and retrying with divisions ",
9346                      direction,".");
9347      g := gbuf; leftfacts := leftfactsbuf; rightfacts := rightfactsbuf;
9348      k[Maximum(p,q)] := k[Maximum(p,q)] + 1;
9349    end;
9350
9351    DivideBy := function ( l )
9352
9353      local  fact, prod, areCTs;
9354
9355      areCTs := ValueOption("ct") = true;
9356      if not IsList(l) then l := [l]; fi;
9357      for fact in l do # Factors in divisors list must commute.
9358        Info(InfoRCWA,1,"Dividing by ",ViewString(fact)," ",direction,".");
9359      od;
9360      if direction = "from the right" then
9361        if   areCTs
9362        then prod   := Product(l);
9363        else prod   := Product(Reversed(l))^-1; fi;
9364        g           := g * prod;
9365        rightfacts  := Concatenation(Reversed(l),rightfacts);
9366      else
9367        if   areCTs
9368        then prod   := Product(Reversed(l));
9369        else prod   := Product(l)^-1; fi;
9370        g           := prod * g;
9371        leftfacts   := Concatenation(leftfacts,l);
9372      fi;
9373      StateInfo();
9374      Assert(4,IsBijective(RcwaMapping(ShallowCopy(Coefficients(g)))));
9375    end;
9376
9377    if not IsBijective(g) then return fail; fi;
9378    if IsOne(g) then return [g]; fi;
9379
9380    if not IsRcwaMappingStandardRep(g) then g := StandardRep(g); fi;
9381
9382    leftfacts := []; rightfacts := []; facts := []; elm := g;
9383    direction := ValueOption("Direction");
9384    if direction <> "from the left" then direction := "from the right"; fi;
9385    multswitches := []; divswitches := []; log := []; loop := false;
9386
9387    if not IsClassWiseOrderPreserving(g) then
9388
9389      Info(InfoRCWA,1,"Making the mapping class-wise order-preserving.");
9390
9391      rev    := ClassWiseOrderReversingOn(g);
9392      revert := [List(AsUnionOfFewClasses(rev  ),ClassReflection),
9393                 List(AsUnionOfFewClasses(rev^g),ClassReflection)];
9394      if   Length(revert[1]) <= Length(revert[2])
9395      then g := Product(revert[1])^-1 * g;
9396      else g := g * Product(revert[2])^-1; fi;
9397
9398    else revert := [[],[]]; fi;
9399
9400    if IsIntegral(g) then
9401
9402      Info(InfoRCWA,1,"Determining largest sources of affine mappings.");
9403
9404      affsrc := Union(List(LargestSourcesOfAffineMappings(g),
9405                           AsUnionOfFewClasses));
9406      m := Modulus(g);
9407
9408      Info(InfoRCWA,1,"Computing respected partition.");
9409
9410      if ValueOption("ShortenPartition") <> false then
9411        h := PermList(List([0..m-1],i->i^g mod m + 1));
9412        P := Set(List(affsrc,S->Intersection(S,[0..m-1]))) + 1;
9413        repeat
9414          oldP := ShallowCopy(P); newP := [];
9415          P    := List(P,block->OnSets(block,h));
9416          for i in [1..Length(P)] do
9417            if P[i] in oldP then Add(newP,P[i]); else # split
9418              parts := List([1..Length(oldP)],j->Intersection(P[i],oldP[j]));
9419              parts := Filtered(parts,block->not IsEmpty(block));
9420              newP  := Concatenation(newP,parts);
9421            fi;
9422          od;
9423          P := Set(newP);
9424        until P = oldP;
9425        P := Set(P,res->ResidueClassUnion(Integers,m,res-1));
9426        Assert(4,Union(P)=Integers);
9427        if   not ForAll(P,IsResidueClass)
9428        then P := AllResidueClassesModulo(Modulus(g)); fi;
9429      else P := AllResidueClassesModulo(Modulus(g)); fi;
9430
9431      if InfoLevel(InfoRCWA) >= 1 then
9432        Print("#I  Computing induced permutation on respected partition ");
9433        View(P); Print(".\n");
9434      fi;
9435
9436      if   ValueOption("ShortenPartition") <> false
9437      then h := PermutationOpNC(g,P,OnPoints);
9438      else h := PermList(List([0..Modulus(g)-1],i->i^g mod Modulus(g) + 1));
9439      fi;
9440      cycs := Orbits(Group(h),MovedPoints(h));
9441      cycs := List(cycs,cyc->Cycle(h,Minimum(cyc)));
9442      for cyc in cycs do
9443        for i in [2..Length(cyc)] do
9444          Add(facts,ClassTransposition(P[cyc[1]],P[cyc[i]]));
9445        od;
9446      od;
9447
9448      Info(InfoRCWA,1,"Factoring the rest into class shifts.");
9449
9450      gfixP := g/Product(facts); # gfixP stabilizes the partition P.
9451      for cl in P do
9452        rest := RestrictedPerm(gfixP,cl);
9453        if IsOne(rest) then continue; fi;
9454        m := Modulus(rest); r := Residue(cl);
9455        c := Coefficients(rest)[r+1];
9456        facts := Concatenation([ClassShift(r,m)^(c[2]/m)],facts);
9457      od;
9458
9459    else
9460
9461      StateInfo();
9462
9463      repeat
9464
9465        k := ListWithIdenticalEntries(Maximum(Union(Factors(Multiplier(g)),
9466                                                    Factors(Divisor(g)))),1);
9467        while not IsBalanced(g) do
9468
9469          p := 1; q := 1;
9470          multfacts := Set(Factors(Multiplier(g)));
9471          divfacts  := Set(Factors(Divisor(g)));
9472          if   not IsSubset(divfacts,multfacts)
9473          then p := Maximum(Difference(multfacts,divfacts)); fi;
9474          if   not IsSubset(multfacts,divfacts)
9475          then q := Maximum(Difference(divfacts,multfacts)); fi;
9476
9477          if   Maximum(p,q) < Maximum(Union(multfacts,divfacts))
9478          then break; fi;
9479
9480          if Maximum(p,q) >= 3 then
9481            if p > q then # Additional prime p in multiplier.
9482              if p in multswitches then RevertDirectionAndJumpBack(); fi;
9483              Add(divswitches,p); SaveState();
9484              DivideBy(PrimeSwitch(p,k[p]));
9485            else          # Additional prime q in divisor.
9486              if q in divswitches then RevertDirectionAndJumpBack(); fi;
9487              Add(multswitches,q); SaveState();
9488              DivideBy(PrimeSwitch(q,k[q])^-1);
9489            fi;
9490          elif 2 in [p,q]
9491          then DivideBy(ClassTransposition(0,2,1,4):ct); fi;
9492
9493        od;
9494
9495        if IsOne(g) then break; fi;
9496
9497        p     := Maximum(Factors(Multiplier(g)*Divisor(g)));
9498        kmult := Number(Factors(Multiplier(g)),q->q=p);
9499        kdiv  := Number(Factors(Divisor(g)),q->q=p);
9500        k     := Maximum(kmult,kdiv);
9501        Smult := Multpk(g,p,kmult);
9502        Sdiv  := Multpk(g,p,-kdiv);
9503        if   direction = "from the right"
9504        then Smult := Smult^g; Sdiv := Sdiv^g; fi;
9505
9506        Info(InfoRCWA,1,"p = ",p,", kmult = ",kmult,", kdiv = ",kdiv);
9507
9508        # Search residue classes r1(m1) in Smult, r2(m2) in Sdiv
9509        # with m1/m2 = p^k.
9510
9511        clSmult := AsUnionOfFewClasses(Smult);
9512        clSdiv  := AsUnionOfFewClasses(Sdiv);
9513
9514        if InfoLevel(InfoRCWA) >= 1 then
9515          if   direction = "from the right"
9516          then Print("#I  Images of c"); else Print("#I  C"); fi;
9517          Print("lasses being multiplied by q*p^kmult:\n#I  ");
9518          ViewObj(clSmult);
9519          if   direction = "from the right"
9520          then Print("\n#I  Images of c"); else Print("\n#I  C"); fi;
9521          Print("lasses being divided by q*p^kdiv:\n#I  ");
9522          ViewObj(clSdiv); Print("\n");
9523        fi;
9524
9525        if not [p,kmult,kdiv,clSmult,clSdiv,direction] in log then
9526
9527          Add(log,[p,kmult,kdiv,clSmult,clSdiv,direction]);
9528
9529          repeat
9530            if   direction = "from the right"
9531            then sgn := 1; else sgn := -1; fi;
9532            pairs := Filtered(Cartesian(clSmult,clSdiv),
9533                     pair->PadicValuation(Mod(pair[1])/Mod(pair[2]),p)
9534                           = sgn * k);
9535            pairs := Set(pairs);
9536            if pairs = [] then
9537              diffs := List(Cartesian(clSmult,clSdiv),
9538                       pair->PadicValuation(Mod(pair[1])/Mod(pair[2]),p));
9539              if Maximum(diffs) < sgn * k then
9540                Info(InfoRCWA,2,"Splitting classes being multiplied by ",
9541                                "q*p^kmult.");
9542                clSmult := Flat(List(clSmult,cl->SplittedClass(cl,p)));
9543              fi;
9544              if Maximum(diffs) > sgn * k then
9545                Info(InfoRCWA,2,"Splitting classes being divided by ",
9546                                "q*p^kdiv.");
9547                clSdiv := Flat(List(clSdiv,cl->SplittedClass(cl,p)));
9548              fi;
9549            fi;
9550          until pairs <> [];
9551
9552          Info(InfoRCWA,1,"Found ",Length(pairs)," pairs.");
9553
9554          splittedpairs := [];
9555          for i in [1..Length(pairs)] do
9556            largeprimes := List(pairs[i],
9557                                cl->Filtered(Factors(Modulus(cl)),q->q>p));
9558            largeprimes := List(largeprimes,Product);
9559            splitpair   := largeprimes/Gcd(largeprimes);
9560            if 1 in splitpair then # Omit non-disjoint split.
9561              if splitpair = [1,1] then Add(splittedpairs,pairs[i]); else
9562                d := Maximum(splitpair); dpos := 3-Position(splitpair,d);
9563                if dpos = 1 then
9564                  splittedpair := List(SplittedClass(pairs[i][1],d),
9565                                       cl->[cl,pairs[i][2]]);
9566                else
9567                  splittedpair := List(SplittedClass(pairs[i][2],d),
9568                                       cl->[pairs[i][1],cl]);
9569                fi;
9570                splittedpairs := Concatenation(splittedpairs,splittedpair);
9571              fi;
9572            fi;
9573          od;
9574
9575          pairs := splittedpairs;
9576          Info(InfoRCWA,1,"After filtering and splitting: ",
9577                          Length(pairs)," pairs.");
9578
9579          repeat
9580            disjoint := [pairs[1]]; i := 1;
9581            while i < Length(pairs)
9582                  and Sum(List(Flat(disjoint),Density))
9583                    = Density(Union(Flat(disjoint)))
9584            do
9585              i := i + 1;
9586              Add(disjoint,pairs[i]);
9587            od;
9588            if   Sum(List(Flat(disjoint),Density))
9589               > Density(Union(Flat(disjoint)))
9590            then disjoint := disjoint{[1..Length(disjoint)-1]}; fi;
9591            DivideBy(List(disjoint,ClassTransposition):ct);
9592            pairs := Difference(pairs,disjoint);
9593          until pairs = [];
9594
9595        else
9596          if ValueOption("Slave") = true then
9597            Info(InfoRCWA,1,"A loop has been detected. Attempt failed.");
9598            return fail;
9599          else
9600            Info(InfoRCWA,1,"A loop has been detected. Trying to ",
9601                            "factor the inverse instead.");
9602            facts := FactorizationIntoCSCRCT(elm^-1:Slave);
9603            if facts = fail then
9604              Info(InfoRCWA,1,"Factorization of the inverse failed also. ",
9605                              "Giving up.");
9606              return fail;
9607            else return Reversed(List(facts,Inverse)); fi;
9608          fi;
9609        fi;
9610
9611      until IsIntegral(g);
9612
9613      facts := Concatenation(leftfacts,
9614                             FactorizationIntoCSCRCT(g:Slave),
9615                             rightfacts);
9616
9617      if ValueOption("ExpandPrimeSwitches") = true then
9618        facts := Flat(List(facts,FactorizationIntoCSCRCT));
9619      fi;
9620
9621    fi;
9622
9623    if   Length(revert[1]) <= Length(revert[2])
9624    then facts := Concatenation(revert[1],facts);
9625    else facts := Concatenation(facts,revert[2]); fi;
9626
9627    facts := Filtered(facts,fact->not IsOne(fact));
9628
9629    if ValueOption("Slave") <> true and ValueOption("NC") <> true then
9630      Info(InfoRCWA,1,"Checking the result.");
9631      if   Product(facts) <> elm
9632      then Error("FactorizationIntoCSCRCT: Internal error!"); fi;
9633    fi;
9634
9635    return facts;
9636
9637  end );
9638
9639#############################################################################
9640##
9641#M  FactorizationIntoCSCRCT( <g> ) . . . . . . . . . for the identity mapping
9642##
9643InstallMethod( FactorizationIntoCSCRCT,
9644               "for the identity mapping (RCWA)",
9645               true, [ IsRcwaMapping and IsOne ], 0, one -> [ one ] );
9646
9647#############################################################################
9648##
9649#M  Factorization( <g> ) . . . for bijective rcwa mappings, into cs / cr / ct
9650##
9651InstallMethod( Factorization,
9652               "into class shifts / reflections / transpositions (RCWA)",
9653               true, [ IsRcwaMapping ], 0, FactorizationIntoCSCRCT );
9654
9655#############################################################################
9656##
9657#F  ReducingConjugatorCT3Z( <tau> )
9658##
9659BindGlobal( "ReducingConjugatorCT3Z",
9660
9661  function ( tau )
9662
9663    local  w, ct, cls, cls4, cls6, cl, cl1, cl2, cl3, cl4;
9664
9665    w    := [];
9666    cls  := Union(List([2,3,4,6,8,9,12,18],AllResidueClassesModulo));
9667    cls4 := AllResidueClassesModulo(4);
9668    cls6 := AllResidueClassesModulo(6);
9669
9670    repeat
9671
9672      repeat
9673
9674        cl := First(cls4,cl->IsSubset(cl,Support(tau)));
9675        if cl = fail then break; fi;
9676
9677        ct := ClassTransposition((Residue(cl)+1) mod 2,2,Residue(cl),4);
9678        tau := tau^ct;
9679        Add(w,ct);
9680
9681      until cl = fail;
9682
9683      repeat
9684
9685        cl := First(cls6,cl->IsSubset(cl,Support(tau)));
9686        if cl = fail then break; fi;
9687
9688        ct := ClassTransposition((Residue(cl)+1) mod 2,2,Residue(cl),6);
9689        tau := tau^ct;
9690        Add(w,ct);
9691
9692      until cl = fail;
9693
9694      cl1 := TransposedClasses(tau)[1]; cl2 := TransposedClasses(tau)[2];
9695
9696      cl3 := First(cls,cl->Intersection(cl,Support(tau)) = []);
9697
9698      if cl3 = fail then break; fi;
9699
9700      cl4 := First(cls,cl->Intersection(cl,cl1) = []
9701                       and Intersection(cl,cl3) = []
9702                       and IsSubset(cl,cl2) and Modulus(cl) > Modulus(cl3));
9703
9704      if cl4 = fail then break; fi;
9705
9706      ct  := ClassTransposition(cl3,cl4);
9707      tau := tau^ct;
9708      Add(w,ct);
9709
9710    until cl4 = fail;
9711
9712    return [ tau, w ];
9713  end );
9714
9715#############################################################################
9716##
9717#M  FactorizationIntoElementaryCSCRCT( <g> )
9718##
9719InstallMethod( FactorizationIntoElementaryCSCRCT,
9720               "for elements of CT_{3}(Z) (RCWA)", true,
9721               [ IsRcwaMappingOfZ ], 0,
9722
9723  function ( g )
9724
9725    local  CT3Zgens, facts, elementaryfacts, ct, ct1,
9726           elems, elemsold, t;
9727
9728    if not IsBijective(g) then
9729      Error("usage: FactorizationIntoElementaryCSCRCT( <g> ), ",
9730            "where <g> is an rcwa permutation\n");
9731    fi;
9732
9733    if   not IsSubset([2,3],PrimeSet(g)) or not IsSignPreserving(g)
9734    then TryNextMethod(); fi;
9735
9736    CT3Zgens := List([ [0,2,1,2], [1,2,2,4], [0,2,1,4], [1,4,2,4],
9737                       [0,3,1,3], [1,3,2,3], [0,3,1,9], [0,3,4,9],
9738                       [0,3,7,9], [0,2,1,6], [0,3,1,6] ],
9739                     ClassTransposition);
9740
9741    facts           := Flat(List(Factorization(g),Factorization));
9742    elementaryfacts := [];
9743
9744    for ct in facts do
9745      elems := [ct];
9746      while not ForAll(elems,
9747                       ct->IsSubset([2,3,4,6,8,9],
9748                                    List(TransposedClasses(ct),Modulus))) do
9749        elemsold := ShallowCopy(elems);
9750        elems    := [];
9751        for ct1 in elemsold do
9752          t := ReducingConjugatorCT3Z(ct1);
9753          Append(elems,Concatenation(t[2],[t[1]],Reversed(t[2])));
9754        od;
9755      od;
9756      if   Product(elems) <> ct
9757      then Error("ElementaryFactorization: internal error!\n"); fi;
9758      Append(elementaryfacts,elems);
9759    od;
9760
9761    return elementaryfacts;
9762  end );
9763
9764#############################################################################
9765##
9766#E  rcwamap.gi . . . . . . . . . . . . . . . . . . . . . . . . . .  ends here
9767