Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quellcode-Bibliothek specht.gi   Sprache: unbekannt

 
Spracherkennung für: .gi vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

#######################################################################
##  Hecke - specht.gi : the kernel of Hecke                          ##
##                                                                   ##
##     A GAP package for calculating the decomposition numbers of    ##
##     Hecke algebras of type A (over fields of characteristic       ##
##     zero). The functions provided are primarily combinatorial in  ##
##     nature. Many of the combinatorial tools from the (modular)    ##
##     representation theory of the symmetric groups appear in the   ##
##     package.                                                      ##
##                                                                   ##
##     These programs, and the enclosed libraries, are distributed   ##
##     under the usual licensing agreements and conditions of GAP.   ##
##                                                                   ##
##     Dmitriy Traytel                                               ##
##     (heavily using the GAP3-package SPECHT 2.4 by Andrew Mathas)  ##
##                                                                   ##
#######################################################################

## Hecke 1.0: June 2010:
##   - Translated to GAP4

## SPECHT Change log
## 2.4:
##  - fixed more bugs in H.valuation; returned incorrect answers before
##    when e=0 or e=p (symmetric group case).
##  - fixed bug in Dq(), via Sasha Kleshchev.

## 2.3:
##  - fixed bug in H.valuation  reported by Johannes Lipp
##  - fixed bug in Sq() reported by Johannes Lipp.
##  - corrected FindDecompositionMatrix() so that it updates the matrices
##    CrystalMatrices[] and DecompositionMatrices[] after calculating a
##    crystalized decomposition matrix.

## 2.2: June 1996: various changes requested by the referee.
##  - mainly changing function names.
##  - DecompositionMatrix changed so that it no longer attempts to
##    calculate decomposition matrices in the finite field case; added
##    CalculateDecompositionMatrix() to do this.
##  - replaced Matrix() function with MatrixDecompositionMatrix() and
##    added the function DecompositionMatrixMatix().

## 2.1: April 1996:
##  - Added a filename argument to SaveDecompositionMatrix and made it
##    save the non-decomposition matrices under (more) sensible names; the
##    later is done using the existence of a record component d.matname.
##    Need to do something here about reading such matrices in (this can be
##    done using DecompositionMatrix)..
##  - Changed ReadDecompositionMatrix so that it automatically reads the
##    format of version 1.0 files (and deleted ReadOldDecompositionMatrix).
##    Also fixed a bug here which was causing confusion between Hecke algebra
##    and Schur algebra matrices.
##  - Renamed FindDecompositionMatrix as KnownDecompositionMatrix because it
##    doesn't try to calculate a crytalized decomposition matrix; the new
##    FindDecompositionMatrix will return the decomposition matrix if at all
##    possible. In particular, this fixes a 'bug' in SimpleDimension.
##  - Rewrote AdjustmentMatrix so that it actually works.
##  - Changed P()->S() module conversions so that it no longer requires
##    the projective module to have positive coefficients (and fixed bug in
##    matrix ops).

## 2.0: March 1996:
##   - LLT algorithm implemented for calculating crystal basis of
##     the Fock space and hence by specialization the decomposition
##     matrices for all Hecke algebras over fields of characteristic
##     zero; most of the work is done by the internal function Pq().
##     This required a new set of 'module' types ("Pq", "Sq", and "Dq"),
##     with corresponding operation sets H.operations.X. In particular,
##     these include q-induction and q-restriction, effectively allowing
##     computations with $U_q(\hat sl_e)$ on the Fock space.
##   - crystallized decomposition matrices added and decomposition
##     matrix type overhauled. d.d[c] is now a list of records with
##     partitions replaced by references to d.rows etc. Changed format
##     of decomposition matrix library files so as to handle polynomial
##     entries in the matrices..
##   - printing of Specht records changed allowing more compact and
##     readable notation. eg. S(1,1,1,1)->S(1^4). (see SpechtPrintFn).
##   - reversed order of parts and coeffs records in H.S(), d.d etc;
##     these lists are now sets which improves use of Position().
##   - reorganised the Specht function and the record H() which it returns;
##     in particular added H.info.
##   - extended SInducedModule() and SRestrict to allow multiple inducing and
##     restricting (all residues).

## 1.0: December 1995: initial release.

######################################################################

## Here is a description of the structure of the main records used
## in SPECHT
#### In GAP4 all "operations"-fields do not exist. They are replaced
#### by top-level operations with some filter restrictions

## 1. Specht()
## Specht() is the main function in specht.g, it returns a record 'H'
## which represents the family of Hecke algebras, or Schur algebras,
## corresponding to some fixed field <R> and parameter <q>. 'H' has
## the components:
##   IsSpecht      : is either 'true' or 'false' depending on whether
##                   'H' is a Hecke algebra or Schur algebra resp.
#### This function is obsolete in the GAP4 version - it is replaced
#### by the filter IsHecke
##   S(), P(), D() : these three functions return records which
##                   represent Specht modules, PIMs, and simple
##                   'H' modules respectively. These functions exist
##                   only when 'H' is a Hecke algebra record.
#### Use MakeSpecht(H,...) instead of H.S(...)
##   W(), P(), F() : these are the corresponding functions for Weyl
##                   modules, PIMs, and simple modules of Schur algbras.
#### Use MakeSpecht(S,...) instead of S.W(...)
##   info          : this is a record with components
##                     version: SPECHT version number,
##                     Library: path to SPECHT library files
##                     SpechtDirectory: addition directory searched by
##                            SPECHT (defaults to current directory)
##                     Indeterminate: the indeterminate used by SPECHT
##                            in the LLT algorithm when H.p=0.
##   operations    : apart from the obvious things like the Print()
##                   function for 'H' this record also contains the
##                   operation records for the modules S(), P() etc.
##                   as well as functions for manipulating these records
##                   and accessing decomposition matrix files. The most
##                   most important of these are:
##                     S, P, D, Pq, Sq, Dq : operations records for modules
#### Most functions are now on toplevel
##                     New : creation function for modules. Internally
##                       modules are created via
##                         H.operations.new(module,coeffs,parts)
##                       where module is one of "S", "P", "D", "Sq", "Pq",
##                       or "Dq" ("S" and "D" are used even for Schur
##                       algebras), coeffs is an integer or a *set* of
##                       integers and parts is a partition or a *set* of
##                       partitions. In any programs the use of New is
##                       better than H.S(mu), for example, because the
##                       function names are different for Hecke and Schur
##                       algebras. Note that coeffs and parts must be
##                       ordered reverse lexicographically (ie. they are
##                       *sets*).
#### Use Module(H,...) instead of H.operations.New(...)
##                     Collect: like New() except that  coeffs and parts
##                       need not be sets (and may contain repeats).
#### Use Collect(H,...) instead of H.operations.Collect(...)
##                     NewDecompositionMatrix : creates a decomposition
##                       matrix.
#### Use ReadDecompositionMatrix(H,...) instead of H.operations.ReadDecompositionMatrix(...)
##                     ReadDecompositionMatrix : reads, and returns, a
##                       decomposition matrix file.
#### Use KnownDecompositionMatrix(H,...) instead of H.operations.KnownDecompositionMatrix(...)
##                     KnownDecompositionMatrix : returns a decomposition
##                       matrix of a given size; will either extract this
##                       matrix from Specht's internal lists or call
##                       ReadDecompositionMatrix(), or try to calculate
##                       the decomposition matrix (without using the
##                       crystalized decomposition matrices).
#### Use FindDecompositionMatrix(H,...) instead of H.operations.FindDecompositionMatrix(...)
##                     FindDecompositionMatrix : like KnownDM except that
##                       it will calculate the crystalized DM if needed.
##   Ordering      : a function for ordering partitions; controls how
##                   decomposition matrices for H are printed.
#### Use SetOrdering(...) to control the output
##   e             : order of <q> in <R>
#### Use OrderOfQ(...) to extract the e from an algebra or a module
#### corresponding to an algebra
##   p             : characteristic of <R>
##   valuation     : the valuation map of [JM2]; used primarily by
##                   the q-Schaper theorem.D
##   HeckeRing     : bookkeeping string used primarily in searching for
##                   library files.
##   Pq(), Sq()    : Functions for computing elements of the Fock space
##                   when H.p=0 (used in LLT algorithm). Note that there is
##                   no Dq; also unlike their counter parts S(), P(), and
##                   D() they accept only partitions as arguments.
#### Use MakeFockPIM(H,...) instead of H.Pq(...)
#### Use MakeFockSpecht(H,...) instead of H.Sq(...)
##
## 2. The module functions S(), P() and D() (and Schur equivalents)
## These functions return record 'x' which represents some 'H'--module.
## 'x' is a record with the following components:
##   H      : a pointer back to the corresponding algebra
##   module : one of "S", "P", "D", "Sq", "Pq", or "Dq", (not "W", or "F").
##   coeffs : a *set* of coefficients
##   parts  : the corresponding *set* of partitions
##   operations :
##       + - * / : for algebraic manipulations
##       Print : calls PrintModule
##       Coefficient : returns the coefficient of a given partition
#### Use Coefficient(x,...) instead of x.operations.Coefficient(...)
##       PositiveCoefficients : true if all coefficients are non-negative
##       IntegralCoefficients : true if all coefficients are integral
##       InnerProduct : computes the 'Kronecker' inner product
##       Induce, Restrict, SInduce, SRestrict : induction and restriction
##                 functions taylored to 'x'. These functions convert 'x'
##                 to a linear combination of Specht modules, induce and
##                 then convert back to the type of 'x' (if possible).
##                 Quantized versions are applied as appropriate.
##       S, P, D : functions for rewriting 'x' into the specified type
##                 (so, for example, S('x') rewrites 'x' as a linear
##                 combination of Specht modules).
## 'x'.operations is a pointer to 'H'.operations.('x'.module).
##
## 3. DecompositionMatrices
## Decomposition matrices 'd' in Specht are represented as records with the
## following components:
##
##   d : a list, indexed by d.cols, where each entry is a record
##       corresponding to a column of 'd'; this record has components
##       two * sets* coeffs and parts, where parts is the index of the
##       corresponding partition in d.rows.
##   rows : the *set* of the partitions which make up the rows or 'd'.
##   cols : the *set* of the partitions which make up the rows or 'd'.
##   inverse : a list of records containing the inverse of 'd'. These
##          records are computed only as needed.
##   dimensions : a list of the dimensions of the simple modules; again
##          computed only as needed.
##   IsDecompositionMatrix : false if 'd' is a crystallized decomposition
##          matrix, and true otherwise.
##   H :a pointer back to the corresponding algebra
##   operations :
##     = : equality.
##     Print, TeX, Matrix : printing, TeX, and a GAP Matrix.
##     AddIndecomposable : for adding a PIM to 'd'.
##     Store : for updating Specht's internal record of 'd'.
##     S, P, D: for accessing the entries of 'd' and using 'd' to
##              convert between the various types of 'H'--modules.
##              These are actually records, each containing three
##              functions S(), P(), and D(); so X.Y() tells 'd' how
##              to write an X-module as a linear combination of Y-modules.
##     Invert : calculates D(mu) using 'd'.
##     IsNewIndecomposable : the heart of the 'IsNewIndecomposable'
##              function.
##     Induce : for inducing decomposition matrices (non--crystallized).
##   P : a short-hand for d.H.P('d',<mu>).

###########################################################################

## Specht() is the main function in the package, although in truth it is
## little more than a wrapper for the functions S(), P(), and D().
## Originally, I had these as external functions, but decided that it
## was better to tie these functions to e=H.e as strongly as possible.
InstallMethod(Specht_GenAlgebra,"generate a type-Algebra object",
  [IsString,IsInt,IsInt,IsFunction,IsString],
  function(type,e,p,valuation,HeckeRing)
    local H;
    if not IsPrime(p) and p<>0
    then Error("Specht(<e>,<p>,<val>), <p> must be a prime number");
    fi;

    H := rec(
      e:=e,
      p:=p,
      valuation:=valuation,
      HeckeRing:=HeckeRing,
      ## bits and pieces about H
      info:=rec(version:=PackageInfo("hecke")[1].Version,
                Library:=Directory(
                  Concatenation(DirectoriesPackageLibrary("hecke")[1]![1],"e",
                  String(e),"/")),
      ## We keep a copy of SpechtDirectory in H so that we have a
      ## chance of finding new decomposition matrices when it changes.
                SpechtDirectory:=Directory(".")),

      ## This record will hold any decomposition matrices which Specht()
      ## (or rather its derivatives) read in. This used to be a public
      ## record; it is now private because q-Schur algebra matrices and
      ## Hecke algebra matrices might need to coexist.
      DecompositionMatrices:=[],

      ## for ordering the rows of the decomposition matrices
      ## (as it is common to all decomposition matrices it lives here)
      Ordering:=Lexicographic,
    );

    if p = 0
    then
      ## This list will hold the crystallized decomposition matrices (p=0)
      H.CrystalMatrices:=[];
      H.Indeterminate:=Indeterminate(Integers);
      SetName(H.Indeterminate,"v");
    else H.Indeterminate:=1;
    fi;

    if type = "Schur" then
      Objectify(SchurType,H);
    else
      Objectify(HeckeType,H);
    fi;

    return H;
  end
);

InstallMethod(Specht_GenAlgebra,"generate a type-Algebra object",
  [IsString,IsInt,IsInt,IsFunction],
  function(type,e,p,val) local ring;
    if not IsPrime(p)
    then Error("Specht(<e>,<p>,<val>), <p> must be a prime number");
    fi;
    if e=p then
      ring:=Concatenation("p",String(p),"sym");
      return Specht_GenAlgebra(type,e,p,val,ring);
    else
      return Specht_GenAlgebra(type,e,p,val,"unknown");
    fi;
  end
);

InstallMethod(Specht_GenAlgebra,"generate a type-Algebra object",
  [IsString,IsInt,IsInt],
  function(type,e,p) local val, ring;
    if not IsPrime(p)
    then Error("Specht(<e>,<p>,<val>), <p> must be a prime number");
    fi;
    if e=p then
      ring:=Concatenation("p",String(p),"sym");
      ## return the exponent of the maximum power of p dividing x
      val:=function(x) local i;
        i:=0;
        while x mod p=0 do
          i:=i+1;
          x:=x/p;
        od;
        return i;
      end;
    else
      ring:=Concatenation("e",String(e), "p",String(p));
      ## return the exponent of the maximum power of p that
      ## divides e^x-1.
      val:=function(x) local i;
        if x mod e=0 then return 0;
        else
          i:=0;
          while x mod p=0 do
            i:=i+1;
            x:=x/p;
          od;
          return p^i;
        fi;
      end;
    fi;
    return Specht_GenAlgebra(type,e,p,val,ring);
  end
);

InstallMethod(Specht_GenAlgebra,"generate a type-Algebra object",
  [IsString,IsInt],
  function(type,e) local val;
      if e=0 then val:=x->x;
      else
        val:=function(x)
          if x mod e = 0 then return 1;
          else return 0;
          fi;
        end;
      fi;
      return Specht_GenAlgebra(type,e,0,val,Concatenation("e",String(e), "p0"));
  end
);

InstallMethod(Specht,"generate a Hecke-Algebra object",[IsInt],
  function(e) return Specht_GenAlgebra("Specht",e); end
);
InstallMethod(Specht,"generate a Hecke-Algebra object",[IsInt,IsInt],
  function(e,p) return Specht_GenAlgebra("Specht",e,p); end
);
InstallMethod(Specht,"generate a Hecke-Algebra object",[IsInt,IsInt,IsFunction],
  function(e,p,val) return Specht_GenAlgebra("Specht",e,p,val); end
);
InstallMethod(Specht,"generate a Hecke-Algebra object",[IsInt,IsInt,IsFunction,IsString],
  function(e,p,val,ring) return Specht_GenAlgebra("Specht",e,p,val,ring); end
);
InstallMethod(Schur,"generate a Schur-Algebra object",[IsInt],
  function(e) return Specht_GenAlgebra("Schur",e); end
);
InstallMethod(Schur,"generate a Schur-Algebra object",[IsInt,IsInt],
  function(e,p) return Specht_GenAlgebra("Schur",e,p); end
);
InstallMethod(Schur,"generate a Schur-Algebra object",[IsInt,IsInt,IsFunction],
  function(e,p,val) return Specht_GenAlgebra("Schur",e,p,val); end
);
InstallMethod(Schur,"generate a Schur-Algebra object",[IsInt,IsInt,IsFunction,IsString],
  function(e,p,val,ring) return Specht_GenAlgebra("Schur",e,p,val,ring); end
);

InstallImmediateMethod(Characteristic, IsAlgebraObj, 0,
  function(H) return H!.p; end
);

InstallImmediateMethod(IsZeroCharacteristic, IsAlgebraObj, 0,
  function(H) return H!.p = 0; end
);

InstallImmediateMethod(OrderOfQ, IsAlgebraObj, 0,
  function(H) return H!.e; end
);

InstallImmediateMethod(OrderOfQ, IsAlgebraObjModule, 0,
  function(x) return x!.H!.e; end
);

InstallMethod(SetOrdering,"writing access to H.Ordering",
  [IsAlgebraObj,IsFunction],
  function(H,ord) H!.Ordering := ord; end
);

InstallMethod(SpechtCoefficients,"reading access to S.coeffs",[IsHeckeSpecht],
  function(S) return S!.coeffs; end
);

InstallMethod(SpechtPartitions,"reading access to S.parts",[IsHeckeSpecht],
  function(S) return S!.parts; end
);

## FORMER TOPLEVEL FUNCTIONS ###################################################
#F Calculates the dimensions of the simple modules in d
## Usage:  SimpleDimension(d)   -> prints all simple dimensions
##         SimpleDimension(H,n) -> prints all again
##         SimpleDimension(H,mu) or SimpleDimension(d,mu) -> dim D(mu)
InstallMethod(SimpleDimensionOp,
  "all simple dimensions from decomposition matrix",[IsDecompositionMatrix],
  function(d) local cols, collabel, M, c, x;
    if IsSchur(d!.H) then
      Print("# SimpleDimension() not implemented for Schur algebras\n");
      return fail;
    fi;
    cols:=StructuralCopy(d!.cols);
    if d!.H!.Ordering=Lexicographic then
      cols:=cols{[Length(cols),Length(cols)-1..1]};
    else Sort(cols, d!.H!.Ordering);
    fi;
    cols:=List(cols, c->Position(d!.cols,c));
    collabel:=List([1..Length(cols)], c->LabelPartition(d!.cols[cols[c]]));
    M:=Maximum(List(collabel, Length))+1;

    for c in [1..Length(cols)] do
      Print(String(collabel[c],-M),": ");
      if IsBound(d!.dimensions[cols[c]]) then
        Print(d!.dimensions[cols[c]],"\n");
      else
        x:=MakeSimpleOp(d,d!.cols[cols[c]]);
        if x=fail then Print("not known\n");
        else
          d!.dimensions[cols[c]]:=Sum([1..Length(x!.parts)],
                             r->x!.coeffs[r]*SpechtDimension(x!.parts[r]));
          Print(d!.dimensions[cols[c]],"\n");
        fi;
      fi;
    od;
    return true;
  end
);

InstallMethod(SimpleDimensionOp,
  "simple dimensions of a partition from decomposition matrix",
  [IsDecompositionMatrix,IsList],
  function(d,mu) local c, x;
    c:=Position(d!.cols,mu);
    if c=fail then
      Print("# SimpleDimension(<d>,<mu>), <mu> is not in <d>.cols\n");
      return fail;
    else
      if not IsBound(d!.dimensions[c]) then
        x:=MakeSimpleOp(d,d!.cols[c]);
        if x=fail then return fail;
        else d!.dimensions[c]:=Sum([1..Length(x!.parts)],
                            r->x!.coeffs[r]*SpechtDimension(x!.parts[r]));
        fi;
      fi;
      return d!.dimensions[c];
    fi;
  end
);

