
`mod/Normform` := `See ?Frobenius, ?Ratjordan, ?Jordansymbolic`:
# This file contains three routines for the computation of normal forms of
# matrices with entries in Z/pZ, p a prime number.
# The routines are:
#  - Frobenius
#  - Ratjordan
#  - Jordansymbolic
# Descriptions of the algorithms and comments are omitted since they are
# essentially the same as the routines with lower case names. So see the
# file normform for descriptionts and comments.
# For help see the on-line help facility so type one of these:
#  - ?Frobenius
#  - ?Ratjordan
#  - ?Jordansymbolic
#
# AUTHORS: T.M.L. Mulders, A.H.M. Levelt
#          Mathematics Department
#          University of Nijmegen.
# E-mail:  mulders@sci.kun.nl, ahml@sci.kun.nl
#
# January 1993

macro(Id=`Normform/Id`,
      basis=`Normform/basis`,
      companion_to_ratjordanmod=`Normform/companion_to_ratjordanmod`,
      cyclic_to_frobeniusmod=`Normform/cyclic_to_frobeniusmod`,
      cyclic_vectorsmod=`Normform/cyclic_vectorsmod`,
      deg_sort=`Normform/deg_sort`,
      find_companion=`Normform/find_companion`,
      find_ratjblock=`Normform/find_ratjblock`,
      frobenius_to_invfact=`Normform/frobenius_to_invfact`,
      frobenius_to_ratjordanmod=`Normform/frobenius_to_ratjordanmod`,
      frobeniusformmod=`Normform/frobeniusformmod`,
      identitymatrix=`Normform/identitymatrix`,
      invariant_to_jordan=`Normform/invariant_to_jordan`,
      invfact_to_frobenius=`Normform/invfact_to_frobenius`,
      invmod=`Normform/invmod`,
      jordansymbolicformmod=`Normform/jordansymbolicformmod`,
      make_ratj_block=`Normform/make_ratj_block`,
      matmultmod=`Normform/matmultmod`,
      mysmithmod=`Normform/mysmithmod`,
      plist_to_polycompanion=`Normform/plist_to_polycompanion`,
      priminv_to_ratjordan=`Normform/priminv_to_ratjordan`,
      ratjordan_to_jordanmod=`Normform/ratjordan_to_jordanmod`,
      ratjordan_to_priminv=`Normform/ratjordan_to_priminv`,
      ratjordanformmod=`Normform/ratjordanformmod`,
      uppersmithmod=`Normform/uppersmithmod`,
      zero_matrix=`Normform/zero_matrix`):

############################################################################
############################################################################
##
##          Frobenius
##
###########################################################################
###########################################################################
# A Maple program for the computation of the Frobenius normal form of
# a matrix with entries in Z/pZ, p a prime number.
# Authors: T.M.L. Mulders, A.H.M. Levelt
#          Mathematics Department
#          University of Nijmegen.
# E-mail: mulders@sci.kun.nl, ahml@sci.kun.nl
#
# January 1993


`mod/Frobenius`:=proc(A,P,Pinv)
local p,AA,n,i,j,oldmod,r;

  if not type(A,'matrix') then
    AA:=evalm(A)
  else
    AA:=copy(A)
  fi;
  if not type(AA,'matrix'('anything','square')) then
    ERROR(`expecting a square matrix`)
  fi;

  n:=linalg[rowdim](AA);
  for i to n do
    for j to n do
      if type(AA[i,j],indexed) then
        ERROR(`Unassigned array elements`)
      fi
    od
  od;

  oldmod:=`mod`;
  `mod`:=modp;

  p:=args[nargs];

  for i to n do
    for j to n do
      AA[i,j]:=AA[i,j] mod p
    od
  od;

  if nargs=2 then
    r:=frobeniusformmod(p,AA)
  elif nargs=3 then
    r:=frobeniusformmod(p,AA,'P')
  else
    r:=frobeniusformmod(p,AA,'P','Pinv')
  fi;

  if oldmod=mods then
    `mod`:=mods
  fi;

  if nargs>=3 then
    P:=map(`mod`,P,p)
  fi;
  if nargs=4 then
    Pinv:=map(`mod`,Pinv,p)
  fi;

  map(`mod`,r,p)

end:

`Normform/frobeniusformmod`:=proc(p,A,P,Pinv)
local x,plist,inv_fact,V,Vinv,T,Tinv;

  if nargs=2 then
    plist:=cyclic_vectorsmod(p,A,x);
    inv_fact:=cyclic_to_frobeniusmod(p,plist,x)
  elif nargs=3 then
    plist:=cyclic_vectorsmod(p,A,x,'V');
    inv_fact:=cyclic_to_frobeniusmod(p,plist,x,'T');
    P:=matmultmod(p,V,T)
  else
    plist:=cyclic_vectorsmod(p,A,x,'V','Vinv');
    inv_fact:=cyclic_to_frobeniusmod(p,plist,x,'T','Tinv');
    P:=matmultmod(p,V,T);
    Pinv:=matmultmod(p,Tinv,Vinv)
  fi;

  invfact_to_frobenius(inv_fact,x)

end:


`Normform/cyclic_vectorsmod`:=proc(p,A,x,V,Vinv)
local i,j,l,n,r,carrier,car,U,Uinv,S,u,v,s,lincomb,plist,c,temp;

  n:=linalg[rowdim](A);
  U:=array(1..n,1..n);
  S:=array(1..n,1..n);
  plist:=[];
  if nargs>=4 then
    V:=array(1..n,1..n)
  fi;
  if nargs=5 then
    Vinv:=array(1..n,1..n)
  fi;

  carrier:=array(1..n);
  for i to n do carrier[i]:=0 od;
  lincomb:=array(1..n);

  r:=0;
  while r<n do

    for i to n while carrier[i]<>0 do od;   # find first gap
    v:=basis(n,i);

    do
      u:=copy(v);
      for i to n do lincomb[i]:=0 od;

      for i to n do
        car:=carrier[i];
        if car<>0 and u[i]<>0 then
          c:=u[i]/U[i,car] mod p;
          u[i]:=0;
          for j from i+1 to n do u[j]:=u[j]-c*U[j,car] mod p od;
          lincomb[car]:=c  
        fi
      od;

      i:=1;
      while i<=n and u[i]=0 do i:=i+1 od;
      if i<=n then

        r:=r+1;  
        carrier[i]:=r;   # this basis-element carries coordinate i

        for j from i to n do U[j,r]:=u[j] od;
        if nargs>=4 then
          for j to n do V[j,r]:=v[j] od;
        fi;
        for j to r-1 do
          temp:=lincomb[j];
          for l from j+1 to r-1 do temp:=temp+S[j,l]*lincomb[l] mod p od;
          S[j,r]:=-temp
        od;
        S[r,r]:=1;

        for i to n do
          temp:=0;
          for j to n do
            temp:=temp+A[i,j]*v[j] mod p
          od;
          u[i]:=temp
        od;
        v:=copy(u);
      else
        break
      fi
    od;

    s:=array(1..r);
    for j to r do
      temp:=lincomb[j];
      for l from j+1 to r do temp:=temp+S[j,l]*lincomb[l] mod p od;
      s[j]:=temp
    od;
    plist:=[op(plist),x^r-sum('s[r+1-j]*x^(r-j)','j'=1..r)]

  od;

  if nargs=5 then
    Uinv:=invmod(p,U,carrier);
    for i to n do
      for j to n do
        temp:=0;
        for l from i to n do temp:=temp+S[i,l]*Uinv[l,j] mod p od;
        Vinv[i,j]:=temp
      od
    od
  fi;

  plist

