\documentclass{article}
\usepackage{axiom}
\begin{document}
\title{\$SPAD/src/algebra rep2.spad}
\author{Holger Gollan, Johannes Grabmeier}
\maketitle
\begin{abstract}
\end{abstract}
\eject
\tableofcontents
\eject
\section{package REP2 RepresentationPackage2}
<<package REP2 RepresentationPackage2>>=
)abbrev package REP2 RepresentationPackage2
++ Authors: Holger Gollan, Johannes Grabmeier
++ Date Created: 10 September 1987
++ Date Last Updated: 20 August 1990
++ Basic Operations: areEquivalent?, isAbsolutelyIrreducible?,
++   split, meatAxe
++ Related Constructors: RepresentationTheoryPackage1
++ Also See: IrrRepSymNatPackage
++ AMS Classifications:
++ Keywords: meat-axe, modular representation
++ Reference:
++   R. A. Parker: The Computer Calculation of Modular Characters
++   (The Meat-Axe), in M. D. Atkinson (Ed.), Computational Group Theory
++   Academic Press, Inc., London 1984
++   H. Gollan, J. Grabmeier: Algorithms in Representation Theory and
++    their Realization in the Computer Algebra System Scratchpad,
++    Bayreuther Mathematische Schriften, Heft 33, 1990, 1-23.
++ Description:
++   RepresentationPackage2 provides functions for working with
++   modular representations of finite groups and algebra.
++   The routines in this package are created, using ideas of R. Parker,
++   (the meat-Axe) to get smaller representations from bigger ones,
++   i.e. finding sub- and factormodules, or to show, that such the
++   representations are irreducible.
++   Note: most functions are randomized functions of Las Vegas type
++   i.e. every answer is correct, but with small probability
++   the algorithm fails to get an answer.
RepresentationPackage2(R): public == private where

  R    : Ring
  OF    ==> OutputForm
  I     ==> Integer
  L     ==> List
  SM    ==> SquareMatrix
  M     ==> Matrix
  NNI   ==> NonNegativeInteger
  V     ==> Vector
  PI    ==> PositiveInteger
  B     ==> Boolean
  RADIX ==> RadixExpansion

  public ==> with

    completeEchelonBasis : V V R  ->  M R
      ++ completeEchelonBasis(lv) completes the basis {\em lv} assumed
      ++ to be in echelon form of a subspace of {\em R^n} (n the length
      ++ of all the vectors in {\em lv}) with unit vectors to a basis of
      ++ {\em R^n}. It is assumed that the argument is not an empty
      ++ vector and that it is not the basis of the 0-subspace.
      ++ Note: the rows of the result correspond to the vectors of the basis.
    createRandomElement : (L M R, M R) -> M R
      ++ createRandomElement(aG,x) creates a random element of the group
      ++ algebra generated by {\em aG}.
    -- randomWord : (L L I, L M)  ->  M R
      --++ You can create your own 'random' matrix with "randomWord(lli, lm)".
      --++ Each li in lli determines a product of matrices, the entries in li
      --++ determine which matrix from lm is chosen. Finally we sum over all
      --++ products. The result "sm" can be used to call split with (e.g.)
      --++ second parameter "first nullSpace sm"
    if R has EuclideanDomain then   -- using rowEchelon
      cyclicSubmodule  : (L M R, V R) ->   V V R
        ++ cyclicSubmodule(lm,v) generates a basis as follows.
        ++ It is assumed that the size n of the vector equals the number
        ++ of rows and columns of the matrices. Then the matrices generate
        ++ a subalgebra, say \spad{A}, of the algebra of all square matrices of
        ++ dimension n. {\em V R} is an \spad{A}-module in the natural way.
        ++ cyclicSubmodule(lm,v) generates the R-Basis of {\em Av} as
        ++ described in section 6 of R. A. Parker's "The Meat-Axe".
        ++ Note: in contrast to the description in "The Meat-Axe" and to
        ++ {\em standardBasisOfCyclicSubmodule} the result is in
        ++ echelon form.
      standardBasisOfCyclicSubmodule  : (L M R, V R) ->   M R
        ++ standardBasisOfCyclicSubmodule(lm,v) returns a matrix as follows.
        ++ It is assumed that the size n of the vector equals the number
        ++ of rows and columns of the matrices.  Then the matrices generate
        ++ a subalgebra, say \spad{A},
        ++ of the algebra of all square matrices of
        ++ dimension n. {\em V R} is an \spad{A}-module in the natural way.
        ++ standardBasisOfCyclicSubmodule(lm,v) calculates a matrix whose
        ++ non-zero column vectors are the R-Basis of {\em Av} achieved
        ++ in the way as described in section 6
        ++ of R. A. Parker's "The Meat-Axe".
        ++ Note: in contrast to {\em cyclicSubmodule}, the result is not
        ++ in echelon form.
    if R has Field then  -- only because of inverse in SM
      areEquivalent? : (L M R, L M R, B, I) -> M R
        ++ areEquivalent?(aG0,aG1,randomelements,numberOfTries) tests
        ++ whether the two lists of matrices, all assumed of same
        ++ square shape, can be simultaneously conjugated by a non-singular
        ++ matrix. If these matrices represent the same group generators,
        ++ the representations are equivalent.
        ++ The algorithm tries
        ++ {\em numberOfTries} times to create elements in the
        ++ generated algebras in the same fashion. If their ranks differ,
        ++ they are not equivalent. If an
        ++ isomorphism is assumed, then
        ++ the kernel of an element of the first algebra
        ++ is mapped to the kernel of the corresponding element in the
        ++ second algebra. Now consider the one-dimensional ones.
        ++ If they generate the whole space (e.g. irreducibility !)
        ++ we use {\em standardBasisOfCyclicSubmodule} to create the
        ++ only possible transition matrix. The method checks whether the
        ++ matrix conjugates all corresponding matrices from {\em aGi}.
        ++ The way to choose the singular matrices is as in {\em meatAxe}.
        ++ If the two representations are equivalent, this routine
        ++ returns the transformation matrix {\em TM} with
        ++ {\em aG0.i * TM = TM * aG1.i} for all i. If the representations
        ++ are not equivalent, a small 0-matrix is returned.
        ++ Note: the case
        ++ with different sets of group generators cannot be handled.
      areEquivalent? : (L M R, L M R) -> M R
        ++ areEquivalent?(aG0,aG1) calls {\em areEquivalent?(aG0,aG1,true,25)}.
        ++ Note: the choice of 25 was rather arbitrary.
      areEquivalent? : (L M R, L M R, I) -> M R
        ++ areEquivalent?(aG0,aG1,numberOfTries) calls
        ++ {\em areEquivalent?(aG0,aG1,true,25)}.
        ++ Note: the choice of 25 was rather arbitrary.
      isAbsolutelyIrreducible? : (L M R, I) -> B
        ++ isAbsolutelyIrreducible?(aG, numberOfTries) uses
        ++ Norton's irreducibility test to check for absolute
        ++ irreduciblity, assuming if a one-dimensional kernel is found.
        ++ As no field extension changes create "new" elements
        ++ in a one-dimensional space, the criterium stays true
        ++ for every extension. The method looks for one-dimensionals only
        ++ by creating random elements (no fingerprints) since
        ++ a run of {\em meatAxe} would have proved absolute irreducibility
        ++ anyway.
      isAbsolutelyIrreducible? : L M R -> B
        ++ isAbsolutelyIrreducible?(aG) calls
        ++ {\em isAbsolutelyIrreducible?(aG,25)}.
        ++ Note: the choice of 25 was rather arbitrary.
      split : (L M R, V R)  ->  L L M R
        ++ split(aG, vector) returns a subalgebra \spad{A} of all
        ++ square matrix of dimension n as a list of list of matrices,
        ++ generated by the list of matrices aG, where n denotes both
        ++ the size of vector as well as the dimension of each of the
        ++ square matrices.
        ++ {\em V R} is an A-module in the natural way.
        ++ split(aG, vector) then checks whether the cyclic submodule
        ++ generated by {\em vector} is a proper submodule of {\em V R}.
        ++ If successful, it returns a two-element list, which contains
        ++ first the list of the representations of the submodule,
        ++ then the list of the representations of the factor module.
        ++ If the vector generates the whole module, a one-element list
        ++ of the old representation is given.
        ++ Note: a later version this should call the other split.
      split:  (L M R, V V R) -> L L M R
        ++ split(aG,submodule) uses a proper submodule of {\em R^n}
        ++ to create the representations of the submodule and of
        ++ the factor module.
    if (R has Finite) and (R has Field) then
      meatAxe  : (L M R, B, I, I)  ->  L L M R
        ++ meatAxe(aG,randomElements,numberOfTries, maxTests) returns
        ++ a 2-list of representations as follows.
        ++ All matrices of argument aG are assumed to be square
        ++ and of equal size.
        ++ Then \spad{aG} generates a subalgebra, say \spad{A}, of the algebra
        ++ of all square matrices of dimension n. {\em V R} is an A-module
        ++ in the usual way.
        ++ meatAxe(aG,numberOfTries, maxTests) creates at most
        ++ {\em numberOfTries} random elements of the algebra, tests
        ++ them for singularity. If singular, it tries at most {\em maxTests}
        ++ elements of its kernel to generate a proper submodule.
        ++ If successful, a 2-list is returned: first, a list
        ++ containing first the list of the
        ++ representations of the submodule, then a list of the
        ++ representations of the factor module.
        ++ Otherwise, if we know that all the kernel is already
        ++ scanned, Norton's irreducibility test can be used either
        ++ to prove irreducibility or to find the splitting.
        ++ If {\em randomElements} is {\em false}, the first 6 tries
        ++ use Parker's fingerprints.
      meatAxe : L M R -> L L M R
        ++ meatAxe(aG) calls {\em meatAxe(aG,false,25,7)} returns
        ++ a 2-list of representations as follows.
        ++ All matrices of argument \spad{aG} are assumed to be square
        ++ and of
        ++ equal size. Then \spad{aG} generates a subalgebra,
        ++ say \spad{A}, of the algebra
        ++ of all square matrices of dimension n. {\em V R} is an A-module
        ++ in the usual way.
        ++ meatAxe(aG) creates at most 25 random elements
        ++ of the algebra, tests
        ++ them for singularity. If singular, it tries at most 7
        ++ elements of its kernel to generate a proper submodule.
        ++ If successful a list which contains first the list of the
        ++ representations of the submodule, then a list of the
        ++ representations of the factor module is returned.
        ++ Otherwise, if we know that all the kernel is already
        ++ scanned, Norton's irreducibility test can be used either
        ++ to prove irreducibility or to find the splitting.
        ++ Notes: the first 6 tries use Parker's fingerprints.
        ++ Also, 7 covers the case of three-dimensional kernels over
        ++ the field with 2 elements.
      meatAxe: (L M R, B) ->  L L M R
        ++ meatAxe(aG, randomElements) calls {\em meatAxe(aG,false,6,7)},
        ++ only using Parker's fingerprints, if {\em randomElemnts} is false.
        ++ If it is true, it calls {\em  meatAxe(aG,true,25,7)},
        ++ only using random elements.
        ++ Note: the choice of 25 was rather arbitrary.
        ++ Also, 7 covers the case of three-dimensional kernels over the field
        ++ with 2 elements.
      meatAxe : (L M R, PI)  ->  L L M R
        ++ meatAxe(aG, numberOfTries) calls
        ++ {\em meatAxe(aG,true,numberOfTries,7)}.
        ++ Notes: 7 covers the case of three-dimensional
        ++ kernels over the field with 2 elements.
      scanOneDimSubspaces: (L V R, I)  ->  V R
        ++ scanOneDimSubspaces(basis,n) gives a canonical representative
        ++ of the {\em n}-th one-dimensional subspace of the vector space
        ++ generated by the elements of {\em basis}, all from {\em R^n}.
        ++ The coefficients of the representative are of shape
        ++ {\em (0,...,0,1,*,...,*)}, {\em *} in R. If the size of R
        ++ is q, then there are {\em (q^n-1)/(q-1)} of them.
        ++ We first reduce n modulo this number, then find the
        ++ largest i such that {\em +/[q^i for i in 0..i-1] <= n}.
        ++ Subtracting this sum of powers from n results in an
        ++ i-digit number to basis q. This fills the positions of the
        ++ stars.
        -- would prefer to have (V V R,....   but nullSpace  results
        -- in L V R

  private ==> add

    -- import of domain and packages
    import OutputForm

    -- declarations and definitions of local variables and
    -- local function

    blockMultiply: (M R, M R, L I, I) -> M R
      -- blockMultiply(a,b,li,n) assumes that a has n columns
      -- and b has n rows, li is a sublist of the rows of a and
      -- a sublist of the columns of b. The result is the
      -- multiplication of the (li x n) part of a with the
      -- (n x li) part of b. We need this, because just matrix
      -- multiplying the parts would require extra storage.
    blockMultiply(a, b, li, n) ==
      matrix([[ +/[a(i,s) * b(s,j) for s in 1..n ] _
        for j in li  ]  for i in li])

    fingerPrint: (NNI, M R, M R, M R) -> M R
      -- is local, because one should know all the results for smaller i
    fingerPrint (i : NNI, a : M R, b : M R, x :M R) ==
      -- i > 2 only gives the correct result if the value of x from
      -- the parameter list equals the result of fingerprint(i-1,...)
      (i::PI) = 1 => x := a + b + a*b
      (i::PI) = 2 => x := (x + a*b)*b
      (i::PI) = 3 => x := a + b*x
      (i::PI) = 4 => x := x + b
      (i::PI) = 5 => x := x + a*b
      (i::PI) = 6 => x := x - a + b*a
      error "Sorry, but there are only 6 fingerprints!"
      x


    -- definition of exported functions


    --randomWord(lli,lm)  ==
    --  -- we assume that all matrices are square of same size
    --  numberOfMatrices := #lm
    --  +/[*/[lm.(1+i rem numberOfMatrices) for i in li ] for li in lli]

    completeEchelonBasis(basis) ==

      dimensionOfSubmodule : NNI := #basis
      n : NNI := # basis.1
      indexOfVectorToBeScanned : NNI := 1
      row : NNI := dimensionOfSubmodule

      completedBasis : M R := zero(n, n)
      for i in 1..dimensionOfSubmodule repeat
        completedBasis := setRow_!(completedBasis, i, basis.i)
      if #basis <= n then
        newStart : NNI := 1
        for j in 1..n
          while indexOfVectorToBeScanned <= dimensionOfSubmodule repeat
            if basis.indexOfVectorToBeScanned.j = 0 then
              completedBasis(1+row,j) := 1  --put unit vector into basis
              row := row + 1
            else
              indexOfVectorToBeScanned := indexOfVectorToBeScanned + 1
            newStart : NNI := j + 1
        for j in newStart..n repeat
          completedBasis(j,j) := 1  --put unit vector into basis
      completedBasis


    createRandomElement(aG,algElt) ==
      numberOfGenerators : NNI := #aG
      -- randomIndex := randnum numberOfGenerators
      randomIndex := 1 + random(numberOfGenerators)$Integer
      algElt := algElt * aG.randomIndex
      -- randomIndxElement := randnum numberOfGenerators
      randomIndex := 1 + random(numberOfGenerators)$Integer
      algElt + aG.randomIndex


    if R has EuclideanDomain then
      cyclicSubmodule (lm : L M R, v : V R)  ==
        basis : M R := rowEchelon matrix list entries v
        -- normalizing the vector
        -- all these elements lie in the submodule generated by v
        furtherElts : L V R := [(lm.i*v)::V R for i in 1..maxIndex lm]
        --furtherElts has elements of the generated submodule. It will
        --will be checked whether they are in the span of the vectors
        --computed so far. Of course we stop if we have got the whole
        --space.
        while (not null furtherElts) and (nrows basis < #v)  repeat
          w : V R := first furtherElts
          nextVector : M R := matrix list entries w -- normalizing the vector
          -- will the rank change if we add this nextVector
          -- to the basis so far computed?
          addedToBasis : M R := vertConcat(basis, nextVector)
          if rank addedToBasis ~= nrows basis then
             basis := rowEchelon addedToBasis  -- add vector w to basis
             updateFurtherElts : L V R := _
               [(lm.i*w)::V R for i in 1..maxIndex lm]
             furtherElts := append (rest furtherElts, updateFurtherElts)
          else
             -- the vector w lies in the span of matrix, no updating
             -- of the basis
             furtherElts := rest furtherElts
        vector [row(basis, i) for i in 1..maxRowIndex basis]


      standardBasisOfCyclicSubmodule (lm : L M R, v : V R)  ==
        dim   : NNI := #v
        standardBasis : L L R := list(entries v)
        basis : M R := rowEchelon matrix list entries v
        -- normalizing the vector
        -- all these elements lie in the submodule generated by v
        furtherElts : L V R := [(lm.i*v)::V R for i in 1..maxIndex lm]
        --furtherElts has elements of the generated submodule. It will
        --will be checked whether they are in the span of the vectors
        --computed so far. Of course we stop if we  have got the whole
        --space.
        while (not null furtherElts) and (nrows basis < #v)  repeat
          w : V R := first furtherElts
          nextVector : M R := matrix list entries w  -- normalizing the vector
          -- will the rank change if we add this nextVector
          -- to the basis so far computed?
          addedToBasis : M R := vertConcat(basis, nextVector)
          if rank addedToBasis ~= nrows basis then
             standardBasis := cons(entries w, standardBasis)
             basis := rowEchelon addedToBasis  -- add vector w to basis
             updateFurtherElts : L V R := _
               [lm.i*w for i in 1..maxIndex lm]
             furtherElts := append (rest furtherElts, updateFurtherElts)
          else
             -- the vector w lies in the span of matrix, therefore
             -- no updating of matrix
             furtherElts := rest furtherElts
        transpose matrix standardBasis


    if R has Field then  -- only because of inverse in Matrix

      -- as conditional local functions, *internal have to be here

      splitInternal: (L M R, V R, B) -> L L M R
      splitInternal(algebraGenerators : L M R, vector: V R,doSplitting? : B) ==

        n : I := # vector    -- R-rank of representation module =
                               -- degree of representation
        submodule : V V R := cyclicSubmodule (algebraGenerators,vector)
        rankOfSubmodule : I := # submodule  -- R-Rank of submodule
        submoduleRepresentation    : L M R := nil()
        factormoduleRepresentation : L M R := nil()
        if n ~= rankOfSubmodule then
          messagePrint "  A proper cyclic submodule is found."
          if doSplitting? then   -- no else !!
            submoduleIndices : L I := [i for i in 1..rankOfSubmodule]
            factormoduleIndices : L I := [i for i in (1+rankOfSubmodule)..n]
            transitionMatrix : M R := _
              transpose completeEchelonBasis submodule
            messagePrint "  Transition matrix computed"
            inverseTransitionMatrix : M R :=  _
             autoCoerce(inverse transitionMatrix)$Union(M R,"failed")
            messagePrint "  The inverse of the transition matrix computed"
            messagePrint "  Now transform the matrices"
            for i in 1..maxIndex algebraGenerators repeat
              helpMatrix : M R := inverseTransitionMatrix * algebraGenerators.i
              -- in order to not create extra space and regarding the fact
              -- that we only want the two blocks in the main diagonal we
              -- multiply with the aid of the local function blockMultiply
              submoduleRepresentation := cons( blockMultiply( _
                helpMatrix,transitionMatrix,submoduleIndices,n), _
                submoduleRepresentation)
              factormoduleRepresentation := cons( blockMultiply( _
                helpMatrix,transitionMatrix,factormoduleIndices,n), _
                factormoduleRepresentation)
          [reverse submoduleRepresentation, reverse _
            factormoduleRepresentation]
        else -- represesentation is irreducible
          messagePrint "  The generated cyclic submodule was not proper"
          [algebraGenerators]



      irreducibilityTestInternal: (L M R, M R, B) -> L L M R
      irreducibilityTestInternal(algebraGenerators,_
          singularMatrix,split?) ==
        algebraGeneratorsTranspose : L M R := [transpose _
           algebraGenerators.j for j in 1..maxIndex algebraGenerators]
        xt : M R := transpose singularMatrix
        messagePrint "  We know that all the cyclic submodules generated by all"
        messagePrint "    non-trivial element of the singular matrix under view are"
        messagePrint "    not proper, hence Norton's irreducibility test can be done:"
        -- actually we only would need one (!) non-trivial element from
        -- the kernel of xt, such an element must exist as the transpose
        -- of a singular matrix is of course singular. Question: Can
        -- we get it more easily from the kernel of x = singularMatrix?
        kernel : L V R := nullSpace xt
        result : L L M R :=  _
          splitInternal(algebraGeneratorsTranspose,first kernel,split?)
        if null rest result then  -- this means first kernel generates
          -- the whole module
          if 1 = #kernel then
             messagePrint "  Representation is absolutely irreducible"
          else
             messagePrint "  Representation is irreducible, but we don't know "
             messagePrint "    whether it is absolutely irreducible"
        else
          if split? then
            messagePrint "  Representation is not irreducible and it will be split:"
            -- these are the dual representations, so calculate the
            -- dual to get the desired result, i.e. "transpose inverse"
            -- improvements??
            for i in 1..maxIndex result repeat
              for j in 1..maxIndex (result.i) repeat
                mat : M R := result.i.j
                result.i.j := _
                  transpose autoCoerce(inverse mat)$Union(M R,"failed")
          else
            messagePrint "  Representation is not irreducible, use meatAxe to split"
        -- if "split?" then dual representation interchange factor
        -- and submodules, hence reverse
        reverse result



      -- exported functions for FiniteField-s.


      areEquivalent? (aG0, aG1) ==
        areEquivalent? (aG0, aG1, true, 25)


      areEquivalent? (aG0, aG1, numberOfTries) ==
        areEquivalent? (aG0, aG1, true, numberOfTries)


      areEquivalent? (aG0, aG1, randomelements, numberOfTries) ==
          result : B := false
          transitionM : M R := zero(1, 1)
          numberOfGenerators  : NNI := #aG0
          -- need a start value for creating random matrices:
          -- if we switch to randomelements later, we take the last
          -- fingerprint.
          if randomelements then   -- random should not be from I
             --randomIndex  : I   := randnum numberOfGenerators
             randomIndex := 1+ random(numberOfGenerators)$Integer
             x0 : M R := aG0.randomIndex
             x1 : M R := aG1.randomIndex
          n : NNI := #row(x0,1)   -- degree  of representation
          foundResult : B := false
          for i in 1..numberOfTries until foundResult repeat
            -- try to create a non-singular element of the algebra
            -- generated by "aG". If only two generators,
            -- i < 7 and not "randomelements" use Parker's  fingerprints
            -- i >= 7 create random elements recursively:
            -- x_i+1 :=x_i * mr1 + mr2, where mr1 and mr2 are randomly
            -- chosen elements form "aG".
            if i = 7 then randomelements := true
            if randomelements then
               --randomIndex := randnum numberOfGenerators
               randomIndex := 1+ random(numberOfGenerators)$Integer
               x0 := x0 * aG0.randomIndex
               x1 := x1 * aG1.randomIndex
               --randomIndex := randnum numberOfGenerators
               randomIndex := 1 + random(numberOfGenerators)$Integer
               x0 := x0 + aG0.randomIndex
               x1 := x1 + aG1.randomIndex
            else
               x0 := fingerPrint (i, aG0.0, aG0.1 ,x0)
               x1 := fingerPrint (i, aG1.0, aG1.1 ,x1)
            -- test singularity of x0 and x1
            rk0 : NNI := rank x0
            rk1 : NNI := rank x1
            rk0 ~= rk1 =>
              messagePrint  "Dimensions of kernels differ"
              foundResult := true
              result := false
            -- can assume dimensions are equal
            rk0 ~= n - 1 =>
              -- not of any use here if kernel not one-dimensional
              if randomelements then
                messagePrint  "Random element in generated algebra does"
                messagePrint  "  not have a one-dimensional kernel"
              else
                messagePrint  "Fingerprint element in generated algebra does"
                messagePrint  "  not have a one-dimensional kernel"
            -- can assume dimensions are equal and equal to n-1
            if randomelements then
              messagePrint  "Random element in generated algebra has"
              messagePrint  "  one-dimensional kernel"
            else
              messagePrint  "Fingerprint element in generated algebra has"
              messagePrint  "  one-dimensional kernel"
            kernel0 : L V R := nullSpace x0
            kernel1 : L V R := nullSpace x1
            baseChange0 : M R := standardBasisOfCyclicSubmodule(_
              aG0,kernel0.1)
            baseChange1 : M R := standardBasisOfCyclicSubmodule(_
              aG1,kernel1.1)
            (ncols baseChange0) ~= (ncols baseChange1) =>
              messagePrint  "  Dimensions of generated cyclic submodules differ"
              foundResult := true
              result := false
            -- can assume that dimensions of cyclic submodules are equal
            (ncols baseChange0) = n =>   -- full dimension
              transitionM := baseChange0 * _
                autoCoerce(inverse baseChange1)$Union(M R,"failed")
              foundResult := true
              result := true
              for j in 1..numberOfGenerators while result repeat
                if (aG0.j*transitionM) ~= (transitionM*aG1.j) then
                  result := false
                  transitionM := zero(1 ,1)
                  messagePrint "  There is no isomorphism, as the only possible one"
                  messagePrint "    fails to do the necessary base change"
            -- can assume that dimensions of cyclic submodules are not "n"
            messagePrint  "  Generated cyclic submodules have equal, but not full"
            messagePrint  "    dimension, hence we can not draw any conclusion"
          -- here ends the for-loop
          if not foundResult then
            messagePrint  " "
            messagePrint  "Can neither prove equivalence nor inequivalence."
            messagePrint  "  Try again."
          else
            if result then
              messagePrint  " "
              messagePrint  "Representations are equivalent."
            else
              messagePrint  " "
              messagePrint  "Representations are not equivalent."
          transitionM


      isAbsolutelyIrreducible?(aG) == isAbsolutelyIrreducible?(aG,25)


      isAbsolutelyIrreducible?(aG, numberOfTries) ==
        result : B := false
        numberOfGenerators  : NNI := #aG
        -- need a start value for creating random matrices:
        -- randomIndex  : I   := randnum numberOfGenerators
        randomIndex := 1 + random(numberOfGenerators)$Integer
        x : M R := aG.randomIndex
        n : NNI := #row(x,1)   -- degree  of representation
        foundResult : B := false
        for i in 1..numberOfTries until foundResult repeat
          -- try to create a non-singular element of the algebra
          -- generated by "aG", dimension of its kernel being 1.
          -- create random elements recursively:
          -- x_i+1 :=x_i * mr1 + mr2, where mr1 and mr2 are randomly
          -- chosen elements form "aG".
          -- randomIndex := randnum numberOfGenerators
          randomIndex := 1 + random(numberOfGenerators)$Integer
          x := x * aG.randomIndex
          --randomIndex := randnum numberOfGenerators
          randomIndex := 1 + random(numberOfGenerators)$Integer
          x := x + aG.randomIndex
          -- test whether rank of x is n-1
          rk : NNI := rank x
          if rk = n - 1 then
            foundResult := true
            messagePrint "Random element in generated algebra has"
            messagePrint "  one-dimensional kernel"
            kernel : L V R := nullSpace x
            if n=#cyclicSubmodule(aG, first kernel) then
              result := (irreducibilityTestInternal(aG,x,false)).1 ~= nil()$(L M R)
              -- result := not null? first irreducibilityTestInternal(aG,x,false) -- this down't compile !!
            else -- we found a proper submodule
              result := false
              --split(aG,kernel.1) -- to get the splitting
          else -- not of any use here if kernel not one-dimensional
            messagePrint "Random element in generated algebra does"
            messagePrint "  not have a one-dimensional kernel"
        -- here ends the for-loop
        if not foundResult then
          messagePrint "We have not found a one-dimensional kernel so far,"
          messagePrint "  as we do a random search you could try again"
        --else
        --  if not result then
        --    messagePrint "Representation is not irreducible."
        --  else
        --    messagePrint "Representation is irreducible."
        result



      split(algebraGenerators: L M R, vector: V R) ==
        splitInternal(algebraGenerators, vector, true)


      split(algebraGenerators : L M R, submodule: V V R) == --not zero submodule
        n : NNI := #submodule.1 -- R-rank of representation module =
                                -- degree of representation
        rankOfSubmodule : I := (#submodule) :: I --R-Rank of submodule
        submoduleRepresentation    : L M R := nil()
        factormoduleRepresentation : L M R := nil()
        submoduleIndices : L I := [i for i in 1..rankOfSubmodule]
        factormoduleIndices : L I := [i for i in (1+rankOfSubmodule)..(n::I)]
        transitionMatrix : M R := _
          transpose completeEchelonBasis submodule
        messagePrint "  Transition matrix computed"
        inverseTransitionMatrix : M R :=
          autoCoerce(inverse transitionMatrix)$Union(M R,"failed")
        messagePrint "  The inverse of the transition matrix computed"
        messagePrint "  Now transform the matrices"
        for i in 1..maxIndex algebraGenerators repeat
          helpMatrix : M R := inverseTransitionMatrix * algebraGenerators.i
          -- in order to not create extra space and regarding the fact
          -- that we only want the two blocks in the main diagonal we
          -- multiply with the aid of the local function blockMultiply
          submoduleRepresentation := cons( blockMultiply( _
            helpMatrix,transitionMatrix,submoduleIndices,n), _
            submoduleRepresentation)
          factormoduleRepresentation := cons( blockMultiply( _
            helpMatrix,transitionMatrix,factormoduleIndices,n), _
            factormoduleRepresentation)
        cons(reverse submoduleRepresentation, list( reverse _
          factormoduleRepresentation)::(L L M R))


    -- the following is "under"  "if R has Field", as there are compiler
    -- problems with conditinally defined local functions, i.e. it
    -- doesn't know, that "FiniteField" has "Field".


      -- we are scanning through the vectorspaces
      if (R has Finite) and (R has Field) then

        meatAxe(algebraGenerators, randomelements, numberOfTries, _
           maxTests) ==
          numberOfGenerators  : NNI := #algebraGenerators
          result : L L M R := nil()$(L L M R)
          q   : PI  := (size()$R)::PI
          -- need a start value for creating random matrices:
          -- if we switch to randomelements later, we take the last
          -- fingerprint.
          if randomelements then   -- random should not be from I
             --randomIndex  : I   := randnum numberOfGenerators
             randomIndex := 1 + random(numberOfGenerators)$Integer
             x : M R := algebraGenerators.randomIndex
          foundResult : B := false
          for i in 1..numberOfTries until foundResult repeat
            -- try to create a non-singular element of the algebra
            -- generated by "algebraGenerators". If only two generators,
            -- i < 7 and not "randomelements" use Parker's  fingerprints
            -- i >= 7 create random elements recursively:
            -- x_i+1 :=x_i * mr1 + mr2, where mr1 and mr2 are randomly
            -- chosen elements form "algebraGenerators".
            if i = 7 then randomelements := true
            if randomelements then
               --randomIndex := randnum numberOfGenerators
               randomIndex := 1 + random(numberOfGenerators)$Integer
               x := x * algebraGenerators.randomIndex
               --randomIndex := randnum numberOfGenerators
               randomIndex := 1 + random(numberOfGenerators)$Integer
               x := x + algebraGenerators.randomIndex
            else
               x := fingerPrint (i, algebraGenerators.1,_
                 algebraGenerators.2 , x)
            -- test singularity of x
            n : NNI := #row(x, 1)  -- degree  of representation
            if (rank x) ~= n then  -- x singular
              if randomelements then
                 messagePrint "Random element in generated algebra is singular"
              else
                 messagePrint "Fingerprint element in generated algebra is singular"
              kernel : L V R := nullSpace x
              -- the first number is the maximal number of one dimensional
              -- subspaces of the kernel, the second is a user given
              -- constant
              numberOfOneDimSubspacesInKernel : I := (q^(#kernel)-1)quo(q-1)
              numberOfTests : I :=  _
                min(numberOfOneDimSubspacesInKernel, maxTests)
              for j in 1..numberOfTests repeat
                 --we create an element in the kernel, there is a good
                 --probability for it to generate a proper submodule, the
                 --called "split" does the further work:
                 result := _
                   split(algebraGenerators,scanOneDimSubspaces(kernel,j))
                 -- we had "not null rest result" directly in the following
                 -- if .. then, but the statment there foundResult := true
                 -- didn't work properly
                 foundResult :=  not null rest result
                 if foundResult then
                   leave -- inner for-loop
                   -- finish here with result
                 else  -- no proper submodule
                   -- we were not successfull, i.e gen. submodule was
                   -- not proper, if the whole kernel is already scanned,
                   -- Norton's irreducibility test is used now.
                   if (j+1)>numberOfOneDimSubspacesInKernel then
                     -- we know that all the cyclic submodules generated
                     -- by all non-trivial elements of the kernel are proper.
                     foundResult := true
                     result : L L M R := irreducibilityTestInternal (_
                       algebraGenerators,x,true)
                     leave  -- inner for-loop
              -- here ends the inner for-loop
            else  -- x non-singular
              if randomelements then
                messagePrint "Random element in generated algebra is non-singular"
              else
                messagePrint "Fingerprint element in generated algebra is non-singular"
          -- here ends the outer for-loop
          if not foundResult then
             result : L L M R := [nil()$(L M R), nil()$(L M R)]
             messagePrint " "
             messagePrint "Sorry, no result, try meatAxe(...,true)"
             messagePrint "  or consider using an extension field."
          result


        meatAxe (algebraGenerators) ==
          meatAxe(algebraGenerators, false, 25, 7)


        meatAxe (algebraGenerators, randomElements?) ==
          randomElements? => meatAxe (algebraGenerators, true, 25, 7)
          meatAxe(algebraGenerators, false, 6, 7)


        meatAxe (algebraGenerators:L M R, numberOfTries:PI) ==
          meatAxe (algebraGenerators, true, numberOfTries, 7)



        scanOneDimSubspaces(basis,n) ==
          -- "dimension" of subspace generated by "basis"
          dim : NNI := #basis
          -- "dimension of the whole space:
          nn : NNI := #(basis.1)
          q : NNI := size()$R
          -- number of all one-dimensional subspaces:
          nred : I := n rem ((q^dim -1) quo (q-1))
          pos : I := nred
          i : I := 0
          for i in 0..dim-1 while nred >= 0 repeat
            pos := nred
            nred := nred - (q^i)
          i  := if i = 0 then 0 else i-1
          coefficients : V R := new(dim,0$R)
          coefficients.(dim-i) := 1$R
          iR : L I := wholeRagits(pos::RADIX q)
          for j in 1..(maxIndex iR) repeat
            coefficients.(dim-((#iR)::I) +j) := index((iR.j+(q::I))::PI)$R
          result : V R := new(nn,0)
          for i in 1..maxIndex coefficients repeat
            newAdd : V R := coefficients.i * basis.i
            for j in 1..nn repeat
              result.j := result.j + newAdd.j
          result

@
\section{License}
<<license>>=
--Copyright (c) 1991-2002, The Numerical ALgorithms Group Ltd.
--All rights reserved.
--
--Redistribution and use in source and binary forms, with or without
--modification, are permitted provided that the following conditions are
--met:
--
--    - Redistributions of source code must retain the above copyright
--      notice, this list of conditions and the following disclaimer.
--
--    - Redistributions in binary form must reproduce the above copyright
--      notice, this list of conditions and the following disclaimer in
--      the documentation and/or other materials provided with the
--      distribution.
--
--    - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--      names of its contributors may be used to endorse or promote products
--      derived from this software without specific prior written permission.
--
--THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
--IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
--TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
--PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
--OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
--EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
--PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
--PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
--LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
--NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
--SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@
<<*>>=
<<license>>

<<package REP2 RepresentationPackage2>>
@
\eject
\begin{thebibliography}{99}
\bibitem{1} nothing
\end{thebibliography}
\end{document}