InstallMethod(SimpleDimensionOp,
  "all simple dimensions from algebra",[IsAlgebraObj,IsInt],
  function(H,n) local d;
    d:=FindDecompositionMatrix(H,n);
    if d=fail then
      Print("# SimpleDimension(H,n), the decomposition matrix of H_n is ",
            "not known.\n");
      return fail;
    fi;
    return SimpleDimensionOp(d);
  end
);

InstallMethod(SimpleDimensionOp,
  "simple dimensions of a partition from algebra",[IsAlgebraObj,IsList],
  function(H,mu) local d;
    d:=FindDecompositionMatrix(H,Sum(mu));
    if d=fail then
      Print("# SimpleDimension(H,mu), the decomposition matrix of H_Sum(mu) is",
            " not known.\n");
      return fail;
    fi;
    return SimpleDimensionOp(d,mu);
  end
); # SimpleDimension

#P returns a list of the e-regular partitions occurring in x
InstallMethod(ListERegulars,"e-regular partitions of a module",
  [IsAlgebraObjModule],
  function(x) local e,parts,coeffs,p;
    e:=x!.H!.e;
    parts:=x!.parts;
    coeffs:=x!.coeffs;
    if e=0 then return parts;
    elif x=0*x then return [];
    else return List(Filtered([Length(parts),Length(parts)-1..1],
           p->IsERegular(e,parts[p])),p->[coeffs[p], parts[p]]);
    fi;
  end
); # ListERegulars


##P Print the e-regular partitions in x if IsSpecht(x); on the other hand,
### if IsDecompositionMatrix(x) then return the e-regular part of the
### decomposition matrix.
InstallMethod(ERegulars,"e-regular part of the given decomposition matrix",
  [IsDecompositionMatrix],
  function(d) local regs, y, r, len;
    regs:=DecompositionMatrix(d!.H,d!.rows,d!.cols,not IsCrystalDecompositionMatrix(d));
    regs!.d:=[]; #P returns a list of the e-regular partitions occurring in x
    for y in [1..Length(d!.cols)] do
      if IsBound(d!.d[y]) then
        regs!.d[y]:=rec(parts:=[], coeffs:=[]);
        for r in [1..Length(d!.d[y].parts)] do
          len:=Position(d!.cols,d!.rows[d!.d[y].parts[r]]);
          if len<>fail then
            Add(regs!.d[y].parts,len);
            Add(regs!.d[y].coeffs,d!.d[y].coeffs[r]);
          fi;
        od;
      fi;
    od;
    regs!.rows:=regs!.cols;
    return regs;
  end
);

InstallMethod(ERegulars, "print e-regular partitions of a module",
  [IsAlgebraObjModule],
  function(x) local len, regs, y;
    len:=0;
    regs:=ListERegulars(x);
    if regs=[] or IsInt(regs[1]) then Print(regs, "\n");
    else
      for y in regs do
        if (len + 5 + 4*Length(y[2])) > 75 then len:=0; Print("\n"); fi;
        if y[1]<>1 then Print(y[1], "*"); len:=len + 3; fi;
        Print(y[2], "  ");
        len:=len + 5 + 4*Length(y[2]);
      od;
      Print("\n");
    fi;
  end
); # ERegulars

#F Returns true if S(mu)=D(mu) - note that this implies that mu is e-regular
## (if mu is not e-regular, fail is returned).     -- see [JM2]
## IsSimple(H,mu)
##   ** uses H.valuation
InstallMethod(IsSimpleModuleOp,
  "test whether the given partition defines a simple module",
  [IsAlgebraObj,IsList],
  function(H,mu) local mud, simple, r, c, v;
    if not IsERegular(H!.e,mu) then return false;
    elif mu=[] then return true; fi;

    mud:=ConjugatePartition(mu);
    simple:=true; c:=1;
    while simple and c <=mu[1] do
      v:=H!.valuation(mu[1]+mud[c]-c);
      simple:=ForAll([2..mud[c]], r->v=H!.valuation(mu[r]+mud[c]-c-r+1));
      c:=c+1;
    od;
    return simple;
  end
); #IsSimpleModule

#F Split an element up into components which have the same core.
## Usage: SplitECores(x) - returns as list of all block components
##        SplitECores(x,lambda) - returns a list with (i) core lambda,
## (ii) the same core as lambda, or (iii) the same core as the first
## element in lambda if IsSpecht(lambda).
InstallMethod(SplitECoresOp,"for a single module",[IsAlgebraObjModule],
  function(x) local cores, c, cpos, y, cmp;
    if x=fail or x=0*x then return []; fi;

    cores:=[]; cmp:=[];
    for y in [1..Length(x!.parts)] do
      c:=ECore(x!.H!.e, x!.parts[y]);
      cpos:=Position(cores, c);
      if cpos=fail then
        Add(cores, c);
        cpos:=Length(cores);
        cmp[cpos]:=[[],[]];
      fi;
      Add(cmp[cpos][1], x!.coeffs[y]);
      Add(cmp[cpos][2], x!.parts[y]);
    od;
    for y in [1..Length(cmp)] do
      cmp[y]:=Module(x!.H,x!.module,cmp[y][1],cmp[y][2]);
    od;
    return cmp;
  end
);

InstallMethod(SplitECoresOp,"for a module and a partition",
  [IsAlgebraObjModule,IsList],
  function(x,mu) local c, cpos, y, cmp;
    c:=ECore(x!.H!.e, mu);
    cmp:=[ [],[] ];
    for y in [1..Length(x!.parts)] do
      if ECore(x!.H!.e, x!.parts[y])=c then
        Add(cmp[1], x!.coeffs[y]);
        Add(cmp[2], x!.parts[y]);
      fi;
    od;
    cmp:=Module(x!.H,x!.module, cmp[1], cmp[2]);
    return cmp;
  end
);

InstallMethod(SplitECoresOp,"for a module and a specht module",
  [IsAlgebraObjModule,IsAlgebraObjModule],
  function(x,s) local c, cpos, y, cmp;
    c:=ECore(s!.H!.e, s!.parts[Length(x!.parts)]);
    cmp:=[ [],[] ];
    for y in [1..Length(x!.parts)] do
      if ECore(x!.H!.e, x!.parts[y])=c then
        Add(cmp[1], x!.coeffs[y]);
        Add(cmp[2], x!.parts[y]);
      fi;
    od;
    cmp:=Module(x!.H,x!.module, cmp[1], cmp[2]);
    return cmp;
  end
); #SplitECores

#F This function returns the image of <mu> under the Mullineux map using
## the Kleshcehev(-James) algorithm, or the supplied decomposition matrix.
## Alternatively, given a "module" x it works out the image of x under
## Mullineux.
## Usage:  MullineuxMap(e|H|d, mu) or MullineuxMap(x)
InstallMethod(MullineuxMapOp,"image of x under Mullineux",[IsAlgebraObjModule],
  function(x) local e, v;
    e := x!.H!.e;
    if x=fail or not IsERegular(e,x!.parts[Length(x!.parts)]) then
      Print("# The Mullineux map is defined only for e-regular partitions\n");
      return fail;
    fi;
    if x=fail or x=0*x then return fail; fi;
    if IsHeckeSpecht(x) then
      if Length(x!.module)=1 then
        return Collect(x!.H,x!.module,x!.coeffs,
                 List(x!.parts, ConjugatePartition));
      else
        v:=x!.H!.info.Indeterminate;
        return Collect(x!.H,x!.module,
             List([1..Length(x!.coeffs)],
                mu->Value(v^-EWeight(e,x!.parts[mu])*x!.coeffs[mu],v^-1)),
             List(x!.parts,ConjugatePartition) );
      fi;
    elif Length(x!.module)=1 then
      return Sum([1..Length(x!.coeffs)],
               mu->Module(x!.H,x!.module,x!.coeffs[mu],
                     MullineuxMap(e,x!.parts[mu])));
    else
      v:=x!.H!.info.Indeterminate;
      return Sum([1..Length(x!.coeffs)],
               mu->Module(x!.H,x!.module,
                     Value(v^-EWeight(e,x!.parts[mu])*x!.coeffs[mu]),
                     MullineuxMap(e,x!.parts[mu])));
    fi;
  end
);

InstallMethod(MullineuxMapOp,"for ints: image of <mu> under the Mullineux map",
  [IsInt,IsList],
  function(e,mu)
    if not IsERegular(e,mu) then                     ## q-Schur algebra
      Error("# The Mullineux map is defined only for e-regular ",
            "partitions\n");
    fi;
    return PartitionGoodNodeSequence(e,
                  List(GoodNodeSequence(e,mu),x->-x mod e));
  end
);

InstallMethod(MullineuxMapOp,
  "for algebras: image of <mu> under the Mullineux map",
  [IsAlgebraObj,IsList],
  function(H,mu)
    return MullineuxMapOp(H!.e,mu);
  end
);

InstallMethod(MullineuxMapOp,
  "for decomposition matrices: image of <mu> under the Mullineux map",
  [IsDecompositionMatrix,IsList],
  function(d,mu) local e, x;
    e := d!.H!.e;
    if not IsERegular(e,mu) then                     ## q-Schur algebra
      Error("# The Mullineux map is defined only for e-regular ",
            "partitions\n");
    fi;
    x:=d!.H!.P(d,mu);
    if x=fail or x=0*x then
      Print("MullineuxMap(<d>,<mu>), P(<d>,<mu>) not known\n");
      return fail;
    else return ConjugatePartition(x!.parts[1]);
    fi;
    return true;
  end
); # MullineuxMap

#F Calculates the Specht modules in sum_{i>0}S^lambda(i) using the
## q-analogue of Schaper's theorem.
## Uses H.valuation.
##   Usage:  Schaper(H,mu);
InstallMethod(SchaperOp,"calculates Specht modules",[IsAlgebraObj,IsList],
  function(H,mu)
    local mud, schaper, hooklen, c, row, r, s, v;

    Sort(mu); mu:=mu{[Length(mu),Length(mu)-1..1]};
    mud:=ConjugatePartition(mu);
    hooklen:=[];
    for r in [1..Length(mu)] do
      hooklen[r]:=[];
      for c in [1..mu[r]] do
        hooklen[r][c]:=mu[r] + mud[c] - r - c + 1;
      od;
    od;

    schaper:=Module(H,"S",0,[]);
    for c in [1..mu[1]] do
      for row in [1..mud[1]] do
        for r in [row+1..mud[1]] do
          if mu[row] >=c and mu[r] >=c then
            v:=H!.valuation(hooklen[row][c])
                  - H!.valuation(hooklen[r][c]);
            if v<>0 then
              s:=AddRimHook(RemoveRimHook(mu,r,c,mud),row,hooklen[r][c]);
              if s<>fail then
                schaper:=schaper+Module(H,"S",
                                    (-1)^(s[2]+mud[c]-r)*v,s[1]);
              fi;
            fi;
          fi;
        od;
      od;
    od;
    return schaper;
  end
);  #Schaper

#F returns the matrix of upper bounds on the entries in the decomposition
## matrix <d> given by the q-Schaper theorem
## *** undocumented
InstallMethod(SchaperMatrix,"upper bounds of entries of a decomposition matrix",
  [IsDecompositionMatrix],
  function(d) local r, C, c, coeff, sh, shmat;
    shmat:=DecompositionMatrix(d!.H,d!.rows,d!.cols,true);
    shmat!.d:=List(shmat!.cols, c->rec(parts:=[],coeffs:=[]));
    C:=Length(d!.cols)+1; ## this keeps track of which column we're up to
    for r in [Length(d!.rows),Length(d!.rows)-1..1] do
      if d!.rows[r] in d!.cols then C:=C-1; fi;
      sh:=Schaper(d!.H,d!.rows[r]);
      for c in [C..Length(d!.cols)] do
        coeff:=InnerProduct(sh,MakePIMSpechtOp(d,d!.cols[c]));
        if coeff<>fail and coeff<>0*coeff then
          Add(shmat!.d[c].parts,r);
          Add(shmat!.d[c].coeffs,coeff);
        fi;
      od;
    od;
    sh:=[];
    for c in [1..Length(d!.d)] do
      Add(shmat!.d[c].parts, Position(shmat!.rows,shmat!.cols[c]));
      Add(shmat!.d[c].coeffs,1);
    od;
    shmat!.matname:="Schaper matrix";
    return shmat;
  end
);

## FORMER DECOMPOSITION MATRICES TOPLEVEL FUNCTIONS ############################
##############################################################
## Next some functions for accessing decomposition matrices ##
##############################################################

#F Returns the decomposition number d_{mu,nu}; row and column removal
## are used if the projective P(nu) is not already known.
## Usage: DecompositionNumber(H,mu,nu), or
##        DecompositionNumber(d,mu,nu);
## If unable to calculate the decomposition number we return false.
## Note that H.IsSpecht is false if we are looking at decomposition matrices
## of a q-Schur algebra and true for a Hecke algebra.
InstallMethod(DecompositionNumber,
  "for a decomposition matrix and two partitions",
  [IsDecompositionMatrix,IsList,IsList],
  function(d,mu,nu) local Pnu;
    if mu=nu then return 1;
    elif not Dominates(nu,mu) then return 0;
    else
      Pnu:=MakePIMSpechtOp(d,nu);
      if Pnu<>fail then return Coefficient(Pnu,mu); fi;
      return Specht_DecompositionNumber(d!.H,mu,nu);
    fi;
  end
);

InstallMethod(DecompositionNumber,"for an algebra and two partitions",
  [IsAlgebraObj,IsList,IsList],
  function(H,mu,nu) local Pnu;
    Pnu:=MakeSpechtOp(Module(H,"P",1,nu),true);
    if Pnu<>fail then return Coefficient(Pnu,mu); fi;
    if not IsSchur(H) and not IsERegular(H!.e, nu) then
      Error("DecompositionNumber(H,mu,nu), <nu> is not ",H!.e,"-regular");
    fi;
    return Specht_DecompositionNumber(H,mu,nu);
  end
);

InstallMethod(Specht_DecompositionNumber,
  "internal: for an algebra and two partitions",
  [IsAlgebraObj,IsList,IsList],
  function(H,mu,nu) local Pnu, RowAndColumnRemoval;

    ## Next we try row and column removal (James, Theorem 6.18)
    ## (here fn is either the identity or conjugation).
    RowAndColumnRemoval:=function(fn) local m,n,i,d1,d2;
      ## x, mu, and nu as above

      mu:=fn(mu); nu:=fn(nu);

      m:=0; n:=0; i:=1;
      while i<Length(nu) and i<Length(mu) do
        m:=m+mu[i]; n:=n+nu[i];
        if m=n then
          d2:=DecompositionNumber(H, fn(mu{[i+1..Length(mu)]}),
                     fn(nu{[i+1..Length(nu)]}));
          if d2=0 then return d2;
          elif IsInt(d2) then
            d1:=DecompositionNumber(H, fn(mu{[1..i]}),fn(nu{[1..i]}));
            if IsInt(d1) then return d1*d2; fi;
          fi;
        fi;
        i:=i+1;
      od;
      return fail;
    end;

    Pnu:=RowAndColumnRemoval(a->a);
    if Pnu=fail then Pnu:=RowAndColumnRemoval(ConjugatePartition); fi;
    return Pnu;
  end
);

#F Returns a list of those e-regular partitions mu such that Px-P(mu)
## has positive coefficients (ie. those partitions mu such that P(mu)
## could potentially split off Px). Simple minded, but useful.
InstallMethod(Obstructions,"for a decomposition matrix and a module",
  [IsDecompositionMatrix,IsAlgebraObjModule],
  function(d,Px) local obs, mu, Pmu, possibles;
    obs:=[];
    if not IsSchur(d!.H) then
      possibles:=Filtered(Px!.parts, mu->IsERegular(Px!.H!.e, mu));
    else possibles:=Px!.parts;
    fi;
    for mu in possibles do
      if mu<>Px!.parts[Length(Px!.parts)] then
        Pmu:=MakePIMSpechtOp(d,mu);
        if Pmu=fail or PositiveCoefficients(Px-Pmu) then Add(obs,mu); fi;
      fi;
    od;
    return obs{[Length(obs),Length(obs)-1..1]};
  end
);

## Interface to d.operations.IsNewDecompositionMatrix. Returns true
## if <Px> contains an indecomposable not listed in <d> and false
## otherwise. Note that the value of <Px> may well be changed by
## this function. If the argument <mu> is used then we assume
## that all of the decomposition numbers down given by <Px> down to
## <mu> are correct. Note also that if d is the decomposition matrix
## for H(Sym_{r+1}) then the decomposition matrix for H(Sym_r) is passed
## to IsNewDecompositionMatrix.
##   Usage: IsNewIndecomposable(<d>,<Px> [,<mu>]);
## If <mu> is not supplied then we set mu:=true; this
## turns on the message printing in IsNewIndecomposable().
InstallMethod(IsNewIndecomposableOp,
  "for a decomposition matrix and a module",
  [IsDecompositionMatrix,IsAlgebraObjModule],
  function(d,Px) local oldd;
    oldd:=FindDecompositionMatrix(d!.H,Sum(d!.rows[1])-1);
    return IsNewIndecomposableOp(d!.H,d,Px,oldd,[]);
  end
);

InstallMethod(IsNewIndecomposableOp,
  "for a decomposition matrix, a module and a partition",
  [IsDecompositionMatrix,IsAlgebraObjModule,IsList],
  function(d,Px,mu) local oldd;
    oldd:=FindDecompositionMatrix(d!.H,Sum(d!.rows[1])-1);
    return IsNewIndecomposableOp(d!.H,d,Px,oldd,mu);
  end
); # IsNewIndecomposable (toplevel)

##P Removes the columns for <Px> in <d>
InstallMethod(RemoveIndecomposableOp,
  "for a decomposition matrix and a partition",
  [IsDecompositionMatrix,IsList],
  function(d,mu) local r, c;
    c:=Position(d!.cols, mu);
    if c=fail then
      Print("RemoveIndecomposable(<d>,<mu>), <mu> is not listed in <d>\n");
    else Unbind(d!.d[c]);
    fi;
  end
); # RemoveIndecomposable

### Prints a list of the indecomposable missing from d
InstallMethod(MissingIndecomposables,
  "missing entries of a decomposition matrix",
  [IsDecompositionMatrix],
  function(d) local c, missing;
    missing:=List([1..Length(d!.cols)], c->not IsBound(d!.d[c]) );
    if true in missing then
      Print("The following projectives are missing from <d>:\n  ");
      for c in [Length(missing),Length(missing)-1..1] do
        if missing[c] then Print("  ", d!.cols[c]); fi;
      od;
      Print("\n");
    fi;
  end
); # MissingIndecomposables