end:


`Normform/cyclic_to_frobeniusmod`:=proc(p,plist,x,T,Tinv)
local r,d,i,j,k,n,G,L,Linv,D,c,inv_fact,columnT,rowT,ii,jj,rr,q,
      columnTinv,rowTinv,US,S;

  r:=nops(plist);
  d:=array(0..r);
  d[0]:=0;
  for j to r do d[j]:=degree(plist[j],x) od;
  n:=d[r];

  G:=zero_matrix(r,r);
  for j to r do
    for i to j-1 do
      G[i,j]:=sum('coeff(plist[j],x,k)*x^(k-d[i-1])','k'=d[i-1]..d[i]-1)
    od;
    G[j,j]:=sum('coeff(plist[j],x,k)*x^(k-d[j-1])','k'=d[j-1]..d[j])
  od;

  if nargs=3 then
    US:=uppersmithmod(p,G,x);
    S:=mysmithmod(p,US,x)
  elif nargs=4 then
    US:=uppersmithmod(p,G,x,'L');
    S:=mysmithmod(p,US,x,'L')
  else
    US:=uppersmithmod(p,G,x,'L','Linv');
    S:=mysmithmod(p,US,x,'L','Linv')
  fi;

  D:=array(1..r);
  for i to r do D[i]:=degree(S[i,i],x) od;

  if nargs>=4 then

    c:=array(1..r);
    T:=array(1..n,1..n);
    columnT:=0;
    for i to r do
      for k to r do c[k]:=L[k,i] od;
      for j to D[i] do
        columnT:=columnT+1;
        for ii from r by -1 to 1 do
          q:=Quo(c[ii],G[ii,ii],x,'rr') mod p;
          c[ii]:=rr;
          for jj to ii-1 do
            c[jj]:=Expand(c[jj]-q*G[jj,ii]) mod p
          od
        od;
        rowT:=0;
        for ii to r do
          for jj to d[ii]-d[ii-1] do
            rowT:=rowT+1;
            T[rowT,columnT]:=coeff(c[ii],x,jj-1)
          od
        od;
        for ii to r do c[ii]:=Expand(c[ii]*x) mod p od
      od
    od

  fi;

  if nargs=5 then

    Tinv:=array(1..n,1..n);
    columnTinv:=0;
    for i to r do
      for k to r do c[k]:=Linv[k,i] od;
      for j to d[i]-d[i-1] do
        columnTinv:=columnTinv+1;
        rowTinv:=0;
        for ii to r do
          c[ii]:=Rem(c[ii],S[ii,ii],x) mod p;
          for jj to D[ii] do
            rowTinv:=rowTinv+1;
            Tinv[rowTinv,columnTinv]:=coeff(c[ii],x,jj-1)
          od
        od;
        for ii to r do c[ii]:=Expand(c[ii]*x) mod p od
      od
    od

  fi;

  inv_fact:=[];
  for i to r do
    if D[i]>0 then inv_fact:=[op(inv_fact),S[i,i]] fi
  od;

  inv_fact

end:


`Normform/uppersmithmod`:=proc(p,B,x,L,Linv)
local i,j,k,n,r,s,t,A,d,q;

  A:=copy(B);
  n := linalg[rowdim](A);

  if nargs>=4 then
    L:=Id(n)
  fi;
  if nargs=5 then
    Linv:=Id(n)
  fi;

  for j from 2 to n do
    for i to j-1 do
      d:=Gcdex(A[i,i],A[j,j],x,'s','t') mod p;
      q:=Quo(A[i,j],d,x,'r') mod p;
      A[i,j]:=r;
      for k to i-1 do
        A[k,j]:=Expand(A[k,j]-q*s*A[k,i]) mod p
      od;
      for k from j+1 to n do
        A[i,k]:=Expand(A[i,k]-q*t*A[j,k]) mod p
      od;
      if nargs>=4 then
        for k to i do
          L[k,j]:=Expand(L[k,j]+q*t*L[k,i]) mod p
        od
      fi;
      if nargs=5 then
        Linv[i,j]:=Expand(-q*t) mod p
      fi
    od
  od;

  op(A)

end:


`Normform/mysmithmod`:=proc(p,B,x,L,Linv)
local a,b,g,i,j,k,n,r,s,t,temp,A,isClear,q,lc;

  n:=linalg[rowdim](B);
  A:=copy(B);

  for k to n do
    isClear:=false;
    while not isClear do
      for i from k+1 to n do
        if A[i,k]=0 then next fi;
        g:=Gcdex(A[k,k],A[i,k],x,'s','t') mod p;
        a:=Quo(A[k,k],g,x) mod p;b:=Quo(A[i,k],g,x) mod p;
        for j from k+1 to n do
          temp:=Expand(s*A[k,j]+t*A[i,j]) mod p;
          A[i,j]:=Expand(a*A[i,j]-b*A[k,j]) mod p;
          A[k,j]:=temp
        od;
        if nargs>=4 then
          for j to n do
            temp:=Expand(a*L[j,k]+b*L[j,i]) mod p;
            L[j,i]:=Expand(-t*L[j,k]+s*L[j,i]) mod p;
            L[j,k]:=temp
          od
        fi;
        if nargs=5 then
          for j to n do
            temp:=Expand(s*Linv[k,j]+t*Linv[i,j]) mod p;
            Linv[i,j]:=Expand(a*Linv[i,j]-b*Linv[k,j]) mod p;
            Linv[k,j]:=temp
          od
        fi;
        A[k,k]:=g;
        A[i,k]:=0
      od;
      isClear:=true;
      for i from k+1 to n do
        A[k,i]:=Rem(A[k,i],A[k,k],x,'q') mod p;
      od;
      for i from k+1 to n do
        if A[k,i]=0 then next fi;
        g:=Gcdex(A[k,k],A[k,i],x,'s','t') mod p;
        a:=Quo(A[k,k],g,x) mod p;b:=Quo(A[k,i],g,x) mod p;
        for j from k+1 to n do
          temp:=Expand(s*A[j,k]+t*A[j,i]) mod p;
          A[j,i]:=Expand(a*A[j,i]-b*A[j,k]) mod p;
          A[j,k]:=temp
        od;
        A[k,k]:=g;
        A[k,i]:=0;
        isClear:=false;
      od
    od
  od;
  r:=0;
  for i to n do
    if A[i,i]<>0 then
      r:=r+1;
      lc:=lcoeff(A[i,i],x);
      A[r,r]:=A[i,i]/lc mod p;
      if i<>r then
        A[i,i]:=0;
        if nargs>=4 then
          for j to n do
            temp:=L[j,r];
            L[j,r]:=L[j,i];
            L[j,i]:=temp
          od
        fi;
        if nargs=5 then
          for j to n do
            temp:=Linv[r,j];
            Linv[r,j]:=Linv[i,j];
            Linv[i,j]:=temp
          od
        fi
      fi
    fi
  od;
  for i to r-1 do
    for j from i+1 to r while A[i,i]<>1 do
      g:=Gcdex(A[i,i],A[j,j],x,'s','t') mod p;
      a:=Quo(A[i,i],g,x) mod p; b:=Quo(A[j,j],g,x) mod p;
      A[i,i]:=g;
      A[j,j]:=Expand(a*A[j,j]) mod p;
      if nargs>=4 then
        for k to n do
          temp:=Expand(a*L[k,i]+b*L[k,j]) mod p;
          L[k,j]:=Expand(-t*L[k,i]+s*L[k,j]) mod p;
          L[k,i]:=temp
        od
      fi;
      if nargs=5 then
        for k to n do
          temp:=Expand(s*Linv[i,k]+t*Linv[j,k]) mod p;
          Linv[j,k]:=Expand(a*Linv[j,k]-b*Linv[i,k]) mod p;
          Linv[i,k]:=temp
        od
      fi
    od
  od;

  op(A)

