home *** CD-ROM | disk | FTP | other *** search
/ Math Solutions 1995 October / Math_Solutions_CD-ROM_Walnut_Creek_October_1995.iso / pc / mac / discrete / lib / polyfin.g < prev    next >
Encoding:
Text File  |  1993-05-05  |  32.0 KB  |  1,136 lines

  1. #############################################################################
  2. ##
  3. #A  polyfin.g                   GAP library                      Frank Celler
  4. ##
  5. #A  @(#)$Id: polyfin.g,v 3.20 1993/02/11 17:08:11 fceller Rel $
  6. ##
  7. #Y  Copyright 1990-1992,  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
  8. ##
  9. ##  This file contains functions for polynomials over finite fields.
  10. ##
  11. #H  $Log: polyfin.g,v $
  12. #H  Revision 3.20  1993/02/11  17:08:11  fceller
  13. #H  added <list> + <polynomial>
  14. #H
  15. #H  Revision 3.19  1993/01/08  09:13:08  fceller
  16. #H  'Factors' now stores its result
  17. #H
  18. #H  Revision 3.18  1992/12/16  19:47:27  martin
  19. #H  replaced quoted record names with escaped ones
  20. #H
  21. #H  Revision 3.17  1992/11/25  12:35:26  fceller
  22. #H  added 'Degree'
  23. #H
  24. #H  Revision 3.16  1992/11/19  14:10:23  fceller
  25. #H  fixed a minor bug
  26. #H
  27. #H  Revision 3.15  92/11/16  12:23:10  fceller
  28. #H  added Laurent polynomials
  29. #H  
  30. #H  Revision 3.14  92/10/28  09:15:08  fceller
  31. #H  fixed 'mod' for zero polynomial
  32. #H  
  33. #H  Revision 3.13  1992/07/24  07:08:07  fceller
  34. #H  improved '*' to allow <nullpoly> * <int>
  35. #H
  36. #H  Revision 3.12  1992/07/23  09:20:48  fceller
  37. #H  improved '*' to allow <list> * <polynomial>
  38. #H
  39. #H  Revision 3.11  1992/07/02  11:51:31  fceller
  40. #H  add 'FiniteFieldPolynomialOps.+'
  41. #H
  42. #H  Revision 3.10  1992/06/17  07:06:04  fceller
  43. #H  moved '<somedomain>.operations.Polynomial' function to "<somedomain>.g"
  44. #H
  45. #H  Revision 3.9  1992/06/04  09:17:14  fceller
  46. #H  fixed a missing set command
  47. #H
  48. #H  Revision 3.8  1992/06/03  10:43:51  fceller
  49. #H  fixed some minor bugs
  50. #H
  51. #H  Revision 3.7  1992/06/01  07:33:22  fceller
  52. #H  moved a few functions to "fldpoly.g"
  53. #H
  54. #H  Revision 3.6  1992/05/25  09:23:47  fceller
  55. #H  added 'RootsRepresentative', added an adapted version of
  56. #H  Martin's implementation of Cantor/Zassenhaus factorisation
  57. #H
  58. #H  Revision 3.5  1992/05/25  09:12:54  fceller
  59. #H  added 'PowerMod', fixed a bug
  60. #H
  61. #H  Revision 3.4  1992/05/07  10:30:56  fceller
  62. #H  changed order algorithm into order mod scalar
  63. #H
  64. #H  Revision 3.3  1992/04/23  10:33:13  fceller
  65. #H  changed 'PrimePowers' into 'PrimePowersInt'
  66. #H
  67. #H  Revision 3.2  1992/04/23  07:23:11  fceller
  68. #H  Initial GAP 3.2 release
  69. ##
  70.  
  71.  
  72. #############################################################################
  73. ##
  74. #F  InfoPoly1(...)  . . . . . . . . . . . infomation function for polynomials
  75. #F  InfoPoly2(...)  . . . . . . . . . . . . .  debug function for polynomials
  76. ##
  77. if not IsBound(InfoPoly1)    then InfoPoly1   := Ignore;  fi;
  78. if not IsBound(InfoPoly2)    then InfoPoly2   := Ignore;  fi;
  79.  
  80.  
  81. #############################################################################
  82. ##
  83.  
  84. #V  FiniteFieldPolynomialRingOps  . . . . . . . . .  full polynomial ring ops
  85. ##
  86. FiniteFieldPolynomialRingOps := Copy( FieldPolynomialRingOps );
  87. FiniteFieldPolynomialRingOps.name := "FiniteFieldPolynomialRingOps";
  88.  
  89.  
  90. #############################################################################
  91. ##
  92. #F  FiniteFieldPolynomialRingOps.Gcd( <R>, <r>, <s> ) . . . . . . . . . . gcd
  93. ##
  94. FiniteFieldPolynomialRingOps.Gcd := function ( R, r, s )
  95.     local   gcd,  u,  v,  w,  val;
  96.  
  97.     # remove common x^i term
  98.     val := Minimum( r.valuation, s.valuation );
  99.     s   := ShiftedCoeffs( s.coefficients, s.valuation-val );
  100.     r   := ShiftedCoeffs( r.coefficients, r.valuation-val );
  101.  
  102.     # perform a Euclidean algorithm
  103.     u := r;
  104.     v := s;
  105.     while 0 < Length(v)  do
  106.         w := v;
  107.     ReduceCoeffs( u, v );
  108.     v := u;
  109.         u := w;
  110.     od;
  111.     gcd := u * u[Length(u)]^-1;
  112.  
  113.     # return the gcd
  114.     return Polynomial( R.baseRing, gcd, val );
  115. end;
  116.  
  117.  
  118. #############################################################################
  119. ##
  120. #F  FiniteFieldPolynomialRingOps.PowerMod( <R>, <g>, <e>, <m> )    . . . . power
  121. ##
  122. FiniteFieldPolynomialRingOps.PowerMod := function( R, g, e, m )
  123.     local   val;
  124.  
  125.     # if <m> is of degree one return the zero polynomial
  126.     if Degree(m) = 1  then
  127.     return R.zero;
  128.  
  129.     # if <e> is zero return one
  130.     elif e = 0  then
  131.     return R.one;
  132.     fi;
  133.  
  134.     # reduce polynomial
  135.     g := R.operations.Mod( R, g, m );
  136.  
  137.     # and invert if necessary
  138.     if e < 0  then
  139.     g := R.operations.QuotientMod( R, R.one, g, m );
  140.         if g = false  then
  141.         Error( "<g> must be invertable module <m>" );
  142.         fi;
  143.         e := -e;
  144.     fi;
  145.  
  146.     # use 'PowerModCoeffs' to power polynomial
  147.     if g.valuation = m.valuation  then
  148.     val := g.valuation;
  149.     g   := g.coefficients;
  150.     m   := m.coefficients;
  151.     else
  152.     val := 0;
  153.         g   := ShiftedCoeffs( g.coefficients, g.valuation );
  154.         m   := ShiftedCoeffs( m.coefficients, m.valuation );
  155.     fi;
  156.     return Polynomial( R.baseRing, PowerModCoeffs( g, e, m ), val );
  157.  
  158. end;
  159.  
  160.  
  161. #############################################################################
  162. ##
  163. #F  FiniteFieldPolynomialRingOps.RootsRepresentative( <R>, <f>, <n> )  a root
  164. ##
  165. FiniteFieldPolynomialRingOps.RootsRepresentative := function( R, f, n )
  166.     local   r,  d,  p,  e,  z,  i,  o,  v;
  167.  
  168.     r := [];
  169.     p := R.baseRing.char;
  170.     d := R.baseRing.degree;
  171.     z := R.baseRing.root;
  172.     v := f.valuation;
  173.     o := p^d-1;
  174.     for i  in [ 0 .. (Length(f.coefficients)-1)/n ]  do
  175.         e := f.coefficients[i*n+1];
  176.         if e = R.baseRing.zero  then
  177.             r[i+1] := R.baseRing.zero;
  178.         else
  179.             r[i+1] := z ^ ( (LogFFE(e,z) / n) mod o );
  180.         fi;
  181.     od;
  182.     return Polynomial( R.baseRing, r, v/n );
  183.  
  184. end;
  185.  
  186.  
  187. #############################################################################
  188. ##
  189. #F  FiniteFieldPolynomialRingOps.FactorsCommonDegree( <R>, <f>, <d> ) factors
  190. ##
  191. ##  <f> must be a  square free product of irreducible factors of  degree  <d>
  192. ##  and leading coefficient 1.  <R> must be  a polynomial ring over  a finite
  193. ##  field of size p^k.
  194. ##
  195. FiniteFieldPolynomialRingOps.FactorsCommonDegree := function ( R, f, d )
  196.     local  g,  h,  i,  k;
  197.  
  198.     # if <f> has a trivial constant term signal an error
  199.     if f.valuation <> 0  then
  200.     Error( "<f> must have a non-trivial constant term" );
  201.  
  202.     # if <f> has degree 0, return f
  203.     elif Degree(f) = 0  then
  204.         return [];
  205.  
  206.     # if <f> has degree <d>, return irreducible <f>
  207.     elif Degree(f) = d  then
  208.         return [ f ];
  209.     fi;
  210.  
  211.     # choose a random polynomial <g> of degree less than 2*<d>
  212.     g := RandomPol( R.baseRing, 2*d-1 );
  213.  
  214.     # if p = 2 take <g> + <g>^2 + <g>^(2^2) + ... + <g>^(2^(k*<d>-1))
  215.     if R.baseRing.char = 2  then
  216.         h := ShiftedCoeffs( g.coefficients, g.valuation );
  217.     k := ShiftedCoeffs( f.coefficients, f.valuation );
  218.     g := g.coefficients;
  219.         for i  in [ 1 .. R.baseRing.degree*d-1 ]  do
  220.             g := ProductCoeffs( g, g );
  221.         ReduceCoeffs( g, k );
  222.         AddCoeffs( h, g );
  223.         od;
  224.     h := Polynomial( R.baseRing, h );
  225.  
  226.     # if p > 2 take <g> ^ ( ( p ^ (k * <d>) - 1 ) / 2 ) - 1
  227.     else
  228.         h := PowerMod(R,g,(R.baseRing.char^(R.baseRing.degree*d)-1)/2,f);
  229.         h := h - 1;
  230.     fi;
  231.  
  232.     # gcd of <f> and <h> is with probability > 1/2 a proper factor
  233.     g := Gcd( R, f, h );
  234.     return Concatenation( 
  235.         R.operations.FactorsCommonDegree( R, Quotient( R, f, g ), d ),
  236.         R.operations.FactorsCommonDegree( R, g, d ) );
  237. end;
  238.  
  239.  
  240. #############################################################################
  241. ##
  242. #F  FiniteFieldPolynomialRingOps.FactorsSquarefree( <R>, <f> )  . . . factors
  243. ##
  244. ##  <f> must be square free and must have leading coefficient 1. <R> must  be
  245. ##  a polynomial ring over a finite field of size q.
  246. ##
  247. FiniteFieldPolynomialRingOps.FactorsSquarefree := function( R,  f )
  248.     local   facs,  lcf,  deg,  pow,  px,  cyc,  gcd;
  249.  
  250.     # if <f> has a trivial constant term signal an error
  251.     if f.valuation <> 0  then
  252.     Error( "<f> must have a non-trivial constant term" );
  253.     fi;
  254.  
  255.     # <facs> will contain factorisation
  256.     facs := [];
  257.  
  258.     # in the following <pow> = x ^ ( q ^ (<deg>+1) )
  259.     deg := 0;
  260.     px  := Polynomial( R.baseRing, [ R.baseRing.zero, R.baseRing.one ] );
  261.     pow := PowerMod( R, px, R.baseRing.size, f );
  262.  
  263.     # while <f> could still have two irreducible factors
  264.     while 2*(deg+1) <= Degree(f) do
  265.  
  266.         # next degree and next cyclotomic polynomial x^(q^(<deg>+1))-x
  267.         deg := deg + 1;
  268.         cyc := pow - px;
  269.         pow := PowerMod( R, pow, Size( R.baseRing ), f );
  270.  
  271.         # compute the gcd of <f> and <cyc>
  272.         gcd := Gcd( R, f, cyc );
  273.  
  274.         # split the gcd with 'R.operations.FactorsCommonDegree'
  275.         if 0 < Degree(gcd)  then
  276.             Append( facs, R.operations.FactorsCommonDegree( R, gcd, deg ) );
  277.         f := Quotient( R, f, gcd );
  278.         fi;
  279.  
  280.     od;
  281.  
  282.     # if neccessary add irreducible <f> to the list of factors
  283.     if 0 < Degree(f)  then
  284.         Add( facs, f );
  285.     fi;
  286.  
  287.     # return the factorisation
  288.     return facs;
  289.  
  290. end;
  291.  
  292.  
  293. #############################################################################
  294. ##
  295. #F  FiniteFieldPolynomialRingOps.Factors( <R>, <f> )  . . . .  factors of <f>
  296. ##
  297. FiniteFieldPolynomialRingOps.Factors := function ( R, f )
  298.     local  facs,  l,  d,  g,  h,  r,  i,  v,  k;
  299.  
  300.     # handle trivial case
  301.     if Degree(f) < 2  then
  302.     f.factors := [ f ];
  303.     elif Length(f.coefficients) = 1  then
  304.     l := List( [ 1 .. f.valuation ], x -> Indeterminate(f.baseRing) );
  305.     l[1] := l[1] * f.coefficients[1];
  306.     f.factors := l;
  307.     fi;
  308.  
  309.     # if we know the factors, return
  310.     if IsBound(f.factors) and f.baseRing = R.baseRing  then
  311.         return f.factors;
  312.     fi;
  313.  
  314.     # make the polynomial normed, remember the leading coefficient for later
  315.     g := R.operations.StandardAssociate( R, f );
  316.     l := R.operations.Quotient( R, f, g );
  317.     v := g.valuation;
  318.     k := Polynomial( R.baseRing, g.coefficients );
  319.  
  320.     # compute the deriviative
  321.     d := R.operations.Derivative( R, k );
  322.  
  323.     # if the derivative is nonzero then $k / Gcd(k,d)$ is squarefree
  324.     if d <> R.zero  then
  325.  
  326.         # compute the gcd of <k> and the derivative <d>
  327.         g := Gcd( R, k, d );
  328.  
  329.         # factor the squarefree quotient and the remainder
  330.         facs := R.operations.FactorsSquarefree( R, Quotient( R, k, g ) );
  331.         for h  in ShallowCopy(facs)  do
  332.             while 0 = Length( R.operations.Mod( R, g, h ).coefficients )  do
  333.                 Add( facs, h );
  334.         g := Quotient( R, g, h );
  335.             od;
  336.         od;
  337.         if 1 < Length( g.coefficients )  then
  338.             Append( facs, R.operations.Factors( R, g ) );
  339.         fi;
  340.  
  341.     # otherwise <k> is the <p>-th power of another polynomial <r>
  342.     else
  343.  
  344.         # compute the <p>-th root of <f>
  345.         r := R.operations.RootsRepresentative( R, k, R.baseRing.char );
  346.  
  347.         # factor this polynomial
  348.         h := R.operations.Factors( R, r );
  349.  
  350.         # each factor appears <p> times in <k>
  351.         facs := [];
  352.         for i  in [ 1 .. R.baseRing.char ]  do
  353.             Append( facs, h );
  354.         od;
  355.  
  356.     fi;
  357.  
  358.     # Sort the factorization
  359.     Append( facs, List( [1..v], x -> Indeterminate(R.baseRing) ) );
  360.     Sort( facs );
  361.  
  362.     # return the factorization
  363.     facs[1] := facs[1] * l;
  364.     if f.baseRing = R.baseRing  then
  365.     f.factors := facs;
  366.     fi;
  367.     return facs;
  368.  
  369. end;
  370.  
  371.  
  372. #############################################################################
  373. ##
  374.  
  375. #V  FiniteFieldLaurentPolynomials . . . . . . . domain of Laurent polynomials
  376. ##
  377. FiniteFieldLaurentPolynomials      := Copy( LaurentPolynomials );
  378. FiniteFieldLaurentPolynomials.name := "FiniteFieldLaurentPolynomials";
  379. FiniteFieldLaurentPolynomialsOps := FiniteFieldLaurentPolynomials.operations;
  380.  
  381. FiniteFieldLaurentPolynomialsOps.\in := function( p, FinFieldLaurentPolys )
  382.     return     IsRec( p )
  383.            and IsBound( p.isPolynomial )
  384.            and p.isPolynomial
  385.            and IsFiniteField( p.baseRing );
  386. end;
  387.  
  388.  
  389. #############################################################################
  390. ##
  391. #V  FiniteFieldPolynomials  . . . . domain of polynomials over a finite field
  392. ##
  393. FiniteFieldPolynomials            := Copy( Polynomials );
  394. FiniteFieldPolynomials.name       := "FiniteFieldPolynomials";
  395. FiniteFieldPolynomialsOps         := FiniteFieldPolynomials.operations;
  396.  
  397. FiniteFieldPolynomialsOps.\in := function( p, FiniteFieldPolynomials )
  398.     return     IsRec( p )
  399.            and IsBound( p.isPolynomial )
  400.            and p.isPolynomial
  401.            and 0 <= p.valuation
  402.            and IsFiniteField( p.baseRing );
  403. end;
  404.  
  405.  
  406. #############################################################################
  407. ##
  408.  
  409. #V  FiniteFieldPolynomialOps  . . . . . . . . . polynomial over finite fields
  410. ##
  411. FiniteFieldPolynomialOps := Copy( PolynomialOps );
  412. FiniteFieldPolynomialOps.name := "FiniteFieldPolynomialOps";
  413.  
  414.  
  415. #############################################################################
  416. ##
  417. #F  FiniteFieldPolynomialOps.\+  . . . . . . . . . .  sum of two polynomials
  418. ##
  419. FiniteFieldPolynomialOps.\+ := function( l, r )
  420.     local   R,  sum,  val,  vdf,  i;
  421.  
  422.     # handle the case that one argument is a list
  423.     if IsList(l)  then
  424.         return List( l, x -> x+r );
  425.     elif IsList(r)  then
  426.         return List( r, x -> l+x );
  427.     fi;
  428.  
  429.     # handle the case <scalar> + <polynomial>
  430.     if not IsPolynomial(l)  then
  431.         if IsInt(l)  then
  432.             l := l * r.baseRing.one;
  433.         elif not l in r.baseRing  then
  434.             Error( "<l> must lie in the base ring of <r>" );
  435.         fi;
  436.         R := r.baseRing;
  437.         l := R.operations.Polynomial( R, [l], 0 );
  438.     fi;
  439.  
  440.     # handle the case <polynomial> + <scalar>
  441.     if not IsPolynomial(r)  then
  442.         if IsInt(r)  then
  443.             r := r * l.baseRing.one;
  444.         elif not r in l.baseRing  then
  445.             Error( "<r> must lie in the base ring of <l>" );
  446.         fi;
  447.         R := l.baseRing;
  448.     r := R.operations.Polynomial( R, [r], 0 );
  449.     fi;
  450.  
  451.     # our superclass will handle different depth
  452.     if l.operations.Depth(l) <> r.operations.Depth(r)  then
  453.         return PolynomialOps.\+( l, r );
  454.  
  455.     # give up if we have different rings
  456.     elif l.baseRing <> r.baseRing  then
  457.         Error("polynomials must have the same ring");    
  458.  
  459.     # add two polynomials
  460.     else
  461.  
  462.         # get a common ring and the valuation minimum;
  463.         R   := l.baseRing;
  464.         vdf := r.valuation - l.valuation;
  465.  
  466.         # if <r>.valuation is the minimum shift <l>
  467.         if r.valuation < l.valuation  then
  468.             val := r.valuation;
  469.             sum := ShiftedCoeffs( l.coefficients, -vdf );
  470.             AddCoeffs( sum, r.coefficients );
  471.  
  472.         # if <l>.valuation is the minimum shift <r>
  473.         elif l.valuation < r.valuation  then
  474.             r   := ShiftedCoeffs( r.coefficients, vdf );
  475.             sum := SumCoeffs( l.coefficients, r );
  476.             val := l.valuation;
  477.  
  478.         # otherwise they are equal
  479.         else
  480.             sum := SumCoeffs( l.coefficients, r.coefficients );
  481.             val := l.valuation;
  482.         fi;
  483.     fi;
  484.  
  485.     # return the product
  486.     return R.operations.Polynomial( R, sum, val );
  487.  
  488. end;
  489.  
  490.  
  491. #############################################################################
  492. ##
  493. #F  FiniteFieldPolynomialOps.\-  . . . . . . . difference of two polynomials
  494. ##
  495. FiniteFieldPolynomialOps.\- := function( l, r )
  496.     local   R,  dif,  val,  vdf,  i;
  497.  
  498.     # handle the case that one argument is a list
  499.     if IsList(l)  then
  500.         return List( l, x -> x-r );
  501.     elif IsList(r)  then
  502.         return List( r, x -> l-x );
  503.     fi;
  504.  
  505.     # handle the case <scalar> + <polynomial>
  506.     if not IsPolynomial(l)  then
  507.         if IsInt(l)  then
  508.             l := l * r.baseRing.one;
  509.         elif not l in r.baseRing  then
  510.             Error( "<l> must lie in the base ring of <r>" );
  511.         fi;
  512.         R := r.baseRing;
  513.         l := R.operations.Polynomial( R, [l], 0 );
  514.     fi;
  515.  
  516.     # handle the case <polynomial> + <scalar>
  517.     if not IsPolynomial(r)  then
  518.         if IsInt(r)  then
  519.             r := r * l.baseRing.one;
  520.         elif not r in l.baseRing  then
  521.             Error( "<r> must lie in the base ring of <l>" );
  522.         fi;
  523.         R := l.baseRing;
  524.     r := R.operations.Polynomial( R, [r], 0 );
  525.     fi;
  526.  
  527.     # our superclass will handle different depth
  528.     if l.operations.Depth(l) <> r.operations.Depth(r)  then
  529.         return PolynomialOps.\-( l, r );
  530.  
  531.     # give up if we have different rings
  532.     elif l.baseRing <> r.baseRing  then
  533.         Error("polynomials must have the same ring");    
  534.  
  535.     # add two polynomials
  536.     else
  537.  
  538.         # get a common ring and the valuation minimum;
  539.         R   := l.baseRing;
  540.         vdf := r.valuation - l.valuation;
  541.  
  542.         # if <r>.valuation is the minimum shift <l>
  543.         if r.valuation < l.valuation  then
  544.             val := r.valuation;
  545.             dif := ShiftedCoeffs( l.coefficients, -vdf );
  546.             AddCoeffs( dif, r.coefficients, -R.one );
  547.  
  548.         # if <l>.valuation is the minimum shift <r>
  549.         elif l.valuation < r.valuation  then
  550.             val := l.valuation;
  551.             r   := ShiftedCoeffs( r.coefficients, vdf );
  552.             dif := Copy(l.coefficients);
  553.             AddCoeffs( dif, r, -R.one );
  554.  
  555.         # otherwise they are equal
  556.         else
  557.             val := l.valuation;
  558.         dif := Copy(l.coefficients);
  559.             AddCoeffs( dif, r.coefficients, -R.one );
  560.         fi;
  561.     fi;
  562.  
  563.     # return the product
  564.     return R.operations.Polynomial( R, dif, val );
  565.  
  566. end;
  567.  
  568.  
  569. #############################################################################
  570. ##
  571. #F  FiniteFieldPolynomialOps.\*  . . . . . . . .  product of two polynomials
  572. ##
  573. FiniteFieldPolynomialOps.\* := function( l, r )
  574.     local   R,  prd,  val;
  575.  
  576.     # handle the case that one argument is a list
  577.     if IsList(l)  then
  578.         return List(l, x -> x*r);
  579.     elif IsList(r)  then
  580.         return List(r, x -> l*x);
  581.  
  582.     # handle the case <scalar> * <polynomial>
  583.     elif not IsPolynomial(l)  then
  584.         if IsInt( l )  then
  585.         l := l * r.baseRing.one;
  586.     elif not l in r.baseRing  then
  587.         Error( "<l> must lie in the base ring of <r>" );
  588.     fi;
  589.     R := r.baseRing;
  590.     if l = r.baseRing.zero or r.coefficients = []  then
  591.         prd := [];
  592.             val := 0;
  593.         else
  594.         prd := l * r.coefficients;
  595.             val := r.valuation;
  596.     fi;
  597.  
  598.     # handle the case <polynomial> * <scalar>
  599.     elif not IsPolynomial(r)  then
  600.         if IsInt(r)  then
  601.         r := r * l.baseRing.one;
  602.     elif not r in l.baseRing  then
  603.         Error( "<r> must lie in the base ring of <l>" );
  604.     fi;
  605.     R := l.baseRing;
  606.     if r = l.baseRing.zero or l.coefficients = []  then
  607.         prd := [];
  608.             val := 0;
  609.         else
  610.         prd := l.coefficients * r;
  611.             val := l.valuation;
  612.     fi;
  613.  
  614.     # our superclass will handle different depth
  615.     elif l.operations.Depth(l) <> r.operations.Depth(r)  then
  616.         return PolynomialOps.\*( l, r );
  617.  
  618.     # give up if we have different rings
  619.     elif l.baseRing <> r.baseRing  then
  620.         Error( "polynomials must have the same ring" );    
  621.  
  622.     # multiply two polynomials
  623.     else
  624.  
  625.         # get a common ring
  626.         R := l.baseRing;
  627.  
  628.         # use 'ProductCoeffs' in order to fold product
  629.         prd := ProductCoeffs( l.coefficients, r.coefficients );
  630.         val := l.valuation + r.valuation;
  631.     fi;
  632.  
  633.     # return the product
  634.     return R.operations.Polynomial( R, prd, val );
  635.  
  636. end;
  637.  
  638.  
  639. #############################################################################
  640. ##
  641. #F  FiniteFieldPolynomialOps.\mod  . . . . . .  remainder of two polynomials
  642. ##
  643. FiniteFieldPolynomialOps.\mod := function( l, r )
  644.     local   R,  rem,  val,  vdf;
  645.  
  646.     # both <l> and <r> must be polynomials and <r> must be non zero
  647.     if not IsPolynomial( l )  then
  648.         Error( "<l> must be a polynomial" );
  649.     fi;
  650.     if not IsPolynomial( r )  then
  651.         Error( "<r> must be a polynomial" );
  652.     fi;
  653.     if Length( r.coefficients ) = 0  then
  654.         Error( "<r> must be non zero" );
  655.     fi;
  656.  
  657.     # our superclass will handle different depth
  658.     if l.operations.Depth(l) <> r.operations.Depth(r)  then
  659.         return PolynomialOps.\mod( l, r );
  660.  
  661.     # give up if we have different rings
  662.     elif l.baseRing <> r.baseRing  then
  663.         Error( "polynomials must have the same ring" );    
  664.  
  665.     # reduce <l> by <r>
  666.     else
  667.  
  668.         # if one is a Laurent polynomial use 'Mod'
  669.         if l.valuation < 0 or r.valuation < 0  then
  670.             return Mod( DefaultRing(l,r), l, r );
  671.         fi;
  672.  
  673.         # get a common ring and the value difference
  674.         R   := l.baseRing;
  675.         vdf := r.valuation - l.valuation;
  676.  
  677.         # if <r>.valuation is the minimum shift <l>
  678.         if r.valuation < l.valuation  then
  679.             val := r.valuation;
  680.             rem := ShiftedCoeffs( l.coefficients, -vdf );
  681.             ReduceCoeffs( rem, r.coefficients );
  682.  
  683.         # if <l>.valuation is the minimum shift <r>
  684.         elif l.valuation < r.valuation  then
  685.             r   := ShiftedCoeffs( r.coefficients, vdf );
  686.             rem := RemainderCoeffs( l.coefficients, r );
  687.             val := l.valuation;
  688.  
  689.         # otherwise they are equal
  690.         else
  691.             rem := RemainderCoeffs( l.coefficients, r.coefficients );
  692.             val := l.valuation;
  693.         fi;
  694.     fi;
  695.  
  696.     # return the remainder
  697.     return R.operations.Polynomial( R, rem, val );
  698.  
  699. end;
  700.  
  701.  
  702. #############################################################################
  703. ##
  704. #F  FiniteFieldPolynomialOps.\^  . . . . . . . . . .  power of a polynomials
  705. ##
  706. FiniteFieldPolynomialOps.\^ := function( l, r )
  707.     local   R,  pow,  val;
  708.  
  709.     # <l> must be a polynomial and <r> a non-negative integer
  710.     if not IsPolynomial( l )  then
  711.         Error( "<l> must be a polynomial" );
  712.     fi;
  713.     if not IsInt( r )  then
  714.         Error( "<r> must be an integer" );
  715.     fi;
  716.  
  717.     # invert <l> if necessary
  718.     if r < 0  then
  719.         R := LaurentPolynomialRing( l.baseRing );
  720.         l := R.operations.Quotient( R, R.one, l );
  721.         r := -r;
  722.     fi;
  723.  
  724.     # if <r> is zero, return x^0, if <r> is one return <l>
  725.     R := l.baseRing;
  726.     if r = 0  then
  727.         return Polynomial( R, [ R.one ] );
  728.     elif r = 1  then
  729.         return l;
  730.     fi;
  731.  
  732.     # if <l> is of degree less than 2, return
  733.     if Length(l.coefficients) = 0  then
  734.         return l;
  735.     elif Length(l.coefficients) = 1  then
  736.         return Polynomial( R, [ l.coefficients[1]^r ], l.valuation*r );
  737.     fi;
  738.  
  739.     # use repeated squaring
  740.     val := l.valuation * r;
  741.     l   := l.coefficients;
  742.     pow := [ R.one ];
  743.     while 0 < r  do
  744.         if r mod 2 = 1  then
  745.         pow := ProductCoeffs( pow, l );
  746.             r   := r - 1;
  747.         fi; 
  748.         if 1 < r  then
  749.         l := ProductCoeffs( l, l );
  750.             r := r / 2;
  751.         fi;
  752.     od;
  753.  
  754.     # return the power
  755.     return R.operations.Polynomial( R, pow, val );
  756.  
  757. end;
  758.  
  759.  
  760. #############################################################################
  761. ##
  762.  
  763. #F  PrimePowersInt( <n> ) . . . . . . . . . .  return the prime powers of <n>
  764. ##
  765. PrimePowersInt := function( n )
  766.     local   p,  pows,  lst;
  767.  
  768.     if n = 1  then
  769.     return [];
  770.     elif n = 0  then
  771.         Error( "<n> must be non zero" );
  772.     elif n < 0  then
  773.         n := n - 1;
  774.     fi;
  775.     lst  := Factors( n );
  776.     pows := [];
  777.     for p  in Set( lst )  do
  778.     Add( pows, p );
  779.         Add( pows, Number( lst, x -> x = p ) );
  780.     od;
  781.     return pows;
  782.  
  783. end;
  784.  
  785.  
  786. #############################################################################
  787. ##
  788. #F  ProductPP( <l>, <r> ) . . . . . . . . . . . . product of two prime powers
  789. ##
  790. ProductPP := function( l, r )
  791.     local   res, p1, p2, ps, p, i, n;
  792.  
  793.     if l = []  then
  794.     return r;
  795.     elif r = []  then
  796.     return l;
  797.     fi;
  798.     res := [];
  799.     p1  := Sublist( l, 2 * [ 1 .. Length( l ) / 2 ] - 1 );
  800.     p2  := Sublist( r, 2 * [ 1 .. Length( r ) / 2 ] - 1 );
  801.     ps  := Set( Union( p1, p2 ) );
  802.     for p  in ps  do
  803.         n := 0;
  804.     Add( res, p );
  805.         i := Position( p1, p );
  806.         if i <> false   then
  807.         n := l[ 2*i ];
  808.         fi;
  809.         i := Position( p2, p );
  810.         if i <> false  then
  811.         n := n + r[ 2*i ];
  812.         fi;
  813.         Add( res, n );
  814.     od;
  815.     return res;
  816.  
  817. end;
  818.  
  819.  
  820. #############################################################################
  821. ##
  822. #F  LcmPP( <l>, <r> ) . . . . . . . . . . . . lcm of prime powers <l> and <r>
  823. ##
  824. LcmPP := function( l, r )
  825.     local   res, p1, p2, ps, p, i, n;
  826.  
  827.     if l = []  then
  828.     return r;
  829.     elif r = []  then
  830.     return l;
  831.     fi;
  832.     res := [];
  833.     p1  := Sublist( l, 2 * [ 1 .. Length( l ) / 2 ] - 1 );
  834.     p2  := Sublist( r, 2 * [ 1 .. Length( r ) / 2 ] - 1 );
  835.     ps  := Set( Union( p1, p2 ) );
  836.     for p  in ps  do
  837.         n := 0;
  838.     Add( res, p );
  839.         i := Position( p1, p );
  840.         if i <> false   then
  841.         n := l[ 2*i ];
  842.         fi;
  843.         i := Position( p2, p );
  844.         if i <> false and n < r[ 2*i ]  then
  845.         n := r[ 2*i ];
  846.         fi;
  847.         Add( res, n );
  848.     od;
  849.     return res;
  850.  
  851. end;
  852.  
  853.  
  854. #############################################################################
  855. ##
  856.  
  857. #F  OrderKnownDividendList( <l>, <pp> )    . . . . . . . . . . . . . . . . local
  858. ##
  859. ##  Computes  an  integer  n  such  that  OnSets( <l>, n ) contains  only one
  860. ##  element e.  <pp> must be a list of prime powers of an integer d such that
  861. ##  n divides d. The functions returns the integer n and the element e.
  862. ##
  863. OrderKnownDividendList := function( l, pp )
  864.  
  865.     local   pp1,    # first half of <pp>
  866.             pp2,        # second half of <pp>
  867.             a,          # half exponent of first prime power
  868.             k,          # power of <l>
  869.             o,  o1,     # computed order of <k>
  870.             i;          # loop
  871.  
  872.     # if <pp> contains no element return order 1
  873.     if Length(pp) = 0  then
  874.         return [ 1, l[1] ];
  875.  
  876.     # if <l> contains only one element return order 1
  877.     elif Length(l) = 1  then
  878.         return [ 1, l[1] ];
  879.  
  880.     # if the dividend is a prime return
  881.     elif Length(pp) = 2 and pp[2] = 1  then
  882.         return [ pp[1], l[1]^pp[1] ];
  883.  
  884.     # if the dividend is a prime power divide and conquer
  885.     elif Length(pp) = 2  then
  886.         pp := Copy(pp);
  887.         a  := QuoInt( pp[2], 2 );
  888.         k  := OnSets( l, pp[1]^a );
  889.  
  890.         # if <k> is trivial try smaller dividend
  891.         if Length(k) = 1  then
  892.             pp[2] := a;
  893.             return OrderKnownDividendList( l, pp );
  894.  
  895.         # otherwise try to find order of <h>
  896.         else
  897.             pp[2] := pp[2] - a;
  898.             o := OrderKnownDividendList( k, pp );
  899.             return [ pp[1]^a*o[1], o[2] ];
  900.         fi;
  901.  
  902.     # split different primes into two parts
  903.     else
  904.         a   := 2 * QuoInt( Length(pp), 4 );
  905.         pp1 := Sublist( pp, [ 1 .. a ] );
  906.         pp2 := Sublist( pp, [ a+1 .. Length(pp) ] );
  907.  
  908.         # compute the order of <l>^<pp1>
  909.         k := l;
  910.         for i  in [ 1 .. Length(pp2)/2 ]  do
  911.             k := OnSets( k, pp2[2*i-1]^pp2[2*i] );
  912.         od;
  913.         o1 := OrderKnownDividendList( k, pp1 );
  914.  
  915.         # compute the order of <l>^<o1> and return
  916.         o := OrderKnownDividendList( OnSets( l, o1[1] ), pp2 );
  917.         return [ o1[1]*o[1], o[2] ];
  918.     fi;
  919.  
  920. end;
  921.  
  922.  
  923. #############################################################################
  924. ##
  925. #F  FiniteFieldPolynomialRingOps.OrderKnownDividend( <R>, <g>, <f>, <pp> )
  926. ##
  927. ##  Computes an integer n such that <g>^n = const  mod <f> where <g>  and <f>
  928. ##  are polynomials in <R> and <pp> is list  of prime powers of  an integer d
  929. ##  such that n divides  d.   The  functions  returns  the integer n  and the
  930. ##  element const.
  931. ##
  932. FiniteFieldPolynomialRingOps.OrderKnownDividend := function ( R, g, f, pp )
  933.     local   a,  q,  h,  o,  l,  k,  i,  j,  r,  pp1,  pp2,  n1,  n2;
  934.  
  935.     # if <g> is constant return order 1
  936.     InfoPoly2( "#I  OrderKnownDividend:\n" );
  937.     if 0 = Degree(g)  then
  938.     InfoPoly2( "#I    <g> is constant\n" );
  939.         l := [ 1, g.coefficients[1] ];
  940.         InfoPoly2( "#I  OrderKnownDividend returns ", l, "\n" );
  941.         return l;
  942.  
  943.     # if the dividend is a prime, we must compute g^pp[1] to get the constant
  944.     elif Length(pp) = 2 and pp[2] = 1  then
  945.         k := PowerMod( g, pp[1], f );
  946.         l := [ pp[1], k.coefficients[1] ];
  947.         InfoPoly2( "#I  OrderKnownDividend returns ", l, "\n" );
  948.         return l;
  949.  
  950.     # if the dividend is a prime power find the necessary power
  951.     elif Length(pp) = 2  then
  952.     InfoPoly2( "#I    prime power, divide and conquer\n" );
  953.         pp := Copy( pp );
  954.         a  := QuoInt( pp[2], 2 );
  955.     q  := pp[1] ^ a;
  956.         h  := PowerMod( R, g, q, f );
  957.  
  958.     # if <h> is constant try again with smaller dividend
  959.         if 0 = Degree(h)  then
  960.         pp[2] := a;
  961.         o := R.operations.OrderKnownDividend( R, g, f, pp );
  962.         else
  963.         pp[2] := pp[2] - a;
  964.         l := R.operations.OrderKnownDividend( R, h, f, pp );
  965.         o := [ q*l[1], l[2] ];
  966.         fi;
  967.         InfoPoly2( "#I  OrderKnownDividend returns ", o, "\n" );
  968.     return o;
  969.  
  970.     # split different primes.
  971.     else
  972.  
  973.         # divide primes
  974.     InfoPoly2( "#I    ", Length(pp)/2, " different primes\n" );
  975.     n1  := 1;  pp1 := [];
  976.         n2  := 1;  pp2 := [];
  977.         for i  in Reversed( [ 1 .. Length(pp)/2 ] )  do
  978.         if n1 < n2  then
  979.             Add( pp1, pp[2*i-1] );
  980.                 Add( pp1, pp[2*i] );
  981.                 n1 := n1 * pp[2*i-1];
  982.             else
  983.             Add( pp2, pp[2*i-1] );
  984.                 Add( pp2, pp[2*i] );
  985.                 n2 := n2 * pp[2*i-1];
  986.         fi;
  987.         od;
  988.  
  989.         # compute order for <pp1>
  990.         l := Length( pp2 );
  991.         q := 1;
  992.     for i  in [ 1 .. l/2 ]  do
  993.         q := pp2[2*i-1] ^ pp2[2*i];
  994.     od;
  995.         k := PowerMod( R, g, q, f );
  996.     o := R.operations.OrderKnownDividend( R, k, f, pp1 );
  997.  
  998.         # compute order for <pp2>
  999.         k := PowerMod( R, g, o[1], f );
  1000.     l := R.operations.OrderKnownDividend( R, k, f, pp2 );
  1001.         o := [ o[1]*l[1], l[2] ];
  1002.         InfoPoly2( "#I  OrderKnownDividend returns ", o, "\n" );
  1003.         return o;
  1004.     fi;
  1005.  
  1006. end;
  1007.  
  1008.  
  1009. #############################################################################
  1010. ##
  1011. #F  FiniteFieldPolynomialRingOps.UpperBoundOrder( <R>, <f> )  . . .upper bound
  1012. ##
  1013. ##  Computes the  irreducible factors f_i  of a polynomial  <f>  over a field
  1014. ##  with  p^n  elements.  It returns a list  l of triples (f_i,a_i,pp_i) such
  1015. ##  that the p-part  of x  mod  f_i is p^a_i and  the p'-part divides d_i for
  1016. ##  which the prime powers pp_i are given.
  1017. ##
  1018. FiniteFieldPolynomialRingOps.UpperBoundOrder := function( R, f )
  1019.     local   F,  fs,  a,  pp,  f,  d,  L,  phi,  B,  i;
  1020.  
  1021.     # factorize <f> into irreducible factors
  1022.     InfoPoly2( "#I  UpperBoundOrder:\n" );
  1023.     fs := Collected( Factors( R, f ) );
  1024.  
  1025.     # get the field over which the polynomials are written
  1026.     F  := R.baseRing;
  1027.  
  1028.     # <phi>(m) gives ( minpol of 1^(1/m) )( F.char )
  1029.     L := [ PrimePowersInt( F.char-1 ) ];
  1030.     phi := function( m )
  1031.         local    x, d, pp, i;
  1032.         if not IsBound( L[m] )  then
  1033.         x := F.char^m-1;
  1034.             for d  in Difference( DivisorsInt( m ), [m] )  do
  1035.             pp := phi( d );
  1036.                 for i  in [ 1 .. Length(pp)/2 ]  do
  1037.                 x := x / pp[2*i-1]^pp[2*i];
  1038.                 od;
  1039.             od;
  1040.             L[m] := PrimePowersInt( x );
  1041.         fi;
  1042.         return L[m];
  1043.     end;
  1044.  
  1045.     # compute a_i and pp_i
  1046.     B := [];
  1047.     for i  in [ 1 .. Length(fs) ]  do
  1048.  
  1049.         # p-part is p^Roof(Log_p(e_i)) where e_i is the multiplicity of f_i
  1050.         a := 0;
  1051.         if fs[i][2] > 1  then
  1052.             a := 1+LogInt(fs[i][2]-1,F.char);
  1053.         fi;
  1054.  
  1055.         # p'-part: (p^n)^d_i-1/(p^n-1) where d_i is the degree of f_i
  1056.         d := Degree(fs[i][1]);
  1057.         InfoPoly2( "#I    irreducible factor of degree ", d, "\n" );
  1058.     pp := [];
  1059.         for f  in DivisorsInt( d*F.degree )  do
  1060.             if F.degree mod f <> 0  then
  1061.             pp := ProductPP( phi(f), pp );
  1062.             fi;
  1063.         od;
  1064.  
  1065.     # add <a> and <pp> to <B>
  1066.         Add( B, [fs[i][1],a,pp] );
  1067.     od;
  1068.  
  1069.     # OK, that's it
  1070.     InfoPoly2( "#I  UpperBoundOrder returns ", B, "\n" );
  1071.     return B;
  1072.  
  1073. end;
  1074.  
  1075.  
  1076. #############################################################################
  1077. ##
  1078. #F  FiniteFieldPolynomialRingOps.OrderScalar( <R>, <f> )  . . . . order of <f>
  1079. ##
  1080. ##  Return an  integer n  and a finite field element  const  such that x^n  =
  1081. ##  const mod <f>.
  1082. ##
  1083. FiniteFieldPolynomialRingOps.OrderScalar := function( R, f )
  1084.     local   U,  O,  x,  n,  g,  o,  q;
  1085.  
  1086.     # <f> must not be divisible by x.
  1087.     if 0 < f.valuation  then
  1088.         Error( "<f> must have a non zero constant term" );
  1089.     fi;
  1090.  
  1091.     # if degree is zero, return
  1092.     if 0 = Degree(f)  then
  1093.         return [ 1, f.coefficients[1] ];
  1094.     fi;
  1095.  
  1096.     # use 'UpperBoundOrder' to split <f> into irreducibles
  1097.     U := R.operations.UpperBoundOrder( R, f );
  1098.  
  1099.     # run through the irrducibles and compute their order
  1100.     x := Polynomial( R.baseRing, [ R.baseRing.zero, R.baseRing.one ] );
  1101.     O := [];
  1102.     n := 1;
  1103.     for g  in U  do
  1104.         o := R.operations.OrderKnownDividend( R, x mod g[1], g[1], g[3] );
  1105.         q := R.baseRing.char^g[2];
  1106.         n := Lcm( n, o[1]*q );
  1107.     Add( O, [ o[1]*q, o[2]^q ] );
  1108.     od;
  1109.  
  1110.     # try to get the same constant in each block
  1111.     U := [];
  1112.     q := Size( R.baseRing ) - 1;
  1113.     for g  in O  do
  1114.         AddSet( U, g[2]^((n/g[1]) mod q) );
  1115.     od;
  1116.  
  1117.     # return the order <n> times the order of <U>
  1118.     o := OrderKnownDividendList( U, PrimePowersInt(q) );
  1119.     return [ n*o[1], o[2] ];
  1120.  
  1121. end;
  1122.  
  1123.  
  1124. #############################################################################
  1125. ##
  1126.  
  1127. #E  Emacs . . . . . . . . . . . . . . . . . . . . . . . local emacs variables
  1128. ##
  1129. ##  Local Variables:
  1130. ##  mode:               outline
  1131. ##  outline-regexp:     "#F\\|#V\\|#E"
  1132. ##  eval:               (local-set-key "\t" 'indent-for-tab-command)
  1133. ##  eval:               (hide-body)
  1134. ##  End:
  1135. ##
  1136.