## When no ordering is supplied then rows are ordered first by length and
## then lexicographically. The rows and columns may also be explicitly
## assigned.
## Usage:
##   DecompositionMatrix(H, n [,ordering]);
##   DecompositionMatrix(H, <file>) ** force Specht() to read <file>
InstallOtherMethod(DecompositionMatrix,"for an algebra and an integer",
  [IsAlgebraObj,IsInt],
  function(H,n) local Px, d, c;
    d:=FindDecompositionMatrix(H,n);

    if d=fail then
      if H!.p>0 and n>2*H!.e then  ## no point even trying
        Print("# This decomposition matrix is not known; use ",
              "CalculateDecompositionMatrix()\n# or ",
              "InducedDecompositionMatrix() to calculate with this matrix.",
              "\n");
        return d;
      fi;
      if not IsSchur(H) then c:=ERegularPartitions(H!.e,n);
      else c:=Partitions(n);
      fi;
      d:=DecompositionMatrix(H,Partitions(n),c,true);
    fi;
    if ForAny([1..Length(d!.cols)],c->not IsBound(d!.d[c])) then
      for c in [1..Length(d!.cols)] do
        if not IsBound(d!.d[c]) then
          Px:=MakeSpechtOp(Module(H,"P",1,d!.cols[c]),true);
          if Px<>fail then AddIndecomposable(d,Px,false);
          else Print("# Projective indecomposable P(",
                     TightStringList(d!.cols[c]),") not known.\n");
          fi;
        fi;
      od;
      Store(d,n);
    fi;
    if d<>fail then   ## can't risk corrupting the internal matrix lists
      d:=CopyDecompositionMatrix(d);
    fi;
    return d;
  end
);

InstallOtherMethod(DecompositionMatrix,
  "for an algebra, an integer and an ordering",
  [IsAlgebraObj,IsInt,IsFunction],
  function(H,n,ord)
    H!.Ordering := ord;
    return DecompositionMatrix(H,n);
  end
);

InstallOtherMethod(DecompositionMatrix,"for an algebra and a filename",
  [IsAlgebraObj,IsString],
  function(H,file) local d;
    d:=ReadDecompositionMatrix(H,file,false);
    if d<>fail and not IsBound(d!.matname) then ## override and copy
      Store(d,Sum(d!.cols[1]));
      MissingIndecomposables(d);
    fi;
    if d<>fail then   ## can't risk corrupting the internal matrix lists
      d:=CopyDecompositionMatrix(d);
    fi;
    return d;
  end
);

InstallOtherMethod(DecompositionMatrix,
  "for an algebra, a filename and an ordering",
  [IsAlgebraObj,IsString,IsFunction],
  function(H,file,ord)
      H!.Ordering := ord;
    return DecompositionMatrix(H,file);
  end
); # DecompositionMatrix

#F Tries to calculate the decomposition matrix d_{H,n} from scratch.
## At present will return only those column indexed by the partitions
## of e-weight less than 2.
InstallMethod(CalculateDecompositionMatrix,"for an algebra and an integer",
  [IsAlgebraObj,IsInt],
  function(H,n) local d, c, Px;
    if not IsSchur(H) then c:=ERegularPartitions(H!.e,n);
    else c:=Partitions(n);
    fi;
    d:=DecompositionMatrix(H,Partitions(n),c,true);
    for c in [1..Length(d!.cols)] do
      if not IsBound(d!.d[c]) then
        Px:=MakeSpechtOp(Module(H,"P",1,d!.cols[c]),true);
        if Px<>fail then AddIndecomposable(d,Px,false);
         else Print("# Projective indecomposable P(",
                    TightStringList(d!.cols[c]),") not known.\n");
        fi;
      fi;
    od;
    return d;
  end
); # CalculateDecompositionMatrix

#F Returns a crystallized decomposition matrix
InstallMethod(CrystalDecompositionMatrix,"for an algebra and an integer",
  [IsAlgebraObj,IsInt],
  function(H,n) local d, Px, c;
    if not IsZeroCharacteristic(H) or IsSchur(H) then
      Error("Crystal decomposition matrices are defined only ",
           "for Hecke algebras\n         with H!.p=0\n");
    fi;

    d:=ReadDecompositionMatrix(H,n,true);
    if d<>fail then d:=CopyDecompositionMatrix(d);
    else d:=DecompositionMatrix(H,
                Partitions(n),ERegularPartitions(H!.e,n),false);
    fi;
    for c in [1..Length(d!.cols)] do
      if not IsBound(d!.d[c]) then
        AddIndecomposable(d,FindPq(H,d!.cols[c]),false);
      fi;
    od;
    return d;
  end
);

InstallMethod(CrystalDecompositionMatrix,
  "for an algebra, an integer and an ordering",
  [IsAlgebraObj,IsInt,IsFunction],
  function(H,n,ord)
    H!.Ordering := ord;
    return CrystalDecompositionMatrix(H,n);
  end
); # CrystalDecompositionMatrix

## Given a decomposition matrix induce it to find as many columns as
## possible of the next higher matrix using simple minded induction.
## Not as simple minded as it was originally, as it now tries to use
## Schaper's theorem [JM2] to break up troublesome projectives. The
## function looks deceptively simple because all of the work is now
## done by IsNewIndecomposable().
## Usage: InducedDecompositionMatrix(dn)
## in the second form new columns are added to d{n+1}.
InstallMethod(InducedDecompositionMatrix,"induce from decomposition matrix",
  [IsDecompositionMatrix],
  function(d)
    local newd, mu, nu, Px, Py, n,r, needNewline;

    if IsCrystalDecompositionMatrix(d)
    then Error("InducedDecompositionMatrix(d): ",
                 "<d> must be a decomposition matrix.");
    fi;

    needNewline := false;
    n:=Sum(d!.rows[1])+1;
    if n>8 then                            ## print dots to let the user
      PrintTo("*stdout*","# Inducing.");   ## know something is happening.
      needNewline := true;
    fi;

    nu:=Partitions(n);
    if IsSchur(d!.H) then
      newd:=DecompositionMatrix(d!.H, nu, nu, true);
    else newd:=DecompositionMatrix(d!.H, nu,
                ERegularPartitions(d!.H!.e,n),true);
    fi;

    ## add any P(mu)'s with EWeight(mu)<=1 or P(mu)=S(mu) <=> S(mu')=D(mu')
    for mu in newd!.cols do
      if EWeight(d!.H!.e,mu)<=1 then
       AddIndecomposable(newd,
           MakeSpechtOp(Module(d!.H,"P",1,mu),true),false);
      elif IsSimpleModule(d!.H,ConjugatePartition(mu)) then
        AddIndecomposable(newd, Module(d!.H,"S",1,mu),false);
      fi;
    od;

    ## next we r-induce all of the partitions in d so we can just add
    ## them up as we need them later.
    ## (note that this InducedModule() is Specht()'s and not the generic one)
    d!.ind:=List(d!.rows, mu->List([0..d!.H!.e-1],
              r->RInducedModule(d!.H,Module(d!.H,"S",1,mu),d!.H!.e,r)));

    if n<9 then n:=Length(d!.cols)+1; fi; ## fudge for user friendliness

    for mu in [1..Length(d!.cols)] do
      if IsBound(d!.d[mu]) then
        for r in [1..d!.H!.e] do   ## really the e-residues; see ind above
          ## Here we calculate InducedModule(P(mu),H.e,r).
          Px:=Sum([1..Length(d!.d[mu].parts)],
                     nu->d!.d[mu].coeffs[nu]*d!.ind[d!.d[mu].parts[nu]][r]);
          if IsNewIndecomposableOp(d!.H,newd,Px,d,[fail]) then
            if IsERegular(Px!.H!.e,Px!.parts[Length(Px!.parts)]) then
              # can apply MullineuxMap
              nu:=ConjugatePartition(Px!.parts[1]);
              if nu<>MullineuxMap(d!.H!.e,Px!.parts[Length(Px!.parts)]) then
                ## wrong image under the Mullineux map
                BUG("Induce", 7, "nu = ", nu, ", Px = ", Px);
              else   ## place the Mullineux image of Px as well
                AddIndecomposable(newd,MullineuxMap(Px),false);
              fi;
            fi;
            AddIndecomposable(newd,Px,false);
          fi;
        od;
        if mu mod n = 0 then PrintTo("*stdout*","."); needNewline := true; fi;
      fi;
    od;
    Unbind(d!.ind); Unbind(d!.simples); ## maybe we should leave these.

    if needNewline then Print("\n"); fi;
    MissingIndecomposables(newd);
    return newd;
  end
); # InducedDecompositionMatrix

#F Returns the inverse of (the e-regular part of) d. We invert the matrix
## 'by hand' because the matrix routines can't handle polynomial entries.
## This should be much faster than it is???
InstallMethod(InvertDecompositionMatrix,"for a decomposition matrix",
  [IsDecompositionMatrix],
  function(d) local inverse, c, r;
    inverse:=DecompositionMatrix(d!.H,d!.cols,d!.cols,
                                          not IsCrystalDecompositionMatrix(d));

    ## for some reason I can't put this inside the second loop (deleting
    ## the first because d.inverse is not updated this way around...).
    for c in [1..Length(inverse!.cols)] do
      Invert(d,d!.cols[c]);
    od;
    for c in [1..Length(inverse!.cols)] do
      if IsBound(d!.inverse[c]) then
        inverse!.d[c]:=rec(parts:=[], coeffs:=[]);
        for r in [1..c] do
          if IsBound(d!.inverse[r]) and c in d!.inverse[r].parts then
            Add(inverse!.d[c].parts,r);
            Add(inverse!.d[c].coeffs,
                d!.inverse[r].coeffs[Position(d!.inverse[r].parts,c)]);
          fi;
        od;
        if inverse!.d[c]=rec(parts:=[], coeffs:=[]) then Unbind(inverse!.d[c]); fi;
      fi;
    od;
    inverse!.matname:="Inverse matrix";
    return inverse;
  end
); # InvertDecompositionMatrix

#P Saves a full decomposition matrix; actually, only the d, rows, and cols
## records components are saved and the rest calculated when read back in.
## The decomposition matrices are saved in the following format:
##   A_Specht_Decomposition_Matrix:=rec(
##   d:=[[r1,...,rk,d1,...dk],[...],...[]],rows:=[..],cols:=[...]);
## where r1,...,rk are the rows in the first column with corresponding
## decomposition numbers d1,...,dk (if di is a polynomial then it is saved
## as a list [di.valuation,<sequence of di.coffcients]; in particular we
## don't save the polynomial name).
## Usage: SaveDecompositionMatrix(<d>)
##    or  SaveDecompositionMatrix(<d>,<filename>);
InstallMethod(SaveDecompositionMatrix,
  "for a decomposition matrix and a filename",
  [IsDecompositionMatrix,IsString],
  function(d,file)
    local TightList,n,SaveDm,size, r, c, str, tmp;

    n:=Sum(d!.rows[1]);

    size:=SizeScreen();    ## SizeScreen(0 shouldn't affect PrintTo()
    SizeScreen([80,40]);  ## but it does; this is our protection.

    TightList:=function(list) local l, str;
      str:="[";
      for l in list{[1..Length(list)-1]} do
        if IsList(l) then
          l:=TightList(l);
          str:=Concatenation(str,l);
        else str:=Concatenation(str,String(l));
        fi;
        str:=Concatenation(str,",");
      od;
      l:=list[Length(list)];
      if IsList(l) then
        l:=TightList(l);
        str:=Concatenation(str,l);
      else str:=Concatenation(str,String(l));
      fi;
      return Concatenation(str,"]");
    end;

    if d=fail then Error("SaveDecompositionMatrix(<d>), d=fail!!!\n"); fi;

    SaveDm:=function(file)
      AppendTo(file,"## This is a GAP library file generated by \n## Hecke ",
            d!.H!.info.version, "\n\n## This file contains ");
      if IsBound(d!.matname) then
        AppendTo(file,"a(n) ", d!.matname, " for n = ", Sum(d!.rows[1]),"\n");
      else
        if IsCrystalDecompositionMatrix(d) then AppendTo(file,"the crystallized "); fi;
        AppendTo(file,"the decomposition matrix\n## of the ");
        if not IsSchur(d!.H) then
          if d!.H!.e<>d!.H!.p then AppendTo(file,"Hecke algebra of ");
          else AppendTo(file,"symmetric group ");
          fi;
        else AppendTo(file,"q-Schur algebra of ");
        fi;
        AppendTo(file,"Sym(",n,") over a field\n## ");
        if d!.H!.p=0 then AppendTo(file,"of characteristic 0 with ");
        elif d!.H!.p=d!.H!.e then AppendTo(file,"of characteristic ",d!.H!.p,".\n\n");
        else AppendTo(file,"with HeckeRing = ", d!.H!.HeckeRing, ", and ");
        fi;
        if d!.H!.p<>d!.H!.e then AppendTo(file,"e=", d!.H!.e, ".\n\n");fi;
      fi;

      AppendTo(file,"A_Specht_Decomposition_Matrix:=rec(\nd:=[");
      str:="[";
      for c in [1..Length(d!.cols)] do
        if not IsBound(d!.d[c]) then AppendTo(file,str,"]");
        else
          for r in d!.d[c].coeffs do
            if IsLaurentPolynomial(r) then
            tmp:=ShallowCopy(CoefficientsOfLaurentPolynomial(r));
            AppendTo(file,str,"[",tmp[2],",",TightStringList(tmp[1]),"]");
            else AppendTo(file,str,r);
            fi;
            str:=",";
          od;
          for r in d!.d[c].parts do
            AppendTo(file,str,r);
          od;
          AppendTo(file,"]");
          str:=",[";
        fi;
      od;
      AppendTo(file,"],rows:=",TightList(d!.rows));
      AppendTo(file,",cols:=",TightList(d!.cols));
      if IsCrystalDecompositionMatrix(d) then
        AppendTo(file,",crystal:=true");
      fi;
      if IsBound(d!.matname) then AppendTo(file,",matname:=\"",d!.matname,"\""); fi;
      AppendTo(file,");\n");
    end;

    ## the actual saving of d
    SaveDm(file);

    ## now we put d into DecompositionMatrices
    if not IsBound(d!.matname) then Store(d,n); fi;

    SizeScreen(size); # restore screen.
  end
);

InstallMethod(SaveDecompositionMatrix,"for a decomposition matrix",
  [IsDecompositionMatrix],
  function(d) local n,file;
    if d!.H!.HeckeRing="unknown" then
      Print("SaveDecompositionMatrix(d): \n     the base ring of the Hecke ",
            "algebra is unknown.\n     You must set <d>.H.HeckeRing in ",
            "order to save <d>.\n");
      return;
    fi;

    n:=Sum(d!.rows[1]);

    if IsBound(d!.matname) then
      file:=Concatenation(d!.H!.HeckeRing,".",d!.matname{[1]},String(n));
    elif not IsCrystalDecompositionMatrix(d) then
      file:=Concatenation(d!.H!.HeckeRing,".",String(n));
    else  ## crystallized decomposition matrix
      file:=Concatenation("e", String(d!.H!.e), "crys.", String(n));
    fi;

    SaveDecompositionMatrix(d,file);
  end
);# SaveDecompositionMatrix()

#F Returns the 'adjustment matrix' [J] for <d> and <dp>. ie the
## matrix <a> such that <dp>=<a>*<d>.
InstallMethod(AdjustmentMatrix,"for two decomposition matrices",
  [IsDecompositionMatrix,IsDecompositionMatrix],
  function(dp,d) local ad, c, x;
    if d!.cols<>dp!.cols or d!.rows<>dp!.rows then return fail; fi;

    ad:=DecompositionMatrix(dp!.H,dp!.cols,dp!.cols,true);
    ad!.matname:="Adjustment matrix";
    c:=1;
    while ad<>fail and c<=Length(d!.cols) do
      if IsBound(dp!.cols[c]) then
        x:=MakePIMSpechtOp(dp, dp!.cols[c]);
        x!.H:=d!.H;
        x:=MakePIMOp(d,x);
        if x=fail then ad:=fail;
        else AddIndecomposable(ad,x,false);
        fi;
      fi;
      c:=c+1;
    od;
    return ad;
  end
); # AdjustmentMatrix

## Returns the a GAP matrix for the decomposition matrix <d>. Note that
## the rows and columns and <d> are ordered according to H.info.Ordering.
InstallMethod(MatrixDecompositionMatrix,"decomposition matrix -> matrix",
  [IsDecompositionMatrix],
  function(d) local r,c, rows, cols, m;
    rows:=StructuralCopy(d!.rows);
    if d!.H!.Ordering<>Lexicographic then
      Sort(rows,d!.H!.Ordering);
      rows:=List(rows,r->Position(d!.rows,r));
    else rows:=[Length(rows),Length(rows)-1..1];
    fi;
    cols:=StructuralCopy(d!.cols);
    if d!.H!.Ordering<>Lexicographic then
      Sort(cols,d!.H!.Ordering);
      cols:=List(cols,r->Position(d!.cols,r));
    else cols:=[Length(cols),Length(cols)-1..1];
    fi;
    m:=[];
    for r in [1..Length(rows)] do
      m[r]:=[];
      for c in [1..Length(cols)] do
        if IsBound(d!.d[cols[c]]) and rows[r] in d!.d[cols[c]].parts then
          m[r][c]:=d!.d[cols[c]].coeffs[Position(d!.d[cols[c]].parts,rows[r])];
        else m[r][c]:=0;
        fi;
      od;
    od;
    return m;
  end
);

## Given a GAP matrix this function returns a Specht decomposition matrix.
##   H = Specht() record
##   m = matrix: either #reg x #reg, #parts x #reg, or #parts x #parts
##   n = Sym(n)
InstallMethod(DecompositionMatrixMatrix,"matrix -> decomposition matrix",
  [IsAlgebraObj,IsMatrix,IsInt],
  function(H,m,n) local r, c, rows, cols, d;
    rows:=Partitions(n);
    cols:=ERegularPartitions(H,n);
    if Length(rows)<>Length(m) then rows:=cols; fi;
    if Length(cols)<>Length(m[1]) then cols:=rows; fi;
    if Length(rows)<>Length(m) or Length(cols)<>Length(m[1]) then
       Print("# usage: DecompositionMatrixMatrix(H, m, n)\n",
             "   where m is a matrix of an appropriate size.\n");
       return fail;
    fi;
    if ForAll(m, r->ForAll(r, c->IsInt(c) )) then
      d:=DecompositionMatrix(H,StructuralCopy(rows),StructuralCopy(cols),true);
    else  ## presumably crystalized
      d:=DecompositionMatrix(H,StructuralCopy(rows),StructuralCopy(cols),false);
    fi;
    ## now we order the rows and columns properly
    if H!.Ordering<>Lexicographic then Sort(rows, H!.Ordering);
    else rows:=rows{[Length(rows),Length(rows)-1..1]};
    fi;
    rows:=List(d!.rows, r->Position(rows, r) );
    if H!.Ordering<>Lexicographic then Sort(cols, H!.Ordering);
    else cols:=cols{[Length(cols),Length(cols)-1..1]};
    fi;
    cols:=List(d!.cols, c->Position(cols, c) );
    for c in [1..Length(cols)] do
       d!.d[c]:=rec(parts:=[], coeffs:=[]);
       for r in [1..Length(rows)] do
         if m[rows[r]][cols[c]]<>0*m[rows[r]][cols[c]] then ## maybe polynomial
           Add(d!.d[c].parts, r);
           Add(d!.d[c].coeffs, m[rows[r]][cols[c]]);
         fi;
       od;
       if d!.d[c].parts=[] then Unbind(d!.d[c]); fi;
    od;
    return d;
  end
); # DecompositionMatrixMatrix

## OPERATIONS OF FORMER SPECHT RECORD ##########################################

## The following two functions are used by P(), and elsewhere.
##   generate the hook (k,1^n-k)  - as a list - where k=arg
##   actually not quite a hook since if arg is a list (n,k1,k2,...)
##   this returns (k1,k2,...,1^(n-sum k_i))
InstallMethod(HookOp,"for an integer and a list of lists",[IsInt,IsList],
  function(n,K) local k, i;
    k:=Sum(K);
    if k < n then Append(K, List([1..(n-k)], i->1));
    elif k > n then Error("hook, partition ", k, " bigger than ",n, "\n");
    fi;
    return K;
  end
); #Hook