end:


`Normform/invmod`:=proc(p,A,carrier)
local B,n,i,j,k,temp;
  n:=linalg[rowdim](A);
  B:=array(1..n,1..n);
  for i to n do
    for j to i-1 do
      temp:=0;
      for k from j to i-1 do
        temp:=temp+A[i,carrier[k]]*B[carrier[k],j] mod p
      od;
      B[carrier[i],j]:=-temp/A[i,carrier[i]] mod p
    od;
    B[carrier[i],i]:=1/A[i,carrier[i]] mod p;
    for j from i+1 to n do
      B[carrier[i],j]:=0
    od
  od;
  op(B)
end:


`Normform/matmultmod`:=proc(p,A,B)
local C,n,i,j,k,temp;
  n:=linalg[rowdim](A);
  C:=array(1..n,1..n);
  for i to n do
    for j to n do
      temp:=0;
      for k to n do
        temp:=temp+A[i,k]*B[k,j] mod p
      od;
      C[i,j]:=temp
    od
  od;
  op(C)
end:


`Normform/zero_matrix`:=proc(r,c)
local A,i,j;
  A:=array(1..r,1..c);
  for i to r do
    for j to c do
      A[i,j]:=0
    od
  od;
  op(A)
end:

`Normform/Id`:=proc(n)
local i,j,I;
  I:=array(1..n,1..n);
  for i to n do
    for j to n do
      I[i,j]:=0
    od
  od;
  for i to n do I[i,i]:=1 od;
  op(I)
end:


`Normform/basis`:=proc(n,i)
local b,j;
  b:=array(1..n);
  for j to n do b[j]:=0 od;
  b[i]:=1;
  op(b)
end:


`Normform/invfact_to_frobenius`:=proc(inv_fact,x)
local i;
  linalg[diag](seq(linalg[companion](inv_fact[i],x),i=1..nops(inv_fact)))
end:


`Normform/plist_to_polycompanion`:=proc(plist,x)
local r,d,n,A,i,j,k;
  r:=nops(plist);
  d:=array(0..r);
  d[0]:=0;
  for i to r do d[i]:=degree(plist[i],x) od;
  n:=d[r];
  A:=zero_matrix(n,n);
  for i to r do
    for j from d[i-1]+2 to d[i] do A[j,j-1]:=1 od;
    for j from i to r do
      for k from d[i-1]+1 to d[i] do
        A[k,d[j]]:=-coeff(plist[j],x,k-1)
      od
    od
  od;
  op(A)
end:


`help/text/Frobenius` := TEXT(
`FUNCTION: Frobenius - compute the Frobenius normal form of a matrix with`,
`                      entries in Z/pZ, p a prime number`,
`   `,
`CALLING SEQUENCE:`,
`   Frobenius(A) mod p`,
`   Frobenius(A, 'P') mod p`,
`   Frobenius(A, 'P', 'Pinv') mod p`,
`   `,
`PARAMETERS:`,
`   p           - a prime number`,
`   A           - a square matrix`,
`   'P', 'Pinv' - (optional) assigned the transformation matrix and its inverse`,
`   `,
`SYNOPSIS:`,
`- The call Frobenius(A) mod p computes and returns the Frobenius normal`,
`  form F of a matrix A with entries in Z/pZ.`,
`   `,
`- F has the following structure: F = diag(C1,C2,..,Ck) where the Ci's are com-`,
`  panion matrices associated with polynomials p1, p2,.., pk with the properties`,
`  that pi divides p(i+1), for i = 1..k-1.`,
`   `,
`- If called in the form Frobenius(A, 'P') mod p, then P will be assigned the`,
`  transformation matrix corresponding to the Frobenius normal form, that is,`,
`  the matrix P such that inverse(P) * A * P = F.`,
`  If called in the form Frobenius(A, 'P', 'Pinv') mod p, then Pinv will be`,
`  assigned the inverse of P.`,
`   `,
`- The Frobenius normal form defined in this way is unique (ie. if we require`,
`  that pi divides p(i+1) as above).`,
`   `,
`EXAMPLES:`,
`> A := array([[10,-5,-5,8,3,0],`,
`              [-4,2,-10,-7,-5,-5],`,
`              [-8,2,7,3,7,5],`,
`              [-6,-7,-7,-7,10,7],`,
`              [-4,-3,-3,-6,8,-9],`,
`              [-2,5,-5,9,7,-4]]);`,
`                           [ 10  -5   -5   8   3   0 ]`,
`                           [                         ]`,
`                           [ -4   2  -10  -7  -5  -5 ]`,
`                           [                         ]`,
`                           [ -8   2   7    3   7   5 ]`,
`                      A := [                         ]`,
`                           [ -6  -7   -7  -7  10   7 ]`,
`                           [                         ]`,
`                           [ -4  -3   -3  -6   8  -9 ]`,
`                           [                         ]`,
`                           [ -2   5   -5   9   7  -4 ]`,
`   `,
`> Frobenius(A,'P','Pinv') mod 23;`,
`                             [ 0  17  0  0  0   0 ]`,
`                             [                    ]`,
`                             [ 1  19  0  0  0   0 ]`,
`                             [                    ]`,
`                             [ 0   0  0  0  0  10 ]`,
`                             [                    ]`,
`                             [ 0   0  1  0  0   5 ]`,
`                             [                    ]`,
`                             [ 0   0  0  1  0  15 ]`,
`                             [                    ]`,
`                             [ 0   0  0  0  1  20 ]`,
`   `,
`> op(P);op(Pinv);`,
`                           [  2   9  22  13  15   3 ]`,
`                           [                        ]`,
`                           [  8   5   0   4  11  20 ]`,
`                           [                        ]`,
`                           [ 22  12   0   8  16  15 ]`,
`                           [                        ]`,
`                           [  6  21   0   6  11   5 ]`,
`                           [                        ]`,
`                           [ 15   3   0   4   5  18 ]`,
`                           [                        ]`,
`                           [ 16  21   0   2   5  13 ]`,
`   `,
`                           [  0   8  15   3  1   5 ]`,
`                           [                       ]`,
`                           [  0  19   4  18  0   7 ]`,
`                           [                       ]`,
`                           [ 22  14   0   2  7  16 ]`,
`                           [                       ]`,
`                           [  0   6  14   7  1  13 ]`,
`                           [                       ]`,
`                           [  0   9  13   5  1   5 ]`,
`                           [                       ]`,
`                           [  0  17  21  12  0   7 ]`,
`   `,
`> ``mod``:=mods:`,
`> Frobenius(A,'P','Pinv') mod 23;`,
`                           [ 0  -6  0  0  0   0 ]`,
`                           [                    ]`,
`                           [ 1  -4  0  0  0   0 ]`,
`                           [                    ]`,
`                           [ 0   0  0  0  0  10 ]`,
`                           [                    ]`,
`                           [ 0   0  1  0  0   5 ]`,
`                           [                    ]`,
`                           [ 0   0  0  1  0  -8 ]`,
`                           [                    ]`,
`                           [ 0   0  0  0  1  -3 ]`,
`   `,
`> op(P);op(Pinv);`,
`                                [  2   9   -1  -10  -8   3  ]`,
`                                [                           ]`,
`                                [  8   5    0   4   11   -3 ]`,
`                                [                           ]`,
`                                [ -1  -11   0   8   -7   -8 ]`,
`                                [                           ]`,
`                                [  6   -2   0   6   11   5  ]`,
`                                [                           ]`,
`                                [ -8   3    0   4    5   -5 ]`,
`                                [                           ]`,
`                                [ -7   -2   0   2    5  -10 ]`,
`   `,
`                                 [  0   8   -8   3   1   5  ]`,
`                                 [                          ]`,
`                                 [  0  -4   4    -5  0   7  ]`,
`                                 [                          ]`,
`                                 [ -1  -9   0    2   7   -7 ]`,
`                                 [                          ]`,
`                                 [  0   6   -9   7   1  -10 ]`,
`                                 [                          ]`,
`                                 [  0   9  -10   5   1   5  ]`,
`                                 [                          ]`,
`                                 [  0  -6   -2  -11  0   7  ]`,
`   `,
`SEE ALSO:  smithex, ismithex, frobenius, ratjordan, jordansymbolic, jordan,`,
`           Ratjordan, Jordansymbolic`  
):



############################################################################
############################################################################
##
##          Ratjordan
##
###########################################################################
###########################################################################
# A Maple program for the computation of the rational Jordan normal form
# of a matrix with entries in Z/pZ, p a prime number.
# Authors: T.M.L. Mulders, A.H.M. Levelt
#          Mathematics Department
#          University of Nijmegen.
# E-mail: mulders@sci.kun.nl, ahml@sci.kun.nl
#
# January 1993


`mod/Ratjordan`:=proc(A,P,Pinv)
local p,AA,n,i,j,oldmod,r;

  if not type(A,'matrix') then
    AA:=evalm(A)
  else
    AA:=copy(A)
  fi;
  if not type(AA,'matrix'('anything','square')) then
    ERROR(`expecting a square matrix`)
  fi;

  n:=linalg[rowdim](AA);
  for i to n do
    for j to n do
      if type(AA[i,j],indexed) then
        ERROR(`Unassigned array elements`)
      fi
    od
  od;

  oldmod:=`mod`;
  `mod`:=modp;

  p:=args[nargs];

  for i to n do
    for j to n do
      AA[i,j]:=AA[i,j] mod p
    od
  od;

  if nargs=2 then
    r:=ratjordanformmod(p,AA)
  elif nargs=3 then
    r:=ratjordanformmod(p,AA,'P')
  else
    r:=ratjordanformmod(p,AA,'P','Pinv')
  fi;

  if oldmod=mods then
    `mod`:=mods
  fi;

  if nargs>=3 then
    P:=map(`mod`,P,p)
  fi;
  if nargs=4 then
    Pinv:=map(`mod`,Pinv,p)
  fi;

  map(`mod`,r,p)

end:

`Normform/ratjordanformmod`:=proc(p,A,P,Pinv)
local F,prim_inv,x,T,Tinv,S,Sinv;

  if nargs=2 then
    F:=frobeniusformmod(p,A);
    prim_inv:=frobenius_to_ratjordanmod(p,F,x)
  elif nargs=3 then
    F:=frobeniusformmod(p,A,'T');
    prim_inv:=frobenius_to_ratjordanmod(p,F,x,'S');
    P:=matmultmod(p,T,S)
  else
    F:=frobeniusformmod(p,A,'T','Tinv');
    prim_inv:=frobenius_to_ratjordanmod(p,F,x,'S','Sinv');
    P:=matmultmod(p,T,S);
    Pinv:=matmultmod(p,Sinv,Tinv)
  fi;

  priminv_to_ratjordan(prim_inv,x)

end:


`Normform/frobenius_to_ratjordanmod`:=proc(pp,F,x,S,Sinv)
local P,inv_fact,g,L,h,fact_mat,r,N,T_list,i,j,k,facts,T,n,cols,prim_inv,
      exp_list,G,p,e,Tinv_list,Q,Qinv,d,degp,count,Tinv;

  inv_fact:=frobenius_to_invfact(F,x);
  r:=nops(inv_fact);

  g:=[inv_fact[1],seq(Quo(inv_fact[i],inv_fact[i-1],x) mod pp,i=2..r)];
  L:=[];
  for i to r do
    h:=Factors(g[i]) mod pp;
    h:=h[2];
    L:=[op(L),seq([i,h[j][1],h[j][2]],j=1..nops(h))]
  od;
  P:=deg_sort([op({seq(L[i][2],i=1..nops(L))})],x);
  N:=nops(P);
  G:=array(1..r,1..N);
  fact_mat:=array(1..r,1..N);
  for i to r do for j to N do G[i,j]:=0; fact_mat[i,j]:=0 od od;
  for k to nops(L) do
    i:=L[k][1];
    p:=L[k][2];
    e:=L[k][3];
    for j to N do
      if p=P[j] then break fi
    od;
    G[i,j]:=e
  od;
  for j to N do fact_mat[1,j]:=G[1,j] od;
  for i from 2 to r do
    for j to N do
      fact_mat[i,j]:=fact_mat[i-1,j]+G[i,j]
    od
  od;

  if nargs>=4 then

    T_list:=[];
    if nargs=5 then
      Tinv_list:=[]
    fi;
    for i to r do
      facts:=[];
      for j to N do
        if fact_mat[i,j]<>0 then facts:=[op(facts),[P[j],fact_mat[i,j]]] fi
      od;
      if nargs=4 then
        companion_to_ratjordanmod(pp,facts,x,inv_fact[i],'Q')
      else
        companion_to_ratjordanmod(pp,facts,x,inv_fact[i],'Q','Qinv');
        Tinv_list:=[op(Tinv_list),op(Qinv)]
      fi;
      T_list:=[op(T_list),op(Q)];
    od;

    d:=array(1..r,1..N);
    degp:=array(1..r);
    for i to r do
      for j to N do
        d[i,j]:=degree(P[j],x)*fact_mat[i,j]
      od;
      degp[i]:=sum('d[i,j]','j'=1..N)
    od;
    cols:=[];
    for j to N do
      for i to r do
        count:=sum('degp[k]','k'=1..i-1)+sum('d[i,k]','k'=1..j-1);
        for h to d[i,j] do
          cols:=[op(cols),count+h]
        od
      od
    od;

    T:=linalg[diag](op(T_list));
    n:=linalg[rowdim](T);
    S:=array(1..n,1..n);
    for i to n do
      for j to n do
        S[i,j]:=T[i,cols[j]]
      od
    od;

    if nargs=5 then
      Tinv:=linalg[diag](op(Tinv_list));
      Sinv:=array(1..n,1..n);
      for i to n do
        for j to n do
          Sinv[i,j]:=Tinv[cols[i],j]
        od
      od
    fi

  fi;

  prim_inv:=[];
  for j to N do
    exp_list:=[];
    for i to r do
      if fact_mat[i,j]<>0 then exp_list:=[op(exp_list),fact_mat[i,j]] fi
    od;
    prim_inv:=[op(prim_inv),[P[j],exp_list]]
  od;

  prim_inv