InstallMethod(DoubleHook,"for four integers",[IsInt,IsInt,IsInt,IsInt],
  function(n,x,y,a) local s, i;
    s:=[x];
    if y<>0 then Add(s,y); fi;
    if a<>0 then Append(s, List([1..a],i->2)); fi;
    i:=Sum(s);
    if i < n then
      Append(s, List([1..n-i], i->1));
      return s;
    elif i=n then return s;
    else return [];
    fi;
  end
); #DoubleHook

#### RENAMED Omega -> HeckeOmega
## Returns p(n) - p(n-1,1) + p(n-2,1^2) - ... + (-1)^(n-1)*p(1^n).
## So, S(mu)*Omega(n) is the linear combination of the S(nu)'s where
## nu is obtained by wrapping an n-hook onto mu and attaching the
## sign of the leg length.
InstallMethod(HeckeOmega,"for an algebra, a string and an integer",
  [IsAlgebraObj,IsString,IsInt],
  function(H,module,n)
    return Module(H,module,List([1..n],x->(-1)^(x)),
                     List([1..n],x->Hook(n,x)));
  end
); #HeckeOmega

## MODULES #####################################################################
InstallMethod(Module,"create new module",[IsAlgebraObj,IsString,IsList,IsList],
  function(H,m,c,p)
    local module;
    module := rec(H:=H,module:=m,coeffs:=c,parts:=p);

    if m = "S" and not IsSchur(H) then Objectify(HeckeSpechtType,module);
    elif m = "P" and not IsSchur(H) then Objectify(HeckePIMType,module);
    elif m = "D" and not IsSchur(H) then Objectify(HeckeSimpleType,module);
    elif m = "Sq" and not IsSchur(H) then Objectify(HeckeSpechtFockType,module);
    elif m = "Pq" and not IsSchur(H) then Objectify(HeckePIMFockType,module);
    elif m = "Dq" and not IsSchur(H) then Objectify(HeckeSimpleFockType,module);
    elif m = "W" or m = "S" then Objectify(SchurWeylType,module); module!.module:="W";
    elif m = "P" then Objectify(SchurPIMType,module);
    elif m = "F" or m = "D" then Objectify(SchurSimpleType,module); module!.module:="F";
    elif m = "Wq" or m = "Sq" then Objectify(SchurWeylFockType,module); module!.module:="Wq";
    elif m = "Pq" then Objectify(SchurPIMFockType,module);
    elif m = "Fq" or m = "Dq" then Objectify(SchurSimpleFockType,module); module!.module:="Fq";
    fi;

    return module;
  end
);

InstallMethod(Module,"create new module",[IsAlgebraObj,IsString,IsInt,IsList],
  function(H,m,c,p)
    return Module(H,m,[c],[p]);
  end
);

InstallMethod(Module,"create new module",[IsAlgebraObj,IsString,IsLaurentPolynomial,IsList],
  function(H,m,c,p)
    return Module(H,m,[c],[p]);
  end
);

## Takes two lists, one containing coefficients and the other the
## corresponding partitions, and orders them lexicographically collecting
## like terms on the way. We use a variation on quicksort which is
## induced by the lexicographic order (if parts contains partitions of
## different integers this can lead to an error - which we don't catch).
InstallMethod(Collect,"utility for module generation",
  [IsAlgebraObj,IsString,IsList,IsList],
  function(H, module, coeffs, parts)
    local newx, i, Place, Unplace, places;

    ## inserting parts[i] into places. if parts[i]=[p1,p2,...] then
    ## we insert it into places at places[p1][[p2][...] stopping
    ## at the fist empty position (say places[p1], or places[p1][p2]
    ## etc.). Here we are trying to position parts[i] at
    ## places[p1]...[pd]. Actually, we just insert i rather than
    ## parts[i] and if we find that parts[i]=parts[j] for some j then
    ## we set coeffs[i]:=coeffs[i]+coeffs[j] and don't place j.
    Place:=function(i, places, d) local t;
      if IsInt(places) then
        t:=places;
        if parts[t]=parts[i] then coeffs[t]:=coeffs[t]+coeffs[i];
        else
          places:=[];
          places[parts[t][d]]:=t;
          if parts[i][d]<>parts[t][d] then places[parts[i][d]]:=i;
          else places:=Place(i, places, d);
          fi;
        fi;
      elif places=[] or not IsBound(places[parts[i][d]]) then
        # must be a list
        places[parts[i][d]]:=i;
      else places[parts[i][d]]:=Place(i, places[parts[i][d]], d+1);
      fi;
      return places;
    end;

    Unplace:=function(places) local p, newp, np;
      newp:=[[],[]];
      for p in places do
        if IsInt(p) then if coeffs[p]<>0*coeffs[p] then
          Add(newp[1], coeffs[p]);
          Add(newp[2], StructuralCopy(parts[p])); fi;
        else
          np:=Unplace(p);
          Append(newp[1], np[1]);
          Append(newp[2], np[2]);
        fi;
      od;
      return newp;
    end;

   if parts=[] then return Module(H,module,0,[]);
   elif Length(parts)=1 then return Module(H,module,coeffs,parts);
   else places:=[];
      for i in [1..Length(parts)] do places:=Place(i, places, 1); od;
      newx:=Unplace(places);
      if newx=[[],[]] then return Module(H,module,0,[]);
      else return Module(H,module,newx[1],newx[2]);
      fi;
    fi;
  end  ## H.operations.Collect
);

## MODULE CONVERSION ###########################################################

## Finally the conversion functions S(), P() and D(). All take
## a linear combination of Specht modules and return corresponding
## linear combinations of Specht, indecomposables, and simples resp.
## If they have a problem they return false and print an error
## message unless silent=true.
InstallMethod(MakeSpechtOp,"S()->S()",[IsHeckeSpecht,IsBool],
  function(x,silent) return x; end
);

InstallMethod(MakeSpechtOp,"S[q]()->S[q]()",[IsDecompositionMatrix,IsHeckeSpecht],
  function(d,x) return x; end
);

## Here I only allow for linear combinations of projectives which
## have non-negative coefficients; the reason for this is that I
## can't see how to do it otherwise. The problem is that in the
## Grothendieck ring there are many ways to write a given linear
## combination of Specht modules (or PIMs).
InstallMethod(MakePIMOp,"S()->P()",[IsHeckeSpecht,IsBool],
  function(x,silent) local proj, tmp;
    if x=fail or x=0*x then return x;
    elif x!.parts=[[]] then return Module(x!.H,"P",x!.coeffs[1],[]);
    fi;

    proj:=Module(x!.H,"P",0,[]);
    while x<>fail and x<>0*x and
    ( IsSchur(x!.H) or IsERegular(x!.H!.e,x!.parts[Length(x!.parts)]) ) do
      proj:=proj+Module(x!.H,"P",x!.coeffs[Length(x!.parts)],
                                      x!.parts[Length(x!.parts)]);
      tmp:=MakeSpechtOp(
                Module(x!.H,"P",-x!.coeffs[Length(x!.parts)],
                                      x!.parts[Length(x!.parts)]),true);
      if tmp<>fail then x:=x+tmp; else x:=fail; fi;
    od;
    if x=fail or x<>0*x then
      if not silent then
        Print("# P(<x>), unable to rewrite <x> as a sum of projectives\n");
      fi;
    else return proj;
    fi;
    return fail;
  end
);

InstallMethod(MakePIMOp,"S[q]()->P[q]()",[IsDecompositionMatrix,IsHeckeSpecht],
  function(d,x) local nx, r, c, P, S;
    if x=fail or x=0*x then return x; fi;
    if IsCrystalDecompositionMatrix(d) then P:="Pq"; S:="Sq";
    else P:="P"; S:="S";
    fi;

    nx:=Module(x!.H,P,0,[]);
    while x<>fail and x<>0*x do
      c:=Position(d!.cols,x!.parts[Length(x!.parts)]);
      if c=fail or not IsBound(d!.d[c]) then return fail; fi;
      nx:=nx+Module(x!.H,P,x!.coeffs[Length(x!.parts)],d!.cols[c]);
      x:=x+Module(x!.H,S,-x!.coeffs[Length(x!.parts)]*d!.d[c].coeffs,
                            List(d!.d[c].parts,r->d!.rows[r]));
    od;
    return nx;
  end
);

InstallMethod(MakeSimpleOp,"S()->D()",[IsHeckeSpecht,IsBool],
  function(x,silent) local y, d, simples, r, c;
    if x=fail or x=0*x then return x;
    elif x!.parts=[[]] then return Module(x!.H,"D",x!.coeffs[1],[]);
    fi;

    d:=KnownDecompositionMatrix(x!.H,Sum(x!.parts[1]));
    if d<>fail then
      y:=MakeSimpleOp(d,x);
      if y<>fail then return y; fi;
    fi;

    ## since that didn't work, we use the LLT algorithm when IsBound(H.Pq)
    if IsZeroCharacteristic(x!.H) and not IsSchur(x!.H) then
      return Sum([1..Length(x!.parts)],
                 r->x!.coeffs[r]*Specialized(FindSq(x!.H,x!.parts[r])));
    fi;

    # next, see if we can calculate the answer.
    d:=Concatenation(x!.H!.HeckeRing,"D");
    # finally, we can hope that only partitions of e-weight<2 appear in x
    r:=1; simples:=Module(x!.H,"D",0,[]);
    while simples<>fail and r <= Length(x!.parts) do
      if IsSimpleModule(x!.H, x!.parts[r]) then
        simples:=simples+Module(x!.H,"D",x!.coeffs[r], x!.parts[r]);
      elif IsERegular(x!.H!.e,x!.parts[r]) and EWeight(x!.H!.e,x!.parts[r])=1
      then
        y:=Module(x!.H,"S",1,ECore(x!.H!.e,x!.parts[r]))
                           * HeckeOmega(x!.H,"S",x!.H!.e);
        c:=Position(y!.parts,x!.parts[r]); ## >1 since not IsSimpleModule
        simples:=simples
                  +Module(x!.H,"D",[1,1],[y!.parts[c],y!.parts[c-1]]);
      #### elif IsBound(x.operations.(d)) then ## FIXME not needed anymore?
      ####   simples:=simples+x.operations.(d)(x.parts[r]);
      else simples:=fail;
      fi;
      r:=r+1;
    od;
    if simples=fail and not silent then
      Print("# D(<x>), unable to rewrite <x> as a sum of simples\n");
      return fail;
    else return simples;
    fi;
  end
);

InstallMethod(MakeSimpleOp,"S[q]()->D[q]()",[IsDecompositionMatrix,IsHeckeSpecht],
  function(d,x) local nx, y, r, rr, c, D, core;
    if x=fail or x=0*x then return x; fi;
    if IsCrystalDecompositionMatrix(d) then D:="Dq"; else D:="D"; fi;

    nx:=Module(x!.H,D,0,[]);
    for y in [1..Length(x!.parts)] do
      r:=Position(d!.rows, x!.parts[y]);
      if r=fail then return fail; fi;
      core:=ECore(x!.H!.e,x!.parts[y]);
      c:=Length(d!.cols);
      while c>0 and d!.cols[c]>=x!.parts[y] do
        if IsBound(d!.d[c]) then
          rr:=Position(d!.d[c].parts,r);
          if rr<>fail then nx:=nx+Module(x!.H,D,
                         x!.coeffs[y]*d!.d[c].coeffs[rr],d!.cols[c]);
          fi;
        elif ECore(x!.H!.e,d!.cols[c])=core then return fail;
        fi;
        c:=c-1;
      od;
    od;
    return nx;
  end
);

## The P->S functions are quite involved.

#F Writes x, which is a sum of indecomposables, as a sum of S(nu)'s if
## possible. We first check to see if the decomposition matrix for x is
## stored somewhere, and if not we try to calculate what we need. If we
## can't do this we return false.
InstallMethod(MakeSpechtOp,"P()->S()",[IsHeckePIM,IsBool],
  function(x,silent) local y, c, d, mu, specht;
    if x=fail or x=0*x then return x;
    elif x!.parts=[[]] then return Module(x!.H,"S",x!.coeffs[1],[]);
    fi;
    d:=KnownDecompositionMatrix(x!.H,Sum(x!.parts[1]));
    if d<>fail then
      y:=MakeSpechtOp(d,x);
      if y<>fail then return y; fi;
    fi;

    ## since that didn't work, we use the LLT algorithm when
    ## IsBound(H.Pq)
    if IsZeroCharacteristic(x!.H) then
      if not IsSchur(x!.H) or ForAll(x!.parts, c->IsERegular(x!.H!.e,c)) then
        return Sum([1..Length(x!.parts)],c->
                 x!.coeffs[c]*Specialized(FindPq(x!.H,x!.parts[c])));
      fi;
    fi;

    d:=Concatenation(x!.H!.HeckeRing,"S");
    mu:=1; specht:=Module(x!.H,"S",0,[]);
    while specht<>fail and mu<=Length(x!.parts) do
      if IsSimpleModule(x!.H,ConjugatePartition(x!.parts[mu])) then
        specht:=specht+Module(x!.H,"S",1,x!.parts[mu]);
      elif EWeight(x!.H!.e,x!.parts[mu])=1 then ## wrap e-hooks onto c
        c:=Module(x!.H,"S",1,ECore(x!.H!.e, x!.parts[mu]))
                 * HeckeOmega(x!.H,"S",x!.H!.e);
        y:=Position(c!.parts, x!.parts[mu]);
        specht:=specht+Module(x!.H,"S",[1,1],
                                        [c!.parts[y-1],c!.parts[y]]);
      #### elif IsBound(H.operations.P.(d)) then ## FIXME not needed anymore?
      ####   specht:=specht+x.H.operations.P.(d)(x.parts[mu]);
      else specht:=fail;
      fi;
      mu:=mu+1;
    od;
    if specht<>fail then return specht;
    elif not silent then
      Print("# P(<x>), unable to rewrite <x> as a sum of specht modules\n");
    fi;
    return fail;
  end
);

InstallMethod(MakeSpechtOp,"P[q]()->S[q]()",[IsDecompositionMatrix,IsHeckePIM],
  function(d,x) local S, nx, y, r, c;
    if x=fail or x=0*x then return x; fi;
    if IsCrystalDecompositionMatrix(d) then S:="Sq"; else S:="S"; fi;

    nx:=Module(x!.H,S,0,[]);
    for y in [1..Length(x!.parts)] do
      c:=Position(d!.cols,x!.parts[y]);
      if c=fail or not IsBound(d!.d[c]) then return fail; fi;
      nx:=nx+Module(x!.H,S,x!.coeffs[y]*d!.d[c].coeffs,
                              List(d!.d[c].parts,r->d!.rows[r]));
    od;
    return nx;
  end
);

InstallMethod(MakePIMOp,"P()->P()",[IsHeckePIM,IsBool],
  function(x,silent) return x; end
);

InstallMethod(MakePIMOp,"P[q]()->P[q]()",[IsDecompositionMatrix,IsHeckePIM],
  function(d,x) return x; end
);

InstallMethod(MakeSimpleOp,"P()->D()",[IsHeckePIM,IsBool],
    function(x,silent)
      x:=MakeSpechtOp(x,silent);
      if x=fail then return x;
      else return MakeSimpleOp(x,silent);
      fi;
    end
);

InstallMethod(MakeSimpleOp,"P[q]()->D[q]()",[IsDecompositionMatrix,IsHeckePIM],
  function(d,x)
    x:=MakeSpechtOp(d,x);
    if x=fail then return x;
    else return MakeSimpleOp(d,x);
    fi;
  end
);

#F Writes D(mu) as a sum of S(nu)'s if possible. We first check to see
## if the decomposition matrix for Sum(mu) is stored in the library, and
## then try to calculate it directly. If we are unable to do this either
## we return fail.
InstallMethod(MakeSpechtOp,"D()->S()",[IsHeckeSimple,IsBool],
  function(x,silent) local c, d, y, a;
    if x=fail or x=0*x then return x;
    elif x!.parts=[[]] then return Module(x!.H,"S",x!.coeffs[1],[]);
    fi;

    ## look for the decomposition matrix
    d:=KnownDecompositionMatrix(x!.H,Sum(x!.parts[1]));
    if d<>fail then
      y:=MakeSpechtOp(d,x);
      if y<>fail then return y; fi;
    fi;

    ## since that didn't work, we use the LLT algorithm when IsBound(H.Pq)
    if IsZeroCharacteristic(x!.H) and not IsSchur(x!.H) then
      return Sum([1..Length(x!.parts)],
                   c->x!.coeffs[c]*Specialized(FindDq(x!.H,x!.parts[c])));
    fi;

    #### ## Next, see if we can calculate it. ## FIXME not needed anymore?
    #### d:=Concatenation(H.HeckeRing, "S");
    #### if IsBound(H.operations.D.(d)) then
    ####  return H.operations.D.(d)(x,silent);
    #### fi;

    ## Finally, hope only e-weights<2 are involved.
    c:=1; d:=true; y:=Module(x!.H,"S",0,[]);
    while d and c<=Length(x!.parts) do
      if IsSimpleModule(x!.H, x!.parts[c]) then
        y:=y+Module(x!.H,"S",x!.coeffs[c],x!.parts[c]);
      elif IsERegular(x!.H!.e, x!.parts[c]) and EWeight(x!.H!.e,x!.parts[c])=1
      then ## wrap e-hooks onto c
        a:=Module(x!.H,"S",1,ECore(x!.H!.e,x!.parts[c]))
             * HeckeOmega(x!.H,"S",x!.H!.e);
        a!.parts:=a!.parts{[1..Position(a!.parts, x!.parts[c])]};
        a!.coeffs:=a!.coeffs{[1..Length(a!.parts)]}*(-1)^(1+Length(a!.parts));
        y:=y+a;
      else d:=fail;
      fi;
      c:=c+1;
    od;
    if d<>fail then return y;
    elif not silent then
      Print("# Unable to calculate D(mu)\n");
    fi;
    return fail;
  end
);

InstallMethod(MakeSpechtOp,"D[q]()->S[q]()",[IsDecompositionMatrix,IsHeckeSimple],
  function(d,x) local S, nx, y, c, inv;
    if x=fail or x=0*x then return x; fi;
    if IsCrystalDecompositionMatrix(d) then S:="Sq"; else S:="S"; fi;

    nx:=Module(x!.H,S,0,[]);
    for y in [1..Length(x!.parts)] do
      c:=Position(d!.cols,x!.parts[y]);
      if c=fail then return fail; fi;
      inv:=Invert(d,x!.parts[y]);
      if inv=fail then return inv;
      else nx:=nx+x!.coeffs[y]*inv;
      fi;
    od;
    return nx;
  end
);

InstallMethod(MakePIMOp,"D()->P()",[IsHeckeSimple,IsBool],
  function(x,silent)
      x:=MakeSpechtOp(x,silent);
      if x=fail then return x;
      else return MakePIMOp(x,silent);
      fi;
    end
);

InstallMethod(MakePIMOp,"D[q]()->P[q]()",[IsDecompositionMatrix,IsHeckeSimple],
  function(d,x)
    x:=MakeSimpleOp(d,x);
    if x=fail then return x;
    else return MakePIMOp(d,x);
    fi;
  end
);

InstallMethod(MakeSimpleOp,"D()->D()",[IsHeckeSimple,IsBool],
  function(x,silent) return x; end
);

InstallMethod(MakeSimpleOp,"D[q]()->D[q]()",[IsDecompositionMatrix,IsHeckeSimple],
  function(d,x) return x; end
);

## Finally, change the various conversion functions X()->Y();
## in fact, we only have to change the four non-trivial ones:
##   P() <-> S() <-> D().

InstallMethod(MakePIMOp,"Sq()->Pq()",[IsFockSpecht,IsBool],
  function(x,silent) local proj;
    if x=fail or x=0*x then return x;
    elif x!.parts=[[]] then return Module(x!.H,"Pq",x!.coeffs[1],[]);
    fi;

    proj:=Module(x!.H,"Pq",0,[]);
    while x<>0*x and PositiveCoefficients(x) do
      proj:=proj+Module(x!.H,"Pq",x!.coeffs[Length(x!.parts)],
                                       x!.parts[Length(x!.parts)]);
      x:=x-x!.coeffs[Length(x!.parts)]*FindPq(x!.H,x!.parts[Length(x!.parts)]);
    od;
    if x=0*x then return proj;
    elif not silent then
      Print("# P(<x>), unable to rewrite <x> as a sum of projectives\n");
    fi;
    return fail;
  end
);

InstallMethod(MakeSimpleOp,"Sq()->Dq()",[IsFockSpecht,IsBool],
  function(x,silent) local mu;
    if x=fail or x=0*x then return x;
    elif x!.parts=[[]] then return Module(x!.H,"Dq",x!.coeffs[1],[]);
    fi;
    return Sum([1..Length(x!.parts)],
      mu->x!.coeffs[mu]*FindSq(x!.H,x!.parts[mu]) );
  end
);

InstallMethod(MakeSpechtOp,"Pq()->Sq()",[IsFockPIM,IsBool],
  function(x,silent) local mu;
    if x=fail or x=0*x then return x;
    elif x!.parts=[[]] then return Module(x!.H,"Sq",x!.coeffs[1],[]);
    fi;

    return Sum([1..Length(x!.parts)],
      mu->x!.coeffs[mu]*FindPq(x!.H,x!.parts[mu]) );
  end
);

InstallMethod(MakeSpechtOp,"Dq()->Sq()",[IsFockPIM,IsBool],
  function(x,silent) local mu;
    if x=fail or x=0*x then return x;
    elif x!.parts=[[]] then return Module(x!.H,"Sq",x!.coeffs[1],[]);
    fi;

    return Sum([1..Length(x!.coeffs)],
      mu->x!.coeffs[mu]*FindDq(x!.H,x!.parts[mu]) );
  end
);

## Make<module> now also plays the role of H.<module> functions
InstallMethod(MakeSpechtOp,"H.S(mu)",[IsAlgebraObj,IsList],
  function(H,mu) local z;
    if mu = [] then return Module(H,"S",1,[]);
    else
      if not ForAll(mu,z->IsInt(z)) then return fail; fi;
      z:=StructuralCopy(mu);
      Sort(mu, function(a,b) return a>b;end); # non-increasing
      if mu<>z then
        Print("## S(mu), warning <mu> is not a partition.\n");
      fi;
      if Length(mu)>0 and mu[Length(mu)]<0 then
        Error("## S(mu): <mu> contains negative parts.\n");
      fi;
      z:=Position(mu,0);
      if z<>fail then mu:=mu{[1..z-1]}; fi;  ## remove any zeros from mu
    fi;
    return Module(H,"S", 1, mu);
  end
);

InstallMethod(MakeSpechtOp,"H.S(d,mu)",[IsDecompositionMatrix,IsList],
  function(d,mu) local z;
    if mu = [] then return Module(d!.H,"S",1,[]);
    else
      if not ForAll(mu,z->IsInt(z)) then return fail; fi;
      z:=StructuralCopy(mu);
      Sort(mu, function(a,b) return a>b;end); # non-increasing
      if mu<>z then
        Print("## S(mu), warning <mu> is not a partition.\n");
      fi;
      if Length(mu)>0 and mu[Length(mu)]<0 then
        Error("## S(mu): <mu> contains negative parts.\n");
      fi;
      z:=Position(mu,0);
      if z<>fail then mu:=mu{[1..z-1]}; fi;  ## remove any zeros from mu
    fi;
    return MakeSimpleOp(d,Module(d!.H,"S", 1, mu));
  end
);

InstallMethod(MakePIMOp,"H.P(mu)",[IsAlgebraObj,IsList],
  function(H,mu) local z;
    if mu = [] then return Module(H,"P",1,[]);
    else
      if not ForAll(mu,z->IsInt(z)) then return fail; fi;
      z:=StructuralCopy(mu);
      Sort(mu, function(a,b) return a>b;end); # non-increasing
      if mu<>z then
        Print("## P(mu), warning <mu> is not a partition.\n");
      fi;
      if Length(mu)>0 and mu[Length(mu)]<0 then
        Error("## P(mu): <mu> contains negative parts.\n");
      fi;
      z:=Position(mu,0);
      if z<>fail then mu:=mu{[1..z-1]}; fi;  ## remove any zeros from mu
    fi;
    return Module(H,"P", 1, mu);
  end
);

InstallMethod(MakePIMOp,"H.P(d,mu)",[IsDecompositionMatrix,IsList],
  function(d,mu) local z;
    if mu = [] then return Module(d!.H,"P",1,[]);
    else
      if not ForAll(mu,z->IsInt(z)) then return fail; fi;
      z:=StructuralCopy(mu);
      Sort(mu, function(a,b) return a>b;end); # non-increasing
      if mu<>z then
        Print("## P(mu), warning <mu> is not a partition.\n");
      fi;
      if Length(mu)>0 and mu[Length(mu)]<0 then
        Error("## P(mu): <mu> contains negative parts.\n");
      fi;
      z:=Position(mu,0);
      if z<>fail then mu:=mu{[1..z-1]}; fi;  ## remove any zeros from mu
    fi;
    if not IsSchur(d!.H) and not IsERegular(d!.H!.e,mu) then
      Error("P(mu): <mu>=[",TightStringList(mu),
              "] must be ", d!.H!.e,"-regular\n\n");
    fi;
    return MakeSpechtOp(d,Module(d!.H,"P", 1, mu));
  end
);

InstallMethod(MakeSimpleOp,"H.D(mu)",[IsAlgebraObj,IsList],
  function(H,mu) local z;
    if mu = [] then return Module(H,"D",1,[]);
    else
      if not ForAll(mu,z->IsInt(z)) then return fail; fi;
      z:=StructuralCopy(mu);
      Sort(mu, function(a,b) return a>b;end); # non-increasing
      if mu<>z then
        Print("## D(mu), warning <mu> is not a partition.\n");
      fi;
      if Length(mu)>0 and mu[Length(mu)]<0 then
        Error("## D(mu): <mu> contains negative parts.\n");
      fi;
      z:=Position(mu,0);
      if z<>fail then mu:=mu{[1..z-1]}; fi;  ## remove any zeros from mu
    fi;
    if not IsSchur(H) and not IsERegular(H!.e,mu) then
      Error("D(mu): <mu>=[",TightStringList(mu),
              "] must be ", H!.e,"-regular\n\n");
    fi;
    return Module(H,"D", 1, mu);
  end
);

InstallMethod(MakeSimpleOp,"H.D(d,mu)",[IsDecompositionMatrix,IsList],
  function(d,mu) local z;
    if mu = [] then return Module(d!.H,"D",1,[]);
    else
      if not ForAll(mu,z->IsInt(z)) then return fail; fi;
      z:=StructuralCopy(mu);
      Sort(mu, function(a,b) return a>b;end); # non-increasing
      if mu<>z then
        Print("## D(mu), warning <mu> is not a partition.\n");
      fi;
      if Length(mu)>0 and mu[Length(mu)]<0 then
        Error("## D(mu): <mu> contains negative parts.\n");
      fi;
      z:=Position(mu,0);
      if z<>fail then mu:=mu{[1..z-1]}; fi;  ## remove any zeros from mu
    fi;
    if not IsSchur(d!.H) and not IsERegular(d!.H!.e,mu) then
      Error("D(mu): <mu>=[",TightStringList(mu),
              "] must be ", d!.H!.e,"-regular\n\n");
    fi;
    return MakeSpechtOp(d,Module(d!.H,"D", 1, mu));
  end
);

InstallMethod(MakeSpechtOp,"H.S(x)",[IsAlgebraObjModule],
  function(x) return MakeSpechtOp(x,false); end
);

InstallMethod(MakePIMOp,"H.P(x)",[IsAlgebraObjModule],
  function(x) return MakePIMOp(x,false); end
);

InstallMethod(MakeSimpleOp,"H.D(x)",[IsAlgebraObjModule],
  function(x) return MakeSimpleOp(x,false); end
);

#a lazy helper
InstallMethod(MakePIMSpechtOp,"mu->P(mu)->S(mu)",[IsDecompositionMatrix,IsList],
  function(d,mu)
    return MakeSpechtOp(d,Module(d!.H,"P",1,mu));
  end
);

InstallMethod(MakeFockSpechtOp,"H.Sq(mu)",[IsAlgebraObj,IsList],
  function(H,mu) local z;
    if not ForAll(mu,z->IsInt(z)) then
      Error("usage: H.Sq(<mu1,mu2,...>)\n");
    fi;
    z:=StructuralCopy(mu);
    Sort(mu, function(a,b) return a>b;end); # non-increasing
    if mu<>z then
      Print("## Sq(mu), warning <mu> is not a partition.\n");
    fi;
    if Length(mu)>0 and mu[Length(mu)]<0 then
      Error("## B Sq(mu): <mu> contains negative parts.\n");
    fi;
    z:=Position(mu,0);
    if z<>fail then mu:=mu{[1..z-1]}; fi;  ## remove any zeros from mu
    return Module(H,"Sq",1,mu);
  end
);

InstallMethod(MakeFockPIMOp,"H.Pq(mu)",[IsAlgebraObj,IsList],
  function(H,mu) local z;
    if not ForAll(mu,z->IsInt(z)) then
      Error("usage: H.Pq(<mu1,mu2,...>)\n");
    fi;
    z:=StructuralCopy(mu);
    Sort(mu, function(a,b) return a>b;end); # non-increasing
    if mu<>z then
      Print("## Pq(mu), warning <mu> is not a partition.\n");
    fi;
    if Length(mu)>0 and mu[Length(mu)]<0 then
      Error("## B Pq(mu): <mu> contains negative parts.\n");
    fi;
    z:=Position(mu,0);
    if z<>fail then mu:=mu{[1..z-1]}; fi;  ## remove any zeros from mu
    if not IsERegular(H!.e,mu) then
          Error("Pq(mu): <mu>=[",TightStringList(mu),
                "] must be ", H!.e,"-regular\n\n");
    else return FindPq(H,mu);
    fi;
  end
);

## ARITHMETICS #################################################################
InstallMethod(\=,"compare modules",[IsAlgebraObjModule,IsAlgebraObjModule],
  function(a,b) return a!.H=b!.H and a!.module=b!.module
    and Length(a!.parts)=Length(b!.parts) and Length(a!.coeffs)=Length(b!.coeffs)
    and ForAll(Zip(a!.coeffs,a!.parts),a->a in Zip(b!.coeffs,b!.parts)); end
);

InstallMethod(\+,"add modules",[IsAlgebraObjModule,IsAlgebraObjModule],
  function(a,b)
    local i, j, ab, x;

    if a=fail or b=fail then return fail;
    elif a=0*a then return b;
    elif b=0*b then return a;
    elif a!.H<>b!.H then
      Error("modules belong to different Grothendieck rings");
    fi;

    if a!.module<>b!.module then # only convert to Specht modules if different
      if Length(a!.module) <> Length(b!.module) then
        Error("AddModule(<a>,<b>): can only add modules of same type.");
      fi;
      a:=MakeSpechtOp(a,false);
      b:=MakeSpechtOp(b,false);
      if a=fail or b=fail then return fail; fi;
    fi;

    ## profiling shows _convincingly_ that the method used below to add
    ## a and b is faster than using SortParallel or H.operations.Collect.
    ab:=[[],[]];
    i:=1; j:=1;
    while i <=Length(a!.parts) and j <=Length(b!.parts) do
      if a!.parts[i]=b!.parts[j] then
        x:=a!.coeffs[i]+b!.coeffs[j];
        if x<>0*x then
          Add(ab[1],x);
          Add(ab[2], a!.parts[i]);
        fi;
        i:=i+1; j:=j+1;
      elif a!.parts[i] < b!.parts[j] then
        if a!.coeffs[i]<>0*a!.coeffs[i] then
          Add(ab[1], a!.coeffs[i]);
          Add(ab[2], a!.parts[i]);
        fi;
        i:=i+1;
      else
        if b!.coeffs[j]<>0*b!.coeffs[j] then
          Add(ab[1], b!.coeffs[j]);
          Add(ab[2], b!.parts[j]);
        fi;
        j:=j+1;
      fi;
    od;
    if i <=Length(a!.parts) then
      Append(ab[1], a!.coeffs{[i..Length(a!.coeffs)]});
      Append(ab[2], a!.parts{[i..Length(a!.parts)]});
    elif j <=Length(b!.parts) then
      Append(ab[1], b!.coeffs{[j..Length(b!.coeffs)]});
      Append(ab[2], b!.parts{[j..Length(b!.parts)]});
    fi;
    if ab=[[],[]] then ab:=[ [0],[[]] ]; fi;
    return Module(a!.H, a!.module, ab[1], ab[2]);
  end
); # AddModules

InstallMethod(\-,[IsAlgebraObjModule,IsAlgebraObjModule],
  function(a,b)
    if a=fail or b=fail then return fail;
    else
      b:=Module(b!.H, b!.module, -b!.coeffs, b!.parts);
      return a+b;
    fi;
  end
); # SubModules

InstallMethod(\*,"multiply module by scalar",[IsScalar,IsAlgebraObjModule],
  function(n,b)
    if n = 0
    then return Module(b!.H, b!.module, 0, []);
    else return Module(b!.H, b!.module, n*b!.coeffs, b!.parts);
    fi;
  end
);

InstallMethod(\*,"multiply module by scalar",[IsAlgebraObjModule,IsScalar],
  function(a,n)
    if n = 0
    then return Module(a!.H, a!.module, 0, []);
    else return Module(a!.H, a!.module, n*a!.coeffs, a!.parts);
    fi;
  end
);

InstallMethod(\*,"multiply specht modules",[IsHeckeSpecht,IsHeckeSpecht],
  function(a,b) local x, y, ab, abcoeff, xy, z;
    if a=fail or b=fail then return fail;
    elif a!.H<>b!.H then
      Error("modules belong to different Grothendieck rings");
    fi;
    #a:=MakeSpechtOp(a,false);
    ab:=[[],[]];
    for x in [1..Length(a!.parts)] do
      for y in [1..Length(b!.parts)] do
        abcoeff:=a!.coeffs[x]*b!.coeffs[y];
        if abcoeff<>0*abcoeff then
          z:=LittlewoodRichardsonRule(a!.parts[x], b!.parts[y]);
          Append(ab[1], List(z, xy->abcoeff));
          Append(ab[2], z);
        fi;
      od;
    od;
    if ab=[] then return Module(a!.H, a!.module, 0, []);
    else return Collect(b!.H, b!.module, ab[1], ab[2]);
    fi;
  end
);

InstallMethod(\*,"multiply projective indecomposable modules",[IsHeckePIM,IsHeckePIM],
  function(a,b) local x, nx;
    x:=MakeSpechtOp(a,false) * MakeSpechtOp(b,false);
    nx:=MakePIMOp(x,true);
    if nx<>fail then return nx; else return x; fi;
  end
);

InstallMethod(\*,"multiply simple modules",[IsHeckeSimple,IsHeckeSimple],
  function(a,b) local x, nx;
    x:=MakeSpechtOp(a,false) * MakeSpechtOp(b,false);
    nx:=MakeSimpleOp(x,true);
    if nx<>fail then return nx; else return x; fi;
  end
);

InstallMethod(\*,"multiply modules",[IsAlgebraObjModule,IsAlgebraObjModule],
  function(a,b)
    return MakeSpechtOp(a,false) * MakeSpechtOp(b,false);
  end
); # MulModules

InstallMethod(\/,"divide module by scalar",[IsAlgebraObjModule,IsScalar],
  function(b,n) local x;
    if n=0 then Error("can't divide by 0!\n");
    else return Module(b!.H, b!.module, b!.coeffs/n, b!.parts);
    fi;
  end
); # DivModules

################################################################################

InstallMethod(InnerProduct,"inner product of modules",
  [IsAlgebraObjModule,IsAlgebraObjModule],
  function(a,b) local pr, x, y;
    if a=0*a or b=0*b then return 0;
    elif a!.module<>b!.module then
      a:=MakeSpechtOp(a,true);
      b:=MakeSpechtOp(b,true);
    fi;

    pr:=0; x:=1; y:=1;  # use the fact that a.parts and b.parts are ordered
    while x <=Length(a!.parts) and y <=Length(b!.parts) do
      if a!.parts[x]=b!.parts[y] then
        pr:=pr + a!.coeffs[x]*b!.coeffs[y];
        x:=x + 1; y:=y + 1;
      elif a!.parts[x]<b!.parts[y] then x:=x + 1;
      else y:=y + 1;
      fi;
    od;
    return pr;
  end
);  # InnerProduct

#F Returns the Coefficient of p in x
InstallMethod(CoefficientOp, "extract coefficient of a partition from module",
  [IsAlgebraObjModule,IsList],
  function(x,p) local pos;
    pos:=Position(x!.parts, p);
    if pos=fail then return 0;
    else return x!.coeffs[pos];
    fi;
  end
);  # Coefficient

#F Returns true if all coefficients are non-negative
InstallMethod(PositiveCoefficients, "test if all coefficients are non-negative",
  [IsAlgebraObjModule],
  function(x) local c;
    return ForAll(x!.coeffs, c->c>=0);
  end
);

InstallMethod(PositiveCoefficients,
  "test if all coefficients of a Fock module are non-negative",
  [IsFockModule],
  function(x)
    return ForAll(x!.coeffs, p->( IsInt(p) and p>=0 ) or
                      ForAll(CoefficientsOfLaurentPolynomial(p)[1], c->c>=0) );
  end
); # PositiveCoefficients

#F Returns true if all coefficients are integral
InstallMethod(IntegralCoefficients, "test if all coefficients are integral",
  [IsAlgebraObjModule],
  function(x) local c;
    return ForAll(x!.coeffs, c->IsInt(c));
  end
);

InstallMethod(IntegralCoefficients,
  "test if all coefficients of a Fock module are non-negative",
  [IsFockModule],
  function(x)
    return ForAll(x!.coeffs, p-> ( IsInt(p) and p>=0 ) or
                  ForAll(CoefficientsOfLaurentPolynomial(p)[1], c->IsInt(c)) );
  end
); # IntegralCoefficients

## INDUCTION AND RESTRICTION ###################################################

## The next functions are for restricting and inducing Specht
## modules. They all assume that their arguments are indeed Specht
## modules; conversations are done in H.operations.X.Y() as necessary.

## r-induction: on Specht modules:
InstallMethod(RInducedModuleOp, "r-induction on specht modules",
  [IsAlgebraObj,IsHeckeSpecht,IsInt,IsInt],
  function(H, a, e, r) local ind, x, i, j, np;
    ind:=[[],[]];
    for x in [1..Length(a!.parts)] do
      for i in [1..Length(a!.parts[x])] do
        if (a!.parts[x][i] + 1 - i) mod e=r then
          if i=1 or a!.parts[x][i-1] > a!.parts[x][i] then
            np:=StructuralCopy(a!.parts[x]);
            np[i]:=np[i]+1;
            Add(ind[1], a!.coeffs[x]);
            Add(ind[2], np);
          fi;
        fi;
      od;
      if ( -Length(a!.parts[x]) mod e)=r then
        np:=StructuralCopy(a!.parts[x]); Add(np, 1);
        Add(ind[1],a!.coeffs[x]);
        Add(ind[2],np);
      fi;
    od;
    if ind=[ [],[] ] then return Module(H,"S",0,[]);
    else return Collect(H,"S", ind[1], ind[2]);
    fi;
  end
); # RInducedModule