end:

`Normform/deg_sort`:=proc(l,x)
local ll,n,i,j;
  ll:=l;
  n:=nops(ll);
  for i from 1 to nops(ll)-1 do
    for j from i+1 to nops(ll) do
      if degree(ll[j],x)<degree(ll[i],x) then
        ll:=[op(1..i-1,ll),ll[j],op(i..j-1,ll),op(j+1..n,ll)]
      fi
    od
  od;
  ll
end:


`Normform/companion_to_ratjordanmod`:=proc(p,fact_list,x,f,Q,Qinv)
local i,j,k,r,g_list,u_list,bbasis,q,e,d,qpower,diffq,
      part_basis,ratj_basis,n,s,t,g,rowQinv,pol_lincomb,qq,rr,
      lincomb,index,v,u,a;

  r:=nops(fact_list);
  n:=degree(f,x);

  g_list:=[seq(Expand(fact_list[i][1]^fact_list[i][2]) mod p,i=1..r)];

  u_list:=array(1..r);
  if r=1 then u_list[1]:=1
  else
    Gcdex(g_list[1],g_list[2],x,'s','t') mod p;
    u_list[1]:=Expand(t*g_list[2]) mod p;
    u_list[2]:=Expand(s*g_list[1]) mod p;
    g:=Expand(g_list[1]*g_list[2]) mod p;
    for i from 3 to r do
      Gcdex(g,g_list[i],x,'s','t') mod p;
      for j to i-1 do
        u_list[j]:=Rem(u_list[j]*t*g_list[i],f,x) mod p
      od;
      u_list[i]:=Expand(s*g) mod p;
      g:=Expand(g*g_list[i]) mod p
    od
  fi;

  bbasis:=[];
  rowQinv:=0;

  Q:=array(1..n,1..n);
  if nargs=6 then
    Qinv:=array(1..n,1..n)
  fi;

  for i to r do
    q:=fact_list[i][1];
    e:=fact_list[i][2];
    d:=degree(q,x);

    qpower:=array(1..e+1);
    qpower[1]:=1;
    for j from 2 to e+1 do qpower[j]:=Expand(q*qpower[j-1]) mod p od;

    if e>1 then
      diffq:=array(1..e-1);
      diffq[1]:=diff(q,x) mod p;
      for j from 2 to e-1 do diffq[j]:=diff(diffq[j-1],x) mod p od
    fi;

    part_basis:=array(1..e);
    part_basis[1]:=Expand(q^(e-1)) mod p;
    for j from 2 to e do
      part_basis[j]:=
      Normal(sum('(-1)^(k-1)/(k!)*diffq[k]*part_basis[j-k]','k'=1..j-1)/q) mod p
    od;

    ratj_basis:=array(1..e*d);
    ratj_basis[1]:=part_basis[1];
    for k from 2 to d do
      ratj_basis[k]:=Expand(x*ratj_basis[k-1]) mod p
    od;
    for j from 2 to e do
      ratj_basis[(j-1)*d+1]:=part_basis[j];
      for k from 2 to d do
        ratj_basis[(j-1)*d+k]:=
        Expand(x*ratj_basis[(j-1)*d+k-1]-ratj_basis[(j-2)*d+k-1]) mod p
      od;
    od;

    for k to e*d do
      t:=Rem(u_list[i]*ratj_basis[k],f,x) mod p;
      bbasis:=[op(bbasis),t]
    od;

    if nargs=6 then

      pol_lincomb:=array(1..e);
      for j to e do pol_lincomb[j]:=0 od;
      Gcdex(part_basis[e],qpower[e+1],x,'s','t') mod p;
      pol_lincomb[e]:=s;
      for j from e by -1 to 1 do
        qq:=Quo(pol_lincomb[j],q,x,'rr') mod p;
        pol_lincomb[j]:=rr;
        for k to j-1 do
          pol_lincomb[j-k]:=
          Rem(pol_lincomb[j-k]+qq*diffq[k]*(-1)^(k-1)/k!,qpower[j+1],x) mod p
        od
      od;
      lincomb:=array(1..e*d);
      for j to e do
        for k to d do
          index:=(j-1)*d+k;
          lincomb[index]:=coeff(pol_lincomb[j],x,k-1);
          for v to min(j-1,k-1) do
            lincomb[index-v*d-v]:=
            lincomb[index-v*d-v]+coeff(pol_lincomb[j],x,k-1)*binomial(k-1,v) mod p
          od
        od
      od;

      for u to e*d do
        rowQinv:=rowQinv+1;
        Qinv[rowQinv,1]:=lincomb[u]
      od;

      for v from 2 to n do
        a:=copy(lincomb);
        index:=0;
        for j to e-1 do
          index:=index+1;
          lincomb[index]:=-coeff(q,x,0)*a[j*d]+a[j*d+1] mod p;
          for k from 2 to d do
            index:=index+1;
            lincomb[index]:=a[(j-1)*d+k-1]-coeff(q,x,k-1)*a[j*d]+a[j*d+k] mod p
          od
        od;
        index:=index+1;
        lincomb[index]:=-coeff(q,x,0)*a[e*d] mod p;
        for k from 2 to d do
          index:=index+1;
          lincomb[index]:=a[(e-1)*d+k-1]-coeff(q,x,k-1)*a[e*d] mod p
        od;

        rowQinv:=rowQinv-e*d;
        for u to e*d do
          rowQinv:=rowQinv+1;
          Qinv[rowQinv,v]:=lincomb[u]
        od

      od

    fi
  od;

  for j to n do
    for k to n do
    Q[k,j]:=coeff(bbasis[j],x,k-1)
    od
  od;

  NULL
end:


`Normform/frobenius_to_invfact`:=proc(F,x)
local n,k,p,i,j,inv_fact;
  n:=linalg[rowdim](F);
  inv_fact:=[];
  k:=1;
  while k<=n do
    p:=0;
    i:=k+1;
    while i<=n and F[i,i-1]=1 do i:=i+1 od;
    for j from k to i-1 do
      p:=p-F[j,i-1]*x^(j-k)
    od;
    p:=sort(p+x^(i-k));
    inv_fact:=[op(inv_fact),p];
    k:=i
  od;
  inv_fact
end:


`Normform/priminv_to_ratjordan`:=proc(prim_inv,x)
local r,i,j,p,exp_list,block_list;
  r:=nops(prim_inv);
  block_list:=[];
  for i to r do
    p:=prim_inv[i][1];
    exp_list:=prim_inv[i][2];
    for j to nops(exp_list) do
      block_list:=[op(block_list), make_ratj_block(p,x,exp_list[j])]
    od
  od;
  linalg[diag](op(block_list))
end:


`Normform/make_ratj_block`:=proc(p,x,e)
local C,d,n,J_block,i;
  C:=linalg[companion](p,x);
  d:=degree(p,x);
  n:=d*e;
  J_block:=zero_matrix(n,n);
  for i to e do
    linalg[copyinto](C,J_block,(i-1)*d+1,(i-1)*d+1)
  od;
  for i to n-d do
    J_block[i,i+d]:=1
  od;
  op(J_block)
end:


`help/text/Ratjordan` := TEXT(
`FUNCTION: Ratjordan - compute the rational Jordan normal form of a matrix`,
`                      with entries in Z/pZ, p a prime number`,
`   `,
`CALLING SEQUENCE:`,
`   Ratjordan(A) mod p`,
`   Ratjordan(A, 'P') mod p`,
`   Ratjordan(A, 'P', 'Pinv') mod p`,
`   `,
`PARAMETERS:`,
`   p          - a prime number`,
`   A          - a square matrix`,
`   'P','Pinv' - (optional) assigned the transformation matrix and its inverse`,
`   `,
`SYNOPSIS:`,
`- The call Ratjordan(A) mod p computes and returns the rational Jordan`,
`  normal form R of a matrix A with entries in Z/pZ.`,
`   `,
`- R has the following structure: R = diag(R11,R12,..,R21,R22,..) where the`,
`  Rij's have the following shape:`,
`   `,
`             [ C(pi)   I                     ]`,
`             [                               ]`,
`             [       C(pi)   I               ]`,
`             [                               ]`,
`             [               .     .         ]`,
`             [                               ]`,
`             [                   C(pi)   I   ]`,
`             [                               ]`,
`             [                         C(pi) ]`,
`   `,
`  where there are eij blocks C(pi) along the diagonal and C(pi) is the`,
`  companion matrix associated with the irreducible polynomial pi.`,
`  Convention: i->degree(pi) is non-decreasing and for each i, j->eij is`,
`  non_decreasing`,
`  `,
`- If called in the form Ratjordan(A, 'P') mod p, then P will be assigned the`,
`  transformation matrix corresponding to the rational Jordan normal form,`,
`  that is, the matrix P such that inverse(P) * A * P = R.`,
`  If called in the form Ratjordan(A, 'P', 'Pinv') mod p, then Pinv will be`,
`  assigned the inverse of P.`,
`   `,
`EXAMPLES:`,
`> A := array([[10,-5,-5,8,3,0],`,
`              [-4,2,-10,-7,-5,-5],`,
`              [-8,2,7,3,7,5],`,
`              [-6,-7,-7,-7,10,7],`,
`              [-4,-3,-3,-6,8,-9],`,
`              [-2,5,-5,9,7,-4]]);`,
`                           [ 10  -5   -5   8   3   0 ]`,
`                           [                         ]`,
`                           [ -4   2  -10  -7  -5  -5 ]`,
`                           [                         ]`,
`                           [ -8   2   7    3   7   5 ]`,
`                      A := [                         ]`,
`                           [ -6  -7   -7  -7  10   7 ]`,
`                           [                         ]`,
`                           [ -4  -3   -3  -6   8  -9 ]`,
`                           [                         ]`,
`                           [ -2   5   -5   9   7  -4 ]`,
`   `,
`> Ratjordan(A,'P','Pinv') mod 23;`,
`                            [ 12   1  0   0  0   0 ]`,
`                            [                      ]`,
`                            [  0  12  0   0  0   0 ]`,
`                            [                      ]`,
`                            [  0   0  0  17  0   0 ]`,
`                            [                      ]`,
`                            [  0   0  1  19  0   0 ]`,
`                            [                      ]`,
`                            [  0   0  0   0  0  17 ]`,
`                            [                      ]`,
`                            [  0   0  0   0  1  19 ]`,
`   `,
`> op(P);op(Pinv);`,
`                           [ 12   5   2   9  17  10 ]`,
`                           [                        ]`,
`                           [ 16  11   8   5  12  17 ]`,
`                           [                        ]`,
`                           [  9  15  22  12   8   3 ]`,
`                           [                        ]`,
`                           [  4   6   6  21  17  22 ]`,
`                           [                        ]`,
`                           [ 16   4  15   3  19   9 ]`,
`                           [                        ]`,
`                           [ 20   2  16  21  21   4 ]`,
`   `,
`                           [  0  22  14  21   2   6 ]`,
`                           [                        ]`,
`                           [ 22   7  10  14   2  16 ]`,
`                           [                        ]`,
`                           [  0   8  15   3   1   5 ]`,
`                           [                        ]`,
`                           [  0  19   4  18   0   7 ]`,
`                           [                        ]`,
`                           [ 22   0  12   7   1  16 ]`,
`                           [                        ]`,
`                           [  0   2  11  15  20  17 ]`,
`   `,
`> ``mod``:=mods:`,
`> Ratjordan(A,'P','Pinv') mod 23;`,
`                           [ -11   1   0   0  0   0 ]`,
`                           [                        ]`,
`                           [  0   -11  0   0  0   0 ]`,
`                           [                        ]`,
`                           [  0    0   0  -6  0   0 ]`,
`                           [                        ]`,
`                           [  0    0   1  -4  0   0 ]`,
`                           [                        ]`,
`                           [  0    0   0   0  0  -6 ]`,
`                           [                        ]`,
`                           [  0    0   0   0  1  -4 ]`,
`   `,
`> op(P);op(Pinv);`,
`                                [ -11   5   2   9    -6  10 ]`,
`                                [                           ]`,
`                                [  -7  11   8   5   -11  -6 ]`,
`                                [                           ]`,
`                                [  9   -8  -1  -11   8    3 ]`,
`                                [                           ]`,
`                                [  4    6   6   -2   -6  -1 ]`,
`                                [                           ]`,
`                                [  -7   4  -8   3    -4   9 ]`,
`                                [                           ]`,
`                                [  -3   2  -7   -2   -2   4 ]`,
`   `,
`                                 [  0  -1   -9  -2   2   6 ]`,
`                                 [                         ]`,
`                                 [ -1   7   10  -9   2  -7 ]`,
`                                 [                         ]`,
`                                 [  0   8   -8   3   1   5 ]`,
`                                 [                         ]`,
`                                 [  0  -4   4   -5   0   7 ]`,
`                                 [                         ]`,
`                                 [ -1   0  -11   7   1  -7 ]`,
`                                 [                         ]`,
`                                 [  0   2   11  -8  -3  -6 ]`,
`   `,
`SEE ALSO: smithex, ismithex, frobenius, ratjordan, jordansymbolic, jordan`,
`          Frobenius, Jordansymbolic`
):


############################################################################
############################################################################
##
##          Jordansymbolic
##
###########################################################################
###########################################################################
# A Maple program for the computation of the Jordan normal form of a matrix
# with entries in Z/pZ, p a prime number.
# Authors: T.M.L. Mulders, A.H.M. Levelt
#          Mathematics Department
#          University of Nijmegen.
# E-mail: mulders@sci.kun.nl, ahml@sci.kun.nl
#
# January 1993


`mod/Jordansymbolic`:=proc(A,P,Pinv)
local p,AA,n,i,j,oldmod,r;

  if not type(A,'matrix') then
    AA:=evalm(A)
  else
    AA:=copy(A)
  fi;
  if not type(AA,'matrix'('anything','square')) then
    ERROR(`expecting a square matrix`)
  fi;

  n:=linalg[rowdim](AA);
  for i to n do
    for j to n do
      if type(AA[i,j],indexed) then
        ERROR(`Unassigned array elements`)
      fi
    od
  od;

  oldmod:=`mod`;
  `mod`:=modp;

  p:=args[nargs];

  for i to n do
    for j to n do
      AA[i,j]:=AA[i,j] mod p
    od
  od;

  if nargs=2 then
    r:=jordansymbolicformmod(p,AA)
  elif nargs=3 then
    r:=jordansymbolicformmod(p,AA,'P')
  else
    r:=jordansymbolicformmod(p,AA,'P','Pinv')
  fi;

  if oldmod=mods then
    `mod`:=mods
  fi;

  if nargs>=3 then
    P:=map(`mod`,P,p)
  fi;
  if nargs=4 then
    Pinv:=map(`mod`,Pinv,p)
  fi;

  [map(`mod`,r[1],p),[r[2][1] mod p,r[2][2]]]