## String-induction: add s r's from each partition in x (ignoring
## multiplicities). Does both standard and q-induction.

## We look at the size of x.module to decide whether we want to use
## ordinary induction or q-induction (in the Fock space). We could
## write H.operations.X.SInduce to as to make this choice for us, or
## do q-induction always, setting v=1 afterwards, but this seems the
## better choice.
InstallMethod(SInducedModuleOp,"string-induction on specht modules",
  [IsAlgebraObj,IsHeckeSpecht,IsInt,IsInt,IsInt],
  function(H, x, e, s, r) local coeffs, parts, y, z, sinduced;
    # add n nodes of residue r to the partition y from the i-th row down
    sinduced:=function(y, n, e, r, i) local ny, j, z;
      ny:=[];
      for j in [i..Length(y)-n+1] do
        if r=(y[j] - j + 1) mod e then
          if j=1 or y[j] < y[j-1] then
            z:=StructuralCopy(y);
            z[j]:=z[j] + 1; # only one node of residue r can be added
            if n=1 then Add(ny, z);   # no more nodes to add
            else Append(ny, sinduced(z, n-1, e, r, j+1));
            fi;
          fi;
        fi;
      od;
      return ny;
    end;

    if s=0 then return Module(x!.H,x!.module,1,[]); fi;
    coeffs:=[]; parts:=[];
    for y in [1..Length(x!.parts)] do
      Append(parts,sinduced(x!.parts[y], s, e, r, 1));
      Append(coeffs,List([1..Length(parts)-Length(coeffs)],r->x!.coeffs[y]));
      if r=( -Length(x!.parts[y]) mod e) then # add a node to the bottom
        z:=StructuralCopy(x!.parts[y]);
        Add(z,1);
        if s > 1 then                        # need to add some more nodes
          Append(parts,sinduced(z, s-1, e, r, 1));
          Append(coeffs,List([1..Length(parts)-Length(coeffs)],
                             r->x!.coeffs[y]));
        else Add(coeffs, x!.coeffs[y]); Add(parts, z);
        fi;
      fi;
    od;

    if coeffs=[] then return Module(H, x!.module,0,[]);
    else return Collect(H, x!.module, coeffs, parts);
    fi;
  end
);  # SInducedModule

## r-restriction
InstallMethod(RRestrictedModuleOp,"r-restriction on specht modules",
  [IsAlgebraObj,IsHeckeSpecht,IsInt,IsInt],
  function(H, a, e, r) local ind, x, i, j, np;
    ind:=[[],[]];
    for x in [1..Length(a!.parts)] do
      for i in [1..Length(a!.parts[x])] do
        if (a!.parts[x][i] - i) mod e=r then
          np:=StructuralCopy(a!.parts[x]);
          if i=Length(a!.parts[x]) or np[i] > np[i+1] then
            np[i]:=np[i] - 1;
            if np[i]=0 then Unbind(np[i]); fi;
            Add(ind[1], a!.coeffs[x]); Add(ind[2], np);
          fi;
        fi;
      od;
    od;
    if ind=[ [],[] ] then return Module(H,"S",0,[]);
    else return Collect(H,"S", ind[1], ind[2]);
    fi;
  end
); #RRestrictedModule

## string-restriction: remove m r's from each partition in x
InstallMethod(SRestrictedModuleOp,"string-restriction on specht modules",
  [IsAlgebraObj,IsHeckeSpecht,IsInt,IsInt,IsInt],
  function(H,x,e,s,r) local coeffs, parts, y, i, srestricted;
    ## remove n nodes from y from the ith row down
    srestricted:=function(y, n, e, r, i) local ny, j, z;
      ny:=[];
      for j in [i..Length(y)-n+1] do
        if r=(y[j] - j) mod e then
          if j=Length(y) or y[j] > y[j+1] then
            z:=StructuralCopy(y);
            z[j]:=z[j] - 1;
            if z[j]=0 then   # n must be 1
              Unbind(z[j]);
              Add(ny, z);
            elif n=1 then Add(ny, z); # no mode nodes to remove
            else Append(ny, srestricted(z, n-1, e, r, j+1));
            fi;
          fi;
        fi;
      od;
      return ny;
    end;

    coeffs:=[]; parts:=[];
    for y in [1..Length(x!.parts)] do
      Append(parts, srestricted(x!.parts[y], s, e, r, 1));
      Append(coeffs,List([1..Length(parts)-Length(coeffs)],i->x!.coeffs[y]));
    od;
    if parts=[] then return Module(H,"S",0,[]);
    else return Collect(H,"S", coeffs, parts);
    fi;
  end
);  # SRestrictedModule

## Induction and restriction; for S()
InstallMethod(RInducedModuleOp, "r-induction for specht modules",
  [IsAlgebraObj, IsHeckeSpecht, IsList],
  function(H, x, list) local r;
    if x=fail or x=0*x then return x;
    elif list=[] then return RInducedModule(H,x,1,0);
    elif H!.e=0 then
      Error("Induce, r-induction is not defined when e=0.");
    elif ForAny(list,r-> r>=H!.e or r<0) then
      Error("Induce, r-induction is defined only when 0<=r<e.\n");
    else
      for r in list do
        x:=RInducedModule(H,x,H!.e,r);
      od;
      return x;
    fi;
  end
);

InstallMethod(RInducedModuleOp, "r-induction for projective indecomposable modules",
  [IsAlgebraObj, IsHeckePIM, IsList],
  function(H, x, list) local nx;
    x:=RInducedModule(H,MakeSpechtOp(x,false),list);
    if x=fail or x=0*x then return x; fi;
    nx:=MakePIMOp(x,false);
    if nx<>fail then return nx; else return x; fi;
  end
);

InstallMethod(RInducedModuleOp, "r-induction for simple modules",
  [IsAlgebraObj, IsHeckeSimple, IsList],
  function(H, x, list) local nx;
    x:=RInducedModule(H,MakeSpechtOp(x,false),list);
    if x=fail or x=0*x then return x; fi;
    nx:=MakeSimpleOp(x,false);
    if nx<>fail then return nx; else return x; fi;
  end
);

InstallMethod(RInducedModuleOp, "r-induction for Fock space specht modules",
  [IsAlgebraObj,IsFockSpecht,IsList],
  function(H, x, list) local r;
    if list=[] then return Sum([0..H!.e-1],r->qSInducedModule(H,x,1,r));
    elif H!.e=0 then
      Error("Induce, r-induction is not defined when e=0.");
    elif ForAny(list,r-> r>=H!.e or r<0) then
      Error("Induce, r-induction is defined only when 0<=r<e.\n");
    else
      for r in list do   ## we could do slightly better here
        x:= qSInducedModule(H,x,1,r);
      od;
      return x;
    fi;
  end
);

InstallMethod(RInducedModuleOp,
  "r-induction for Fock space projective indecomposable modules",
  [IsAlgebraObj,IsFockPIM,IsList],
  function(H,x,list)
    return MakePIMOp(RInducedModule(H,MakeSpechtOp(x,false),list),false);
  end
);

InstallMethod(RInducedModuleOp,
  "r-induction for Fock space simple modules",
  [IsAlgebraObj,IsFockSimple,IsList],
  function(H,x,list)
    return MakeSimpleOp(RInducedModule(H,MakeSpechtOp(x,false),list),false);
  end
); # RInducedModule

InstallMethod(RRestrictedModuleOp, "r-restriction for specht modules",
  [IsAlgebraObj, IsHeckeSpecht, IsList],
  function(H, x, list) local r;
    if x=fail or x=0*x then return x;
    elif list=[] then return RRestrictedModule(H,x,1,0);
    elif H!.e=0 then
      Error("Restrict, r-restriction is not defined when e=0.");
   elif ForAny(list,r-> r>=H!.e or r<0) then
      Error("Restrict, r-restriction is defined only when 0<=r<e.\n");
    else
      for r in list do
        x:=RRestrictedModule(H,x,H!.e,r);
      od;
      return x;
    fi;
  end
);

InstallMethod(RRestrictedModuleOp, "r-restriction for projective indecomposable modules",
  [IsAlgebraObj, IsHeckePIM, IsList],
  function(H, x, list) local nx;
    x:=RRestrictedModule(H,MakeSpechtOp(x,false),list);
    if x=fail or x=0*x then return x; fi;
    nx:=MakePIMOp(x,false);
    if nx<>fail then return nx; else return x; fi;
  end
);

InstallMethod(RRestrictedModuleOp, "r-restriction for simple modules",
  [IsAlgebraObj, IsHeckeSimple, IsList],
  function(H, x, list) local nx;
    x:=RRestrictedModule(H,MakeSpechtOp(x,false),list);
    if x=fail or x=0*x then return x; fi;
    nx:=MakeSimpleOp(x,false);
    if nx<>fail then return nx; else return x; fi;
  end
);

InstallMethod(RRestrictedModuleOp, "r-restriction for Fock space specht modules",
  [IsAlgebraObj,IsFockSpecht,IsList],
  function(H, x, list) local r;
    if list=[] then return Sum([0..H!.e-1],r->qSRestrictedModule(H,x,1,r));
    elif H!.e=0 then
      Error("Restrict, r-restriction is not defined when e=0.");
    elif ForAny(list,r-> r>=H!.e or r<0) then
      Error("Restrict, r-restriction is defined only when 0<=r<e.\n");
    else
      for r in list do   ## we could do slightly better here
        x:= qSRestrictedModule(H,x,1,r);
      od;
      return x;
    fi;
  end
);

InstallMethod(RRestrictedModuleOp,
  "r-restriction for Fock space projective indecomposable modules",
  [IsAlgebraObj,IsFockPIM,IsList],
  function(H,x,list)
    return MakePIMOp(RRestrictedModule(H,MakeSpechtOp(x,false),list),false);
  end
);

InstallMethod(RRestrictedModuleOp,
  "r-restriction for Fock space simple modules",
  [IsAlgebraObj,IsFockSimple,IsList],
  function(H,x,list)
    return MakeSimpleOp(RRestrictedModule(H,MakeSpechtOp(x,false),list),false);
  end
); # RRestrictedModule

InstallMethod(SInducedModuleOp,"string induction for specht modules",
  [IsAlgebraObj, IsHeckeSpecht, IsList],
  function(H, x, list) local r;
    if x=fail or x=0*x then return x;
    elif Length(list)=1 then
      list:=list[1];
      if list=0 then return Module(H,"Sq",1,[]); fi;
      while list > 0 do
        x:=SInducedModule(H,x,1,1,0);
        list:=list-1;
      od;
      return x;
    elif H!.e=0 then
      Error("SInduce, string induction is not defined when e=0.");
    elif list[2]>H!.e or list[2]<0 then
      Error("SInduce, string induction is defined only when 0<=r<e.\n");
    else return SInducedModule(H, x, H!.e, list[1], list[2]);
    fi;
  end
);

InstallMethod(SInducedModuleOp, "string induction for projective indecomposable modules",
  [IsAlgebraObj, IsHeckePIM, IsList],
  function(H, x, list) local nx;
    x:=SInducedModule(H,MakeSpechtOp(x,false),list);
    if x=fail or x=0*x then return x; fi;
    nx:=MakePIMOp(x,false);
    if nx<>fail then return nx; else return x; fi;
  end
);

InstallMethod(SInducedModuleOp, "string induction for simple modules",
  [IsAlgebraObj, IsHeckeSimple, IsList],
  function(H, x, list) local nx;
    x:=SInducedModule(H,MakeSpechtOp(x,false),list);
    if x=fail or x=0*x then return x; fi;
    nx:=MakeSimpleOp(x,false);
    if nx<>fail then return nx; else return x; fi;
  end
);

InstallMethod(SInducedModuleOp, "string induction for Fock space specht modules",
  [IsAlgebraObj,IsFockSpecht,IsList],
  function(H, x, list) local r;
    if Length(list)=1 then
      list:=list[1];
      if list=0 then return Module(H,"Sq",1,[]); fi;
      while list > 0 do
        x:=Sum([0..H!.e-1],r->qSInducedModule(H,x,1,r));
        list:=list-1;
      od;
      return x;
    elif H!.e=0 then
      Error("SInduce, string induction is not defined when e=0.");
    elif list[2]>H!.e or list[2]<0 then
      Error("SInduce, string induction is defined only when 0<=r<e.\n");
    else return qSInducedModule(H, x, list[1], list[2]);
    fi;
  end
);

InstallMethod(SInducedModuleOp,
  "string induction for Fock space projective indecomposable modules",
  [IsAlgebraObj,IsFockPIM,IsList],
  function(H,x,list)
    return MakePIMOp(SInducedModule(H,MakeSpechtOp(x,false),list),false);
  end
);

InstallMethod(SInducedModuleOp,
  "string induction for Fock space simple modules",
  [IsAlgebraObj,IsFockSimple,IsList],
  function(H,x,list)
    return MakeSimpleOp(SInducedModule(H,MakeSpechtOp(x,false),list),false);
  end
); # SInducedModule

InstallMethod(SRestrictedModuleOp,"string restriction for specht modules",
  [IsAlgebraObj, IsHeckeSpecht, IsList],
  function(H, x, list) local r;
    if x=fail or x=0*x then return x;
    elif Length(list)=1 then
      list:=list[1];
      if list=0 then return Module(H,"Sq",1,[]); fi;
      while list > 0 do
        x:=SRestrictedModule(H,x,1,1,0);
        list:=list-1;
      od;
      return x;
    elif H!.e=0 then
      Error("SRestrict, r-restriction is not defined when e=0.");
    elif list[2]>H!.e or list[2]<0 then
      Error("SRestrict, r-restriction is defined only when 0<=r<e.\n");
    else return SRestrictedModule(H, x, H!.e, list[1], list[2]);
    fi;
  end
);

InstallMethod(SRestrictedModuleOp, "string restriction for projective indecomposable modules",
  [IsAlgebraObj, IsHeckePIM, IsList],
  function(H, x, list) local nx;
    x:=SRestrictedModule(H,MakeSpechtOp(x,false),list);
    if x=fail or x=0*x then return x; fi;
    nx:=MakePIMOp(x,false);
    if nx<>fail then return nx; else return x; fi;
  end
);

InstallMethod(SRestrictedModuleOp, "string restriction for simple modules",
  [IsAlgebraObj, IsHeckeSimple, IsList],
  function(H, x, list) local nx;
    x:=SRestrictedModule(H,MakeSpechtOp(x,false),list);
    if x=fail or x=0*x then return x; fi;
    nx:=MakeSimpleOp(x,false);
    if nx<>fail then return nx; else return x; fi;
  end
);

InstallMethod(SRestrictedModuleOp, "string restriction for Fock space specht modules",
  [IsAlgebraObj,IsFockSpecht,IsList],
  function(H, x, list) local r;
    if Length(list)=1 then
      list:=list[1];
      if list=0 then return Module(H,"Sq",1,[]); fi;
      while list > 0 do
        x:=Sum([0..H!.e-1],r->qSRestrictedModule(H,x,1,r));
        list:=list-1;
      od;
      return x;
    elif H!.e=0 then
      Error("SRestrict, string restriction is not defined when e=0.");
    elif list[2]>H!.e or list[2]<0 then
      Error("SRestrict, string restriction is defined only when 0<=r<e.\n");
    else return qSRestrictedModule(H, x, list[1], list[2]);
    fi;
  end
);

InstallMethod(SRestrictedModuleOp,
  "string restriction for Fock space projective indecomposable modules",
  [IsAlgebraObj,IsFockPIM,IsList],
  function(H,x,list)
    return MakePIMOp(SRestrictedModule(H,MakeSpechtOp(x,false),list),false);
  end
);

InstallMethod(SRestrictedModuleOp,
  "string restriction for Fock space simple modules",
  [IsAlgebraObj,IsFockSimple,IsList],
  function(H,x,list)
    return MakeSimpleOp(SRestrictedModule(H,MakeSpechtOp(x,false),list),false);
  end
); #SRestrictedModule

InstallMethod(RInducedModuleOp,
  "toplevel r-induction",
  [IsAlgebraObjModule,IsList],
  function(x,list)
    return RInducedModuleOp(x!.H,x,list);
  end
); #RInducedModule

InstallMethod(RRestrictedModuleOp,
  "toplevel r-restriction",
  [IsAlgebraObjModule,IsList],
  function(x,list)
    return RRestrictedModuleOp(x!.H,x,list);
  end
); #RRestrictedModule

InstallMethod(SInducedModuleOp,
  "toplevel string induction",
  [IsAlgebraObjModule,IsList],
  function(x,list)
    return SInducedModuleOp(x!.H,x,list);
  end
); #SInducedModule

InstallMethod(SRestrictedModuleOp,
  "toplevel string restriction",
  [IsAlgebraObjModule,IsList],
  function(x,list)
    return SRestrictedModuleOp(x!.H,x,list);
  end
); #SRestrictedModule

## Q-INDUCTION AND Q-RESTRICTION ###############################################

## notice that we can't pull the trick to induce all residues at once
## that we used in InducedModule() etc. as we have to keep track of the
## number of addable and removable nodes of each residue. Rather than
## do this we just call this function as many times as necessary.
InstallMethod(qSInducedModule,"q-induction for modules",
  [IsAlgebraObj,IsAlgebraObjModule,IsInt,IsInt],
  function(H,x,s,r) local coeffs, parts, y, z, qsinduced, v;
    v:=H!.Indeterminate;

    # add n nodes of residue r to the partition y from the i-th row down
    # here exp is the exponent of the indeterminate
    qsinduced:=function(y, n, r, i, exp) local ny, j, z;
     ny:=[];
     for j in [i..Length(y)-n+1] do
       if y[j]>0 and r=(y[j]-j) mod H!.e and (j=Length(y) or y[j]>y[j+1])
       then exp:=exp-n;               ## removable node of residue r
       elif r=(y[j] - j + 1) mod H!.e then
         if j=1 or y[j] < y[j-1] then ## addable node of residue r
           z:=StructuralCopy(y);
           z[j]:=z[j] + 1; # only one node of residue r can be added
           if n=1 then Add(ny, [exp,z] );   # no more nodes to add
           else Append(ny, qsinduced(z, n-1, r, j+1, exp));
           fi;
           exp:=exp+n;
         fi;
       fi;
     od;
     return ny;
    end;

    coeffs:=[]; parts:=[];
    for y in [1..Length(x!.parts)] do
      if r=( -Length(x!.parts[y]) mod H!.e) then # add a node to the bottom
        z:=StructuralCopy(x!.parts[y]);
        Add(z,0);
        for z in qsinduced(z,s,r,1,0)  do
          if z[1]=0 then Add(coeffs, x!.coeffs[y]);
          else Add(coeffs, x!.coeffs[y]*v^z[1]);
          fi;
          if z[2][Length(z[2])]=0 then Unbind(z[2][Length(z[2])]); fi;
          Add(parts, z[2]);
        od;
      else
        for z in qsinduced(x!.parts[y],s,r,1,0) do
          if z[1]=0 then Add(coeffs, x!.coeffs[y]);
          else Add(coeffs, x!.coeffs[y]*v^z[1]);
          fi;
          Add(parts, z[2]);
        od;
      fi;
    od;

    if coeffs=[] then return Module(H,x!.module,0,[]);
    else return Collect(H,x!.module, coeffs, parts);
    fi;
  end
);  # qSInducedModule