end:

`Normform/jordansymbolicformmod`:=proc(p,A,P,Pinv)
local l,R,T,Tinv,S,Sinv;

  if nargs=2 then
    R:=ratjordanformmod(p,A);
    l:=ratjordan_to_jordanmod(p,R)
  elif nargs=3 then
    R:=ratjordanformmod(p,A,'T');
    l:=ratjordan_to_jordanmod(p,R,'S');
    P:=matmultmod(p,T,S)
  else
    R:=ratjordanformmod(p,A,'T','Tinv');
    l:=ratjordan_to_jordanmod(p,R,'S','Sinv');
    P:=matmultmod(p,T,S);
    Pinv:=matmultmod(p,Sinv,Tinv)
  fi;

  [invariant_to_jordan(l[1]),l[2]]

end:


`Normform/ratjordan_to_jordanmod`:=proc(pp,R,S,Sinv)
local prim_inv,x,i,j,k,d,N,T,Tinv,Tinvlist,Tlist,exp_list,invariant,n,p,
      partT,partTinv,t,v,w;

  prim_inv:=ratjordan_to_priminv(R,x);

  invariant:=[];
  if nargs>=3 then
    Tlist:=[]
  fi;
  if nargs=4 then
    Tinvlist:=[]
  fi;

  N:=nops(prim_inv);
  for i to N do
    p:=prim_inv[i][1];
    exp_list:=prim_inv[i][2];
    d:=degree(p,x);
    if d=1 then
      invariant:=[op(invariant),[-coeff(p,x,0),exp_list]]
    else
      for j to d do
        invariant:=[op(invariant),[evaln(x.i.j),exp_list]]
      od
    fi;

    if nargs>=3 then

      v:=array(1..d);
      v[d]:=1;
      for j from d-1 by -1 to 1 do
        v[j]:=sum('coeff(p,x,k)*x^(k-j)','k'=j..(n-1))+x^(n-j)
      od;

      n:=sum('exp_list[j]','j'=1..nops(exp_list));
      partT:=zero_matrix(n*d,n);
      for j to n do
        for k to d do
          partT[(j-1)*d+k,j]:=v[k]
        od
      od;
      T:=array(1..n*d,1..n*d);
      if d=1 then
        linalg[copyinto](subs(x=-coeff(p,x,0),op(partT)),T,1,1)
      else
        for j to d do
          linalg[copyinto](subs(x=evaln(x.i.j),op(partT)),T,1,(j-1)*n+1)
        od
      fi;
      Tlist:=[op(Tlist),op(T)]
    fi;

    if nargs=4 then
      Gcdex(p,diff(p,x),x,'s','t') mod pp;
      w:=array(1..d);
      w[1]:=t;
      for j from 2 to d do
        w[j]:=Rem(x*w[j-1],p,x) mod pp
      od;

      partTinv:=zero_matrix(n,n*d);
      for j to n do
        for k to d do
          partTinv[j,(j-1)*d+k]:=w[k]
        od
      od;
      Tinv:=array(1..n*d,1..n*d);
      if d=1 then
        linalg[copyinto](subs(x=-coeff(p,x,0),op(partTinv)),Tinv,1,1)
      else
        for j to d do
          linalg[copyinto](subs(x=evaln(x.i.j),op(partTinv)),Tinv,(j-1)*n+1,1)
        od
      fi;
      Tinvlist:=[op(Tinvlist),op(Tinv)]
    fi
  od;

  if nargs>=3 then
    S:=linalg[diag](op(Tlist))
  fi;
  if nargs=4 then
    Sinv:=linalg[diag](op(Tinvlist))
  fi;
  [invariant,[[seq(prim_inv[i][1],i=1..N)],x]]

end:


`Normform/ratjordan_to_priminv`:=proc(R,x)
local p,r,n,plist,exp_list,l,i,N,prim_inv;
  n:=linalg[rowdim](R);
  r:=1;
  plist:=[];
  while r<=n do
    l:=find_ratjblock(R,r,x);
    plist:=[op(plist),l];
    r:=r+l[2]*degree(l[1],x)
  od;
  p:=plist[1][1];
  exp_list:=[plist[1][2]];
  prim_inv:=[];
  N:=nops(plist);
  i:=2;
  while i<=N do
    if plist[i][1]=p then
      exp_list:=[op(exp_list),plist[i][2]]
    else
      prim_inv:=[op(prim_inv),[p,exp_list]];
      p:=plist[i][1];
      exp_list:=[plist[i][2]]
    fi;
    i:=i+1
  od;
  prim_inv:=[op(prim_inv),[p,exp_list]];
  prim_inv
end:

`Normform/find_ratjblock`:=proc(R,r,x)
local i,n,e,p;
  n:=linalg[rowdim](R);
  p:=find_companion(R,r,x);
  e:=1;
  i:=r+degree(p,x);
  do
    if i>n then RETURN([p,e]) fi;
    if identitymatrix(R,i-degree(p,x),i,degree(p,x)) then
      e:=e+1;
      i:=i+degree(p,x)
    else
      RETURN([p,e])
    fi
  od
end:

`Normform/find_companion`:=proc(A,r,x)
local i,j,n,p;
  n:=linalg[rowdim](A);
  i:=r+1;
  while i<=n and A[i,i-1]=1 do i:=i+1 od;
  p:=0;
  for j from r to i-1 do p:=p-A[j,i-1]*x^(j-r) od;
  p:=p+x^(i-r)
end:

`Normform/identitymatrix`:=proc(A,i,j,m)
local n;
  n:=linalg[rowdim](A);
  if i+m-1>n or j+m-1>n then
    false
  else
    linalg[equal](linalg[submatrix](A,i..i+m-1,j..j+m-1),array(1..m,1..m,identity))
  fi
end:

`Normform/invariant_to_jordan`:=proc(invariant)
local block_list,N,M,i,j;
  N:=nops(invariant);
  block_list:=[];
  for i to N do
    M:=nops(invariant[i][2]);
    for j to M do
      block_list:=[op(block_list),linalg[JordanBlock](invariant[i][1],invariant[i][2][j])]
    od
  od;
  linalg[diag](op(block_list))
end:


`help/text/Jordansymbolic` := TEXT(
`FUNCTION: Jordansymbolic - compute the Jordan normal form of a matrix`,
`                           with entries in Z/pZ, p a prime number`,
`   `,
`CALLING SEQUENCE:`,
`   Jordansymbolic(A) mod p`,
`   Jordansymbolic(A, 'P') mod p`,
`   Jordansymbolic(A, 'P','Pinv') mod p`,
`   `,
`PARAMETERS:`,
`   p          - a prime number`,
`   A          - a square matrix`,
`   'P','Pinv' - (optional) assigned the transformation matrix and its inverse`,
`   `,
`SYNOPSIS:`,
`- The function Jordansymbolic(A) mod p computes and returns the Jordan normal`,
`  form J of a matrix A with entries in Z/pZ.`,
`   `,
`- J has the following structure: J=diag(J11,J12,..,J21,J22,..) where the`,
`  Jij's have the following shape:`,
`   `,
`           [ xi  1              ]`,
`           [                    ]`,
`           [     xi  1          ]`,
`           [                    ]`,
`           [         .   .      ]`,
`           [                    ]`,
`           [             xi  1  ]`,
`           [                    ]`,
`           [                 xi ]`,
`   `,
`  where there are eij times xi along the diagonal. Here xi is a zero of`,
`  the characteristic polynomial p of A. If p does not split completely,`,
`  symbolic names are chosen for the missing zeroes of p.`,
`  Jordansymbolic actually returns a list [J,l] where J is the Jordan`,
`  normal form of A (using symbolic names if necessary) and l=[ll,x], where`,
`  x is a name and ll is a list of irreducible factors of p(x). If symbolic`,
`  names are used then xij is a zero of ll[i].`,
`  Convention: for each i, j->eij is non_decreasing.`,
`   `,
`- If called in the form Jordansymbolic(A,'P') mod p, then P will be assigned`,
`  the transformation matrix corresponding to the Jordan normal form,`,
`  that is, the matrix P such that inverse(P) * A * P = J.`,
`  If called in the form JordanSymbolic(A,'P','Pinv') mod p, then Pinv will be`,
`  assigned the inverse of P.`,
`   `,
`EXAMPLES:`,
`> A := array([[10,-5,-5,8,3,0],`,
`              [-4,2,-10,-7,-5,-5],`,
`              [-8,2,7,3,7,5],`,
`              [-6,-7,-7,-7,10,7],`,
`              [-4,-3,-3,-6,8,-9],`,
`              [-2,5,-5,9,7,-4]]);`,
`                           [ 10  -5   -5   8   3   0 ]`,
`                           [                         ]`,
`                           [ -4   2  -10  -7  -5  -5 ]`,
`                           [                         ]`,
`                           [ -8   2   7    3   7   5 ]`,
`                      A := [                         ]`,
`                           [ -6  -7   -7  -7  10   7 ]`,
`                           [                         ]`,
`                           [ -4  -3   -3  -6   8  -9 ]`,
`                           [                         ]`,
`                           [ -2   5   -5   9   7  -4 ]`,
`   `,\
`> Jordansymbolic(A,'P','Pinv') mod 23;`,
`          [ 12   1   0    0    0    0  ]`,
`          [                            ]`,
`          [  0  12   0    0    0    0  ]`,
`          [                            ]`,
`          [  0   0  x21   0    0    0  ]                       2`,
`         [[                            ], [[11 + x, 6 + 4 x + x ], x]]`,
`          [  0   0   0   x21   0    0  ]`,
`          [                            ]`,
`          [  0   0   0    0   x22   0  ]`,
`          [                            ]`,
`          [  0   0   0    0    0   x22 ]`,
`   `,
`> op(P);op(Pinv);`,
`         [ 12   5   17 + 2 x21   9 + 17 x21   17 + 2 x22   9 + 17 x22 ]`,
`         [                                                            ]`,
`         [ 16  11   14 + 8 x21  19 + 12 x21   14 + 8 x22  19 + 12 x22 ]`,
`         [                                                            ]`,
`         [  9  15   8 + 22 x21   12 + 8 x21   8 + 22 x22   12 + 8 x22 ]`,
`         [                                                            ]`,
`         [  4   6   22 + 6 x21  21 + 17 x21   22 + 6 x22  21 + 17 x22 ]`,
`         [                                                            ]`,
`         [ 16   4  17 + 15 x21  16 + 19 x21  17 + 15 x22  16 + 19 x22 ]`,
`         [                                                            ]`,
`         [ 20   2  16 + 16 x21  19 + 21 x21  16 + 16 x22  19 + 21 x22 ]`,
`   `,
` [      0           22           14           21           2            6     ]`,
` [                                                                            ]`,
` [     22           7            10           14           2           16     ]`,
` [                                                                            ]`,
` [      0      13 + 19 x21   4 x21 + 10  14 x21 + 14  17 x21 + 11   8 x21 + 8 ]`,
` [                                                                            ]`,
` [ 6 x21 + 12    x21 + 3    14 x21 + 22       19       4 x21 + 18  16 x21 + 6 ]`,
` [                                                                            ]`,
` [      0      13 + 19 x22   4 x22 + 10  14 x22 + 14  17 x22 + 11   8 x22 + 8 ]`,
` [                                                                            ]`,
` [ 6 x22 + 12    x22 + 3    14 x22 + 22       19       4 x22 + 18  16 x22 + 6 ]`,
`   `,
`> ``mod``:=mods:`,
`> Jordansymbolic(A,'P','Pinv') mod 23;`,
`              [ -11   1    0    0    0    0  ]`,
`              [                              ]`,
`              [  0   -11   0    0    0    0  ]`,
`              [                              ]`,
`              [  0    0   x21   0    0    0  ]                       2`,
`             [[                              ], [[11 + x, 6 + 4 x + x ], x]]`,
`              [  0    0    0   x21   0    0  ]`,
`              [                              ]`,
`              [  0    0    0    0   x22   0  ]`,
`              [                              ]`,
`              [  0    0    0    0    0   x22 ]`,
`   `,
`> op(P);op(Pinv);`,
`          [ -11   5  - 6 + 2 x21    9 - 6 x21   - 6 + 2 x22    9 - 6 x22  ]`,
`          [                                                               ]`,
`          [  -7  11  - 9 + 8 x21  - 4 - 11 x21  - 9 + 8 x22  - 4 - 11 x22 ]`,
`          [                                                               ]`,
`          [  9   -8    8 - x21    - 11 + 8 x21    8 - x22    - 11 + 8 x22 ]`,
`          [                                                               ]`,
`          [  4    6  - 1 + 6 x21   - 2 - 6 x21  - 1 + 6 x22   - 2 - 6 x22 ]`,
`          [                                                               ]`,
`          [  -7   4  - 6 - 8 x21   - 7 - 4 x21  - 6 - 8 x22   - 7 - 4 x22 ]`,
`          [                                                               ]`,
`          [  -3   2  - 7 - 7 x21   - 4 - 2 x21  - 7 - 7 x22   - 4 - 2 x22 ]`,
`   `,
`[      0          -1            -9           -2            2            6      ]`,
`[                                                                              ]`,
`[     -1           7            10           -9            2            -7     ]`,
`[                                                                              ]`,
`[      0     - 10 - 4 x21   4 x21 + 10  - 9 x21 - 9  - 6 x21 + 11   8 x21 + 8  ]`,
`[                                                                              ]`,
`[ 6 x21 - 11    x21 + 3    - 9 x21 - 1       -4        4 x21 - 5   - 7 x21 + 6 ]`,
`[                                                                              ]`,
`[      0     - 10 - 4 x22   4 x22 + 10  - 9 x22 - 9  - 6 x22 + 11   8 x22 + 8  ]`,
`[                                                                              ]`,
`[ 6 x22 - 11    x22 + 3    - 9 x22 - 1       -4        4 x22 - 5   - 7 x22 + 6 ]`,
`   `,
`SEE ALSO: smithex, ismithex, frobenius, ratjordan, jordansymbolic, jordan`,
`          Frobenius, Ratjordan`
):

#save `Normform.m`;
#quit