## string-restriction: remove m r's from each partition in x
## ** should allow restricting s-times also
InstallMethod(qSRestrictedModule,"q-restriction for modules",
  [IsAlgebraObj,IsAlgebraObjModule,IsInt,IsInt],
  function(H, x, s, r) local coeffs, parts, z, y, i, e, exp, v, qrestricted;
    v:=H!.Indeterminate;

    qrestricted:=function(y, n, r, i, exp) local ny, j, z;
      ny:=[];
      for j in [i,i-1..n] do
        if y[j]>0 and r=(y[j]+1-j) mod H!.e and (j=1 or y[j]<y[j-1]) then
           exp:=exp-n;                 ## an addable node of residue r
        elif r=(y[j] - j) mod H!.e then   ## removable node of residue r
          if j=Length(y) or y[j] > y[j+1] then
            z:=StructuralCopy(y);
            z[j]:=z[j]-1;
            if z[j]=0 then Unbind(z[j]); fi;
            if n=1 then Add(ny, [exp,z]); # no mode nodes to remove
            else Append(ny, qrestricted(z, n-1, r, j-1, exp));
            fi;
            exp:=exp+n;
          fi;
        fi;
      od;
      return ny;
    end;

    e:=x!.H!.e;
    coeffs:=[]; parts:=[];
    if s=0 then return Module(H,"Sq",1,[]); fi;
    for y in [1..Length(x!.parts)] do
      if -Length(x!.parts[y]) mod H!.e = r then exp:=-s; else exp:=0; fi;
      for z in qrestricted(x!.parts[y], s, r, Length(x!.parts[y]), exp) do
        if z[1]=0 then Add(coeffs, x!.coeffs[y]);
        else Add(coeffs, x!.coeffs[y]*v^z[1]);
        fi;
        Add(parts, z[2]);
      od;
    od;
    if parts=[] then return Module(H,"Sq",0,[]);
    else return Collect(H, "Sq", coeffs, parts);
    fi;
  end
);  # qSRestrict

## DECOMPOSITION MATRICES ######################################################

## The following directory is searched by ReadDecompositionMatrix()
## when it is looking for decomposition matrices. By default, it points
## to the current directory (if set, the current directory is not
## searched).
if not IsBound(SpechtDirectory) then SpechtDirectory:=Directory("."); fi;

## This variable is what is used in the decomposition matrices files saved
## by SaveDecompositionMatrix() (and also the variable which contains them
## when they are read back in).
A_Specht_Decomposition_Matrix:=fail;

## Finally, we can define the creation function for decomposition matrices
## (note that NewDM() does not add the partition labels to the decomp.
## matrix; this used to be done here but now happens in PrintDM() because
## crystallized matrices may never be printed and this operation is
## expensive).
## **NOTE: we assume when extracting entries from d that d.rows is
## ordered lexicographically. If this is not the case then addition
## will not work properly.
InstallOtherMethod(DecompositionMatrix,"creates a new decomposition matrix",
  [IsAlgebraObj,IsList,IsList,IsBool],
  function(H, rows, cols, decompmat) local d;
    d := rec(d:=[],      # matrix entries
       rows:=rows, # matrix rows
       cols:=cols, # matrix cols
       inverse:=[], dimensions:=[], ## inverse matrix and dimensions
       H:=H
    );

    if decompmat then
      return Objectify(DecompositionMatrixType,d);
    else
      return Objectify(CrystalDecompositionMatrixType,d);
    fi;
  end
); # DecompositonMatrix

InstallMethod(\=, "for decomposition matrices",
  [IsDecompositionMatrix,IsDecompositionMatrix],
  function(d1,d2)
    return d1!.d=d2!.d and d1!.cols = d2!.cols and d1!.rows = d2!.rows;
  end
); # Equal matrices

InstallMethod(CopyDecompositionMatrix, "for decomposition matrices",
  [IsDecompositionMatrix],
  function(d) local newd;
    newd:=DecompositionMatrix(
      d!.H, StructuralCopy(d!.rows), StructuralCopy(d!.cols),
      not IsCrystalDecompositionMatrix(d)
      );
    newd!.d:=StructuralCopy(d!.d);
    newd!.inverse:=StructuralCopy(d!.inverse);
    newd!.dimensions:=StructuralCopy(d!.dimensions);
    return newd;
  end
); # CopyDecompositionMatrix

## Used by SaveDecomposition matrix to update CrystalMatrices[]
InstallMethod(Store,"for crystal decomposition matrices",
  [IsCrystalDecompositionMatrix,IsInt],
  function(d,n) d!.H!.CrystalMatrices[n]:=d; end
);

## Used by SaveDecomposition matrix to update DecompositionMatrices[]
InstallMethod(Store,"for decomposition matrices",[IsDecompositionMatrix,IsInt],
  function(d,n) d!.H!.DecompositionMatrices[n]:=d; end
);

## This also needs to be done for crystal matrices (this wasn't
## put in above because normal decomposition matrices don't
## specialise.
InstallMethod(Specialized,"specialise CDM",
  [IsCrystalDecompositionMatrix,IsInt],
  function(d,a) local sd, c, p, coeffs, v;
    v:=d!.H!.Indeterminate;
    sd:=DecompositionMatrix(d!.H,d!.rows,d!.cols,true);
    for c in [1..Length(d!.cols)] do
      if IsBound(d!.d[c]) then
        sd!.d[c]:=rec();
        coeffs:=List(d!.d[c].coeffs*v^0,p->Value(p,a));
        p:=List(coeffs,p->p<>0);
        if true in p then
          sd!.d[c]:=rec(coeffs:=ListBlist(coeffs,p),
                        parts:=ListBlist(d!.d[c].parts,p) );
        else sd!.d[c]:=rec(coeffs:=[0], parts:=[ [] ] );
        fi;
      fi;
      if IsBound(d!.inverse[c]) then
        coeffs:=List(d!.inverse[c].coeffs*v^0,p->Value(p,a));
        p:=List(coeffs,p->p<>0);
        if true in p then
          sd!.inverse[c]:=rec(coeffs:=ListBlist(coeffs,p),
                          parts:=ListBlist(d!.inverse[c].parts,p) );
        else sd!.d[c]:=rec(coeffs:=[0], parts:=[ [] ] );
        fi;
      fi;
    od;
    return sd;
  end
);

InstallMethod(Specialized,"specialise CDM",
  [IsCrystalDecompositionMatrix], a->Specialized(a,1)
);

## Specialization taking Xq -> X
InstallMethod(Specialized,"specialise Fock module",
  [IsFockModule,IsInt],
  function(x,a) local coeffs, c;
    coeffs:=List(x!.coeffs*(x!.H!.Indeterminate)^0,c->Value(c,a));
    c:=List(coeffs,c->c<>0);
    if true in c then return Module(x!.H,x!.module{[1]},
                                ListBlist(coeffs,c),ListBlist(x!.parts,c));
    else return Module(x!.H,x!.module{[1]},0,[]);
    fi;
  end
);

InstallMethod(Specialized,"specialise Fock module",
  [IsFockModule], x->Specialized(x,1)
); # Specialized

## writes D(mu) as a linear combination of S(nu)'s if possible
InstallMethod(Invert, "for a decomposition matrix and a partition",
  [IsDecompositionMatrix,IsList],
  function(d,mu) local c, S, D, inv, smu, l, tmp;
    if IsCrystalDecompositionMatrix(d) then S:="Sq"; D:="Dq";
    else S:="S"; D:="D";
    fi;

    c:=Position(d!.cols,mu);
    if c=fail then return fail;
    elif IsBound(d!.inverse[c]) then
      return Module(d!.H,S,d!.inverse[c].coeffs,
                    List(d!.inverse[c].parts,l->d!.cols[l]));
    fi;

    inv:=Module(d!.H,S,1,mu);
    tmp:=MakeSimpleOp(d,inv);
    if tmp=fail then
      smu:=fail;
    else
      smu:=Module(d!.H,D,1,mu)-tmp;
    fi;

    while smu<>fail and smu<>0*smu do
      inv:=inv+Module(d!.H,S,smu!.coeffs[1],smu!.parts[1]);
      tmp:=MakeSimpleOp(d, Module(d!.H,S,-smu!.coeffs[1],smu!.parts[1]));
      if tmp=fail then
        smu:=fail;
      else
        smu:=smu+tmp;
      fi;
    od;
    if smu=fail then return fail; fi;

    d!.inverse[c]:=rec(coeffs:=inv!.coeffs,
                  parts:=List(inv!.parts,l->Position(d!.cols,l)));
    return inv;
  end
);

#P This function adds the column for Px to the decomposition matrix <d>.
## if <checking>=true then Px is checked against its image under the
## Mullineux map; if this image is not there then we also insert it
## into <d>.
InstallMethod(AddIndecomposable,"fill out entries of decomposition matrix",
  [IsDecompositionMatrix,IsAlgebraObjModule,IsBool],
  function(d,Px,checking) local mPx, r, c;
    c:=Position(d!.cols, Px!.parts[Length(Px!.parts)]);
    if checking then
      ## first look to see if <Px> already exists in <d>
      if IsBound(d!.d[c]) then ## Px already exists
        Print("# AddIndecomposable: overwriting old value of P(",
              TightStringList(Px!.parts[Length(Px!.parts)]),") in <d>\n");
        Unbind(d!.inverse);       # just in case these were bound
        Unbind(d!.dimensions);
      fi;
      ## now looks at the image of <Px> under Mullineux
      if (not IsSchur(Px!.H) and IsERegular(Px!.H!.e,Px!.parts[Length(Px!.parts)]))
      and Px!.parts[Length(Px!.parts)]<>ConjugatePartition(Px!.parts[1]) then
        mPx:=MullineuxMap(Px);
        if IsBound(d!.d[Position(d!.cols,mPx!.parts[Length(Px!.parts)])])
        and MakePIMSpechtOp(d,mPx!.parts[Length(Px!.parts)]) <> mPx then
          Print("# AddIndecomposable(<d>,<Px>), WARNING: P(",
                TightStringList(Px!.parts[Length(Px!.parts)]), ") and P(",
                TightStringList(mPx!.parts[Length(Px!.parts)]),
                ") in <d> are incompatible\n");
        else
          Print("# AddIndecomposable(<d>,<Px>): adding MullineuxMap(<Px>) ",
                "to <d>\n");
          AddIndecomposable(d,mPx,false);
        fi;
      fi;
    fi;      # end of check
    d!.d[c]:=rec(coeffs:=Px!.coeffs,
                parts:=List(Px!.parts,r->Position(d!.rows,r)) );
  end
);

#F This function checks to see whether Px is indecomposable using the
## decomposition matrix d. The basic idea is to loop through all of the
## (e-regular, unless IsSpecht=false) projectives Py in Px such that Px-Py
## has non-negative coefficients and to then apply the q-Schaper theorem
## and induce simples, together with a few other tricks to decide
## whether ir not Py slits off. If yes, then we move on; if we can't
## decide (or don't know the value of Py), we spit the dummy and return
## false, printing a "message from our sponsor" explaining why we failed
## if called from outside InducedModule(). If we can count for all
## projectives then we return true. Note that in this case the value
## of Px may have changed, but we update the original value of px=Px
## before leaving (whether we return false or true).
InstallMethod(IsNewIndecomposableOp, "checks whether the given module is indecomposable",
  [IsAlgebraObj, IsDecompositionMatrix,IsAlgebraObjModule,IsDecompositionMatrix,IsList],
  function(H,d,px,oldd,Mu)
    local Px,nu,regs,Py,y,z,a,b,n,mu,m,M,Message;

    if px=fail and px=0*px then return false; fi;

    if Mu=[fail] then Message:=Ignore;
    else Message:=Print;
    fi;

    Px:=px; Py:=true; # strip PIMs from the top of Px
    while Py<>fail and Px<>0*Px do
      Py:=MakePIMSpechtOp(d,Px!.parts[Length(Px!.parts)]);
      if Py<>fail then Px:=Px-Px!.coeffs[Length(Px!.parts)]*Py; fi;
    od;
    if Px=0*Px then
      Message("# This module is a sum of known indecomposables.\n");
      return false;
    fi;
    if IntegralCoefficients(Px / Px!.coeffs[Length(Px!.parts)]) then
      Px:=Px / Px!.coeffs[Length(Px!.parts)];
   fi;

    regs:=Obstructions(d,Px);

    if Mu<>[] then regs:=Filtered(regs,mu->mu<Mu); fi;
    regs:=Obstructions(d,Px);
    for y in regs do   ## loop through projectives that might split

      if not IsSchur(H) and MullineuxMap(H!.e,ConjugatePartition(Px!.parts[1]))
      <>Px!.parts[Length(Px!.parts)] then
        Py:=true;  ## strip any known indecomposables off the bottom of Px
        while Py<>fail and Px<>0*Px do
          Py:=MakePIMSpechtOp(d,ConjugatePartition(Px!.parts[1]));
          if Py<>fail and IsERegular(Py!.H!.e,Py!.parts[Length(Py!.parts)]) then
            Px:=Px-Px!.coeffs[1]*MullineuxMap(Py);
          else Py:=fail;
          fi;
        od;
      fi;

      m:=0;            ## lower and upper bounds on decomposition the number
      if Px=0*Px then M:=0;
      else M:=Coefficient(Px,y)/Px!.coeffs[Length(Px!.parts)];
      fi;
      if M<>0 then Py:=MakePIMSpechtOp(d,y); fi;
      if not ( m=M or Px!.parts[Length(Px!.parts)]>=y ) then
        if Py=fail then
          Message("# The multiplicity of S(", TightStringList(y),
            ") in <Px> is zero; however, S(", TightStringList(y),
            ") is not known\n");
          px!.coeffs:=Px!.coeffs; px!.parts:=Px!.parts; return false;
        else
          Px:=Px-Coefficient(Px,y)*Py;
          if not PositiveCoefficients(Px) then BUG("IsNewIndecomposable",1);fi;
          M:=0;
        fi;
      fi;

      if Px<>0*Px and IntegralCoefficients(Px/Px!.coeffs[Length(Px!.parts)]) then
        Px:=Px/Px!.coeffs[Length(Px!.parts)];
      fi;

      ## remember that Px.coeffs[Length(Px.parts)] could be greater than 1
      if m<>M and (Coefficient(Px,y) mod Px!.coeffs[Length(Px!.parts)])<>0 then
        if Py=fail then
          Message("# <Px> is not indecomposable, as at least ",
            Coefficient(Px,y) mod Px!.coeffs[Length(Px!.parts)], " copies of P(",
            TightStringList(y), ") split off.\n# However, P(",
            TightStringList(y), ") is not known\n");
          px!.coeffs:=Px!.coeffs; px!.parts:=Px!.parts; return false;
        else
          ## this is at least projective; perhaps more still come off though
          Px:=Px-(Coefficient(Px,y) mod Px!.coeffs[Length(Px!.parts)])*Py;
          if not PositiveCoefficients(Px) then BUG("IsNewIndecomposable",2);fi;
          if IntegralCoefficients(Px/Px!.coeffs[Length(Px!.parts)]) then
            Px:=Px/Px!.coeffs[Length(Px!.parts)];
          fi;
        fi;
      fi;

      ## At this point the coefficient of Sx in Px divides the coefficient of
      ## Sy in Px. If Py splits off then it does so in multiples of
      ## (Px:Sx)=Px.coeffs[Length(Px.parts)].
      if m<>M and (Py=fail
        or PositiveCoefficients(Px-Px!.coeffs[Length(Px!.parts)]*Py))
      then
        ## use the q-Schaper theorem to test whether Sy is contained in Px
        M:=Minimum(M,InnerProduct(Px/Px!.coeffs[Length(Px!.parts)],
                                  Schaper(H, y)));
        if M=0 then # NO!
          ## Px-(Px:Sy)Py is still projective so subtract Py if it is
          ## known. If Py=false (ie. not known), then at least we know
          ## that Px is not indecomposable, even though we couldn't
          ## calculate Py.
          if Py=fail then
            Message("# The multiplicity of S(", TightStringList(y),
              ") in P(", TightStringList(Px!.parts[Length(Px!.parts)]),
             ") is zero;\n#  however, P(", TightStringList(y),
             ") is not known.\n");
            px!.coeffs:=Px!.coeffs; px!.parts:=Px!.parts; return false;
          else
            Px:=Px-Coefficient(Px,y)*Py;
            if not PositiveCoefficients(Px) then BUG("IsNewIndecomposable",3);
            fi;
          fi;
        elif Px!.coeffs[Length(Px!.parts)]<>Coefficient(Px,y) then
          ## We know that (Px:Sy)>=m>0, but perhaps some Py's still split off

          m:=1;
          if m=M then
            if Coefficient(Px,y)<>m*Px!.coeffs[Length(Px!.parts)] then
              if Py=fail then
                Message("# The multiplicity of S(", TightStringList(y),
                    ") in P(",TightStringList(Px!.parts[Length(Px!.parts)]),
                    ") is ", m, "however, P(",TightStringList(y),
                    ") is unknown.\n");
                px!.coeffs:=Px!.coeffs; px!.parts:=Px!.parts;
                return false;
              else
                Px:=Px-(Coefficient(Px,y)-Px!.coeffs[Length(Px!.parts)]*m)*Py;
                if not PositiveCoefficients(Px) then
                  BUG("IsNewIndecomposable",4);
                fi;
              fi;
            fi;
          fi;

          if m<>M then
            ## see if we can calculate this decomposition number (this uses
            ## row and column removal)
            a:=DecompositionNumber(Px!.H, y, Px!.parts[Length(Px!.parts)]);
            if a<>fail then
              if Px!.coeffs[Length(Px!.parts)]*a=Coefficient(Px,y) then m:=a; M:=a;
              elif Py<>fail then
                ## precisely this many Py's come off
                Px:=Px-(Coefficient(Px,y)-Px!.coeffs[Length(Px!.parts)]*a)*Py;
                m:=a; M:=a; # upper and lower bounds are equal
                if not PositiveCoefficients(Px) then
                  BUG("IsNewIndecomposable",5);
                fi;
              fi;
            fi;
          fi;

          if m<>M and Py=fail then ## nothing else we can do
            Message("# The multiplicity of S(", TightStringList(y),
                    ") in P(",TightStringList(Px!.parts[Length(Px!.parts)]),
                    ") is at least ", m, " and at most ", M,
                    ";\n# however, P(",TightStringList(y),") is unknown.\n");
            px!.coeffs:=Px!.coeffs; px!.parts:=Px!.parts;
            return false;
          fi;

          if m<>M then
            ## Maybe the Mullineux map can lower M...
            M:=Minimum(M,Coefficient(Px,
                 Py!.parts[Length(Py!.parts)]/Px!.coeffs[Length(Px!.parts)]));
            if Coefficient(Px,y)>M*Px!.coeffs[Length(Px!.parts)] then
              Px:=Px-(Coefficient(Px,y)-M*Px!.coeffs[Length(Px!.parts)])*Py;
            fi;
            while m<M and not
            PositiveCoefficients(Px-(Px!.coeffs[Length(Px!.parts)]*(M-m))*Py) do
              m:=m+1;
            od;
          fi;

          ## finally, we take a look at inducing the simples in oldd
          if m<>M and oldd<>fail then
            if not IsBound(oldd!.simples) then ## we have to first induce them
              oldd!.simples:=[];
              if IsBound(oldd!.ind) then       ## defined in InducedModule()
               for mu in [1..Length(oldd!.cols)] do
                  a:=Invert(oldd,oldd!.cols[mu]);
                  if a<>fail then
                    for n in [1..H!.e] do  ## induce simples of oldd
                      z:=Sum([1..Length(a!.coeffs)],b->a!.coeffs[b]
                           *oldd!.ind[Position(oldd!.rows,a!.parts[b])][n]);
                      if z<>0*z then Add(oldd!.simples,z);fi;
                    od;
                  fi;
                od;
              else   ## do everything by hand
                for mu in [1..Length(oldd!.cols)] do
                  a:=Invert(oldd,oldd!.cols[mu]);
                  if a<>fail then
                    for n in [0..H!.e-1] do
                      z:=RInducedModule(H,a,H!.e,n);
                      if z<>0*z then Add(oldd!.simples,z); fi;
                    od;
                  fi;
                od;
              fi;
            fi;
            mu:=Length(oldd!.simples);
            while mu >0 and m<M do
              z:=oldd!.simples[mu];
              if y=regs[Length(regs)]
              or Lexicographic(z!.parts[1],Py!.parts[Length(Py!.parts)]) then
                a:=InnerProduct(z,Py);
                if a<>0 then
                  b:=InnerProduct(z,Px)/Px!.coeffs[Length(Px!.parts)];
                  m:=Maximum(m,M-Int(b/a));
                fi;
              fi;
              mu:=mu-1;
            od;
            if Coefficient(Px,y)>M*Px!.coeffs[Length(Px!.parts)] then
              Px:=Px-(Coefficient(Px,y)-M*Px!.coeffs[Length(Px!.parts)])*Py;
            fi;
          fi;
          if m<>M then ## nothing else we can do
            px!.coeffs:=Px!.coeffs; px!.parts:=Px!.parts;
            Message("# The multiplicity of S(", TightStringList(y),
                ") in P(",TightStringList(Px!.parts[Length(Px!.parts)]),
                ") is at least ", m, " and at most ", M, ".\n");
            return false;
          fi;
        fi;   ## q-Schaper test
      fi;
    od;
    if Px=0*Px then
      Message("# This module is a sum of known indecomposables.\n");
      return false;
    elif Px!.coeffs[Length(Px!.parts)]<>1 then BUG("IsNewIndecomposable",6);
    else px!.coeffs:=Px!.coeffs; px!.parts:=Px!.parts; return true;
   fi;
  end
);  # IsNewIndecomposable

#P A front end to d.operations.AddIndecomposable. This function adds <Px>
## into the decomposition matrix <d> and checks that it is compatible with
## its image under the Mullineux map, if this is already in <d>, and
## inserts it if it is not.
InstallMethod(AddIndecomposable,"fill out entries of decomposition matrix",
  [IsDecompositionMatrix,IsHeckeSpecht],
  function(d, Px)
    if Position(d!.cols, Px!.parts[Length(Px!.parts)])=fail then
      Print("# The projective P(",TightStringList(Px!.parts[Length(Px!.parts)]),
            ") is not listed in <D>\n");
    else AddIndecomposable(d,Px,true);
    fi;
  end
);# AddIndecomposable

## This function will read the file "n" if n is a string; otherwise
## it will look for the relevant file for H(Sym_n). If crystal=true
## this will be a crystal decomposition matrix, otherwise it will be
## a 'normal' decomposition matrix. When IsInt(n) the lists
## CrystalMatrices[] and DecompositionMatrices[] are also checked, and
## the crystal decomposition matrix is calculated if it is not found
## and crystal=true (and IsInt(n)).
## Question: if crystal=false but IsBound(CrystalMatrices[n]) should
## we still try and read e<H.e>p0.n or specialize CrystalMatrices[n]
## immediately. We try and read the file first...
InstallMethod(ReadDecompositionMatrix, "load matrix from library",
  [IsAlgebraObj,IsInt,IsBool],
  function(H, n, crystal) local d, file, SpechtDirectory;

    if not IsBound(SpechtDirectory) then SpechtDirectory:=""; fi;

    if crystal then
      if IsBound(H!.CrystalMatrices[n]) then
        d:=H!.CrystalMatrices[n];
        if d=fail and H!.info.SpechtDirectory=SpechtDirectory then
          return fail;
        elif d<>fail and ForAll([1..Length(d!.cols)],c->IsBound(d!.d[c]))
        then return d;
        fi;
      fi;
      file:=Concatenation("e",String(H!.e),"crys.",String(n));
    else
      file:=Concatenation(H!.HeckeRing,".",String(n));
    fi;
    d:=ReadDecompositionMatrix(H,file,crystal);
    if crystal then H!.CrystalMatrices[n]:=d; fi;
    return d;
  end
);

InstallMethod(ReadDecompositionMatrix, "load matrix from library",
  [IsAlgebraObj,IsString,IsBool],
  function(H, str, crystal)
    local msg, file, M, d, c, parts, coeffs, p, x, r, cm, rm;

    A_Specht_Decomposition_Matrix:=fail; ## just in case

    file:=Filename([Directory("."),SpechtDirectory,H!.info.Library],str);
    if crystal then
      msg:="ReadCrystalMatrix-";
    else
      msg:="ReadDecompositionMatrix-";
    fi;

    d:=fail;

    if file <> fail then Read(file); fi;

    if A_Specht_Decomposition_Matrix<>fail then   ## extract matrix from M
      M:=A_Specht_Decomposition_Matrix;
      A_Specht_Decomposition_Matrix:=fail;
      r:=Set(M.rows); c:=Set(M.cols);
      if not IsSchur(H) and r=c then
        d:=DecompositionMatrix(H,r,
              Filtered(c,x->IsERegular(H!.e,x)),not IsBound(M.crystal));
      elif not IsSchur(H) then
        d:=DecompositionMatrix(H,r,c,not IsBound(M.crystal));
      else
        d:=DecompositionMatrix(H,r,r,not IsBound(M.crystal));
      fi;
      if IsSet(M.rows) and IsSet(M.cols) then ## new format
        if IsBound(M.matname) then d!.matname:=M.matname; fi;
        for c in [1..Length(d!.cols)] do
          cm:=Position(M.cols, d!.cols[c]);
          if cm<>fail and IsBound(M.d[cm]) then
            x:=M.d[cm];
            parts:=[]; coeffs:=[];
            for rm in [1..Length(x)/2] do
              r:=Position(d!.rows,M.rows[x[rm+Length(x)/2]]);
              if r<>fail then
                Add(parts,r);
                if IsInt(x[rm]) then Add(coeffs,x[rm]);
                else
                  p:=LaurentPolynomialByCoefficients(
                    FamilyObj(1),
                    x[rm]{[2..Length(x[rm])]},x[rm]);
                  Add(coeffs,p);
                fi;
              fi;
            od;
            if parts<>[] then   ## paranoia
              SortParallel(parts,coeffs);
              d!.d[c]:=rec(parts:=parts,coeffs:=coeffs);
           fi;
          fi;
        od;
      else  ## old format
        d!.d:=List(c, r->rec(coeffs:=[], parts:=[]));
        ## next, we unravel the decomposition matrix
        for rm in [1..Length(M.rows)] do
          r:=Position(d!.rows,M.rows[rm]);
          if r<>fail then
            x:=1;
            while x<Length(M.d[rm]) do
              c:=Position(d!.cols,M.cols[M.d[rm][x]]);
              if c<>fail then
                Add(d!.d[c].coeffs, M.d[rm][x+1]);
                Add(d!.d[c].parts, r);
              fi;
              x:=x+2;
            od;
          fi;
        od;
        for c in [1..Length(d!.d)] do
          if d!.d[c].parts=[] then Unbind(d!.d[c]);
          else SortParallel(d!.d[c].parts, d!.d[c].coeffs);
          fi;
        od;
      fi;
    fi;
    return d;
  end
); # ReadDecompositionMatrix

## Look up the decomposition matrix in the library files and in the
## internal lists and return it if it is known.
## NOTE: this function does not use the crystal basis to calculate the
## decomposition matrix because it is called by the various conversion
## functions X->Y which will only need a small part of the matrix in
## general. The function FindDecompositionMatrix() also uses the crystal
## basis.
InstallMethod(KnownDecompositionMatrix,"looks for a known decomposition matrix",
  [IsAlgebraObj,IsInt],
  function(H,n)
    local d, x, r, c;

    if IsBound(H!.DecompositionMatrices[n]) then
      d:=H!.DecompositionMatrices[n];
      if ( d<>fail and ForAll([1..Length(d!.cols)],c->IsBound(d!.d[c])) )
      or H!.info.SpechtDirectory=SpechtDirectory then return d;
      elif H!.info.SpechtDirectory<>SpechtDirectory then
        for x in [1..Length(H!.DecompositionMatrices)] do
          if IsBound(H!.DecompositionMatrices[x]) and
          H!.DecompositionMatrices[x]=fail then
            Unbind(H!.DecompositionMatrices[x]);
          fi;
        od;
      fi;
    fi;
    d:=ReadDecompositionMatrix(H,n,false);

    ## next we look for crystal matrices
    if d=fail and not IsSchur(H) and IsZeroCharacteristic(H) then
      d:=ReadDecompositionMatrix(H,n,true);
      if d<>fail then d:=Specialized(d); fi;
    fi;

    if d=fail and n<2*H!.e then
      ## decomposition matrix can be calculated
      r:=Partitions(n);
      if not IsSchur(H) then c:=ERegularPartitions(H!.e,n);
      else c:=r;
      fi;
      d:=DecompositionMatrix(H, r, c, true);

      for x in [1..Length(d!.cols)] do
        if IsECore(H,d!.cols[x]) then    ## an e-core
          AddIndecomposable(d,
            Module(H,"S",1,d!.cols[x]),false);
        elif IsSimpleModule(H,d!.cols[x]) then ## first e-weight 1 partition
          c:=Module(H,"S",1,ECore(H,d!.cols[x]) )
                   *HeckeOmega(H,"S",H!.e);
          for r in [2..Length(c!.parts)] do
            AddIndecomposable(d,
                  Module(H,"S",[1,1],c!.parts{[r-1,r]}),false);
          od;
          if IsSchur(H) then
            AddIndecomposable(d,
              Module(H,"S",1,c!.parts[1]),false);
          fi;
        fi;
      od;
    elif IsBound(H!.DecompositionMatrices[n]) then ## partial answer only
      return H!.DecompositionMatrices[n];
    fi;
    H!.DecompositionMatrices[n]:=d;
    return d;
  end
);  # KnownDecompositionMatrix

## almost identical to KnownDecompositionMatrix except that if this function
## fails then the crystalized decomposition matrix is calculated.
InstallMethod(FindDecompositionMatrix,"find or calculate CDM",
  [IsAlgebraObj,IsInt],
  function(H,n) local d,c;
    d:=KnownDecompositionMatrix(H,n);
    if d=fail and not IsSchur(H) and IsZeroCharacteristic(H) then
      d:=DecompositionMatrix(H,
              Partitions(n),ERegularPartitions(H!.e,n),false);
      for c in [1..Length(d!.cols)] do
        if not IsBound(d!.d[c]) then
          AddIndecomposable(d,FindPq(H,d!.cols[c]),false);
        fi;
      od;
      H!.CrystalMatrices[n]:=d;
      d:=Specialized(d);
      H!.DecompositionMatrices[n]:=d;
    fi;
    return d;
  end
); # FindDecompositionMatrix

## Crystal basis elements ######################################################

#########################################################################
## Next, for fields of characteristic 0, we implement the LLT algorithm.
## Whenever a crystal basis element of the Fock space is calculated we
## store it in the relevant decomposition matrix n CrystalMatrices[].
## The actual LLT algorithm is contained in the function Pq (should
## really be called Pv...), but there are also functions Sq and Dq as
## well. These functions work as follows:
##   FindPq(mu) -> sum_nu d_{nu,mu} S(nu)
##   FindSq(mu) -> sum_nu d_{mu,nu} D(nu)
##   FindDq(mu) -> sum_nu d_{mu,nu}^{-1} S(nu)
## The later two functions will call Pq() as needed. The "modules" x
## returned by these functions have x.module equal to "Pq", "Sq" and
## "Dq" respectively to distinguish them from the specialized versions.
## Accordingly we need H.operations.Xq for X = S, P, and D; these are
## defined after Pq(), Sq(), and Dq() (which they make use of).

## Retrieves or calculates the crystal basis element Pq(mu)
InstallMethod(FindPq,"finds the crystal basis element Pq(mu)",
  [IsAlgebraObj,IsList],
  function(H,mu) local  n, c, CDM, i, r, s, x, v, val,coeffs,tmp;
    v:=H!.Indeterminate;

    if mu=[] then return Module(H,"Sq",v^0,[]); fi;
    n:=Sum(mu);

    ## first we see if we have already calculated Pq(mu)
    if not IsBound(H!.CrystalMatrices[n]) or H!.CrystalMatrices[n]=fail then
      x:=ReadDecompositionMatrix(H,n,true);
      if x=fail then
        H!.CrystalMatrices[n]:=DecompositionMatrix(H,
          Partitions(n), ERegularPartitions(H!.e,n), false);
      fi;
    fi;
    CDM:=H!.CrystalMatrices[n];
    c:=Position(CDM!.cols,mu);
    if IsBound(CDM!.d[c]) then
      return Module(H,"Sq",CDM!.d[c].coeffs,
                 List(CDM!.d[c].parts, s->CDM!.rows[s]) );
    fi;

    if IsECore(H!.e,mu) then
      x:=Module(H,"Sq",v^0,mu);
    elif EWeight(H!.e,mu)=1 then
      x:=Module(H,"Sq",v^0,ECore(H!.e,mu))
                 * HeckeOmega(H,"Sq",H!.e);
      r:=Position(x!.parts,mu);
      if r=1 then
        x!.parts:=x!.parts{[1]};
        x!.coeffs:=[v^0];
      else
        x!.parts:=x!.parts{[r-1,r]};
        x!.coeffs:=[v,v^0];
      fi;
    else  ## we calculate Pq(mu) recursively using LLT

      ## don't want to change the original mu
      mu:=StructuralCopy(mu);
      i:=1;
      while i<Length(mu) and mu[i]=mu[i+1] do
        i:=i+1;
      od;
      r:=(mu[i]-i) mod H!.e;
      mu[i]:=mu[i]-1;
      s:=1;
      i:=i+1;
      while i<=Length(mu) do
        while i<>Length(mu) and mu[i]=mu[i+1] do
          i:=i+1;
        od;
        if r=(mu[i]-i) mod H!.e then
          s:=s+1;
          mu[i]:=mu[i]-1;
          i:=i+1;
        else i:=Length(mu)+1;
        fi;
      od;
      if mu[Length(mu)]=0  then Unbind( mu[Length(mu)] ); fi;
      x:=qSInducedModule(H, FindPq(H,mu), s, r);
      n:=1;
      while n<Length(x!.parts) do
        tmp:=CoefficientsOfLaurentPolynomial(x!.coeffs[Length(x!.parts)-n]);
        if tmp[2]>0 then n:=n+1;
        else
          r := StructuralCopy( x!.coeffs[Length(x!.parts)-n] );
          tmp:=ShallowCopy(CoefficientsOfLaurentPolynomial(r));
          mu:=x!.parts[Length(x!.parts)-n];
          if Length(tmp[1]) < 1-tmp[1] then
            tmp[1]:=Concatenation(tmp[1],
              List([1..Length(tmp[1])-1-tmp[2]], i->0));
          fi;
          tmp[1]:=tmp[1]{[1..1-tmp[2]]};
          tmp[1]:=Concatenation(tmp[1], Reversed(tmp[1]{[1..-tmp[2]]}));
          r:=LaurentPolynomialByCoefficients(FamilyObj(1),tmp[1],tmp[2]);
          x := x-r*FindPq(H,mu);
          if mu in x!.parts then n:= n+1; fi;
        fi;
      od;
      r := List(x!.coeffs, s->s <> 0 * v ^ 0);
      if false in r then
        x!.coeffs := ListBlist( x!.coeffs, r );
        x!.parts := ListBlist( x!.parts, r );
      fi;
    fi;

    ## having found x we add it to CDM
    CDM!.d[c]:=rec(coeffs:=x!.coeffs,
                  parts:=List(x!.parts, r->Position(CDM!.rows,r)) );

    ## for good measure we also add the Mullineux image of Pq(mu) to CDM
    ## (see LLT Theorem 7.2)
    n:=Position(CDM!.cols,ConjugatePartition(x!.parts[1]));
    if c<>n then             ## not self-image under MullineuxMap
      r:=List(x!.coeffs*v^0, i->Value(i,v^-1));     ## v -> v^-1
      for i in [Length(r),Length(r)-1..1] do   ## multiply by r[1]
        tmp:=ShallowCopy(CoefficientsOfLaurentPolynomial(r[i]));
        tmp[2]:=tmp[2]-CoefficientsOfLaurentPolynomial(r[1])[2];
        r[i]:=LaurentPolynomialByCoefficients(FamilyObj(1),tmp[1],tmp[2]);
      od;
      s:=List(x!.parts, mu->Position(CDM!.rows,ConjugatePartition(mu)));
      SortParallel(s,r);
      CDM!.d[n]:=rec(coeffs:=r,parts:=s);
    fi;
    return x;
  end
); # FindPq

## Strictly speaking the functions Sq() and Dq() are superfluous as
## these functions could be carried out by the decomposition matrix
## operations; however the point is that the crystal matrices are
## allowed to have missing columns, and if any needed columns missing
## these functions calculate them on the fly via using Pq().

## writes S(mu), from the Fock space, as a sum of D(nu)
InstallMethod(FindSq,"write Sq(mu) as sum of Dq(mu)",
  [IsAlgebraObj,IsList],
  function(H,mu) local x, r, CDM, n;
    n:=Sum(mu);

    ## we need the list of e-regular partitions. as we will have to
    ## create CrystalMatrices[n] anyway, we get the columns from there.
    if not IsBound(H!.CrystalMatrices[n]) or H!.CrystalMatrices[n]=fail then
      r:=Partitions(n);
      H!.CrystalMatrices[n]:=DecompositionMatrix(H,
        r, ERegularPartitions(H!.e,n), false);
    fi;
    CDM:=H!.CrystalMatrices[n];

    x:=Module(H,"Dq",0,[]);
    r:=Length(CDM!.cols);
    mu:=Position(CDM!.rows,mu);
    while r>0 and CDM!.cols[r]>=CDM!.rows[mu] do
      if not IsBound(CDM!.d[r]) then FindPq(H,CDM!.cols[r]); fi;
      n:=Position(CDM!.d[r].parts,mu);
      if n<>fail then
        x:=x+Module(H,"Dq",CDM!.d[r].coeffs[n],CDM!.cols[r]);
      fi;
      r:=r-1;
    od;
    return x;
  end
);

## write D(mu), from the Fock space, as a sum of S(nu)
InstallMethod(FindDq, "write Dq(mu) as sum of Sq(mu)",
  [IsAlgebraObj,IsList],
  function(H,mu) local inv, x, c, CDM;
    inv:=Module(H,"Sq",1,mu);
    x:=Module(H,"Dq",1,mu)-FindSq(H,mu);
    CDM:=H!.CrystalMatrices[Sum(mu)];
    c:=Position(CDM!.cols,mu);

    if IsBound(CDM!.inverse[c]) then
      return Module(H,"Sq",CDM!.inverse[c].coeffs,
                                List(CDM!.inverse[c].parts,c->CDM!.cols[c]));
    fi;

    while x<>0*x do
      c:=Length(x!.parts);
      inv:=inv+Module(H,"Sq",x!.coeffs[c],x!.parts[c]);
      x:=x-x!.coeffs[c]*FindSq(H,x!.parts[c]);
    od;

    ## now place answer back in CDM
    CDM!.inverse[Position(CDM!.cols,mu)]:=rec(coeffs:=inv!.coeffs,
             parts:=List(inv!.parts,c->Position(CDM!.cols,c)) );
    return inv;
  end
); # FindDq


[Verzeichnis aufwärts0.86unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-04-27]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge