Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/GAP/pkg/quagroup/gap/   (Algebra von RWTH Aachen Version 4.15.1©)  Datei vom 11.0.2024 mit Größe 132 kB image not shown  

Quelle  qea.gi   Sprache: unbekannt

 
#############################################################################
##
#W  qea.gi                  QuaGroup                           Willem de Graaf
##
##
##  Constructors for quantized enveloping algebras, and highest
##  weight modules.
##

############################################################################
##
## Some functions for dealing with "generalised binomials" as they 
## appear in the basis of the Lusztig Z-form of the quea. These functions
## rewrite elements as linear combinations of basis elements.
## 
## A binomial as the one below is expressed as [ delta, s ], where
## delta = 0,1 according to whether we multiply by K or not.
## An expression is a linear combination of such things.
##
QGPrivateFunctions.Multiply_Bin_Expr:= function( s, exx )

     # expresses
     #
     #    / K \ 
     #    |   | * exx
     #    \ s / 
     #
     # as a linear combination of such things...
     # The algorithm is simply based on the definition of the binomial,
     # and om some relations found in Lusztig, J. Amer math Soc. 1990
     # 278.

     local add_elm, expr, i, j, newexp, m, n, cf;

     add_elm:= function( ee, elm, cf )

          local pos;

          pos:= Position( ee, elm );
          if pos = fail then
             Add( ee, elm ); Add( ee, cf );
          else
             ee[pos+1]:= ee[pos+1]+ cf;
             if ee[pos+1]=0*ee[pos+1] then
                Unbind( ee[pos] ); Unbind( ee[pos+1] );
                ee:= Filtered( ee, x -> IsBound(x) );
             fi;
          fi;
          return ee;
     end;

     expr:= exx;
     for i in [1..s] do
         # multiply expr by q^(-i+1)K-q^(i-1)K^-1:
         
         newexp:= [ ];
         for j in [1,3..Length(expr)-1] do
             m:= expr[j];
             if m[1] = 0 then
                n:= ShallowCopy( m );
                n[1]:= 1;
                newexp:= add_elm( newexp, n, expr[j+1]*_q^(-i+1) );
             else
                n:= ShallowCopy( m );
                n[2]:= n[2]+1;
                newexp:= add_elm( newexp, n,
                    expr[j+1]*_q^(-i+1)*_q^m[2]*(_q^n[2]-_q^-n[2]) );
                n:= ShallowCopy( m );
                n[1]:= 0;
                newexp:= add_elm( newexp, n,
                    expr[j+1]*_q^(-i+1)*_q^(2*m[2]) );
             fi;
             
             if m[1]=0 then
                n:= ShallowCopy( m );
                n[2]:= n[2]+1;
                newexp:= add_elm( newexp, n,
                    expr[j+1]*_q^(i-1)*_q^-m[2]*(_q^n[2]-_q^-n[2]) );
                n:= ShallowCopy( m );
                n[1]:= 1;
                newexp:= add_elm( newexp, n,
                    -expr[j+1]*_q^(i-1)*_q^(-2*m[2]) );
             else   
                n:= ShallowCopy( m );
                n[1]:= 0;
                newexp:= add_elm( newexp, n, -expr[j+1]*_q^(i-1) );
             fi;
         od;
         expr:= newexp;
     od;

     cf:= (_q-_q^-1)^s*GaussianFactorial( s, _q );
     for i in [2,4..Length(expr)] do
         expr[i]:= expr[i]/cf;
     od;
     return expr;
end;    

QGPrivateFunctions.Multiply_K_Expr:= function( exx )

     # multiply exx by K...

     local add_elm, expr, i, m;

     add_elm:= function( ee, elm, cf )

          local pos;

          pos:= Position( ee, elm );
          if pos = fail then
             Add( ee, elm ); Add( ee, cf );
          else
             ee[pos+1]:= ee[pos+1]+ cf;
             if ee[pos+1]=0*ee[pos+1] then
                Unbind( ee[pos] ); Unbind( ee[pos+1] );
                ee:= Filtered( ee, x -> IsBound(x) );
             fi;
          fi;
          return ee;
     end;

     expr:= [ ];
     for i in [1,3..Length(exx)-1] do
         m:= ShallowCopy( exx[i] );
         if m[1] = 0 then
            m[1]:= 1;
            expr:= add_elm( expr, m, exx[i+1] );
         else
            m[2]:= m[2]+1;
            expr:= add_elm( expr, m, exx[i+1]*_q^(m[2]-1)*(_q^m[2]-_q^-m[2]) );
            m:= ShallowCopy( exx[i] );
            m[1]:= 0;
            expr:= add_elm( expr, m, exx[i+1]*_q^(2*m[2]) );
         fi;
     od;

     return expr;
end;

QGPrivateFunctions.Multiply_Exp_Exp:= function( ex1, ex2 )

      # multiply the two expressions ex1, ex2.

     local add_elm, res, expr, i, j, m;

     add_elm:= function( ee, elm, cf )

          local pos;

          pos:= Position( ee, elm );
          if pos = fail then
             Add( ee, elm ); Add( ee, cf );
          else
             ee[pos+1]:= ee[pos+1]+ cf;
             if ee[pos+1]=0*ee[pos+1] then
                Unbind( ee[pos] ); Unbind( ee[pos+1] );
                ee:= Filtered( ee, x -> IsBound(x) );
             fi;
          fi;
          return ee;
     end;

     res:= [ ];
     for i in [ 1, 3 .. Length(ex1)-1] do
         expr:= ex2;
         m:= ex1[i];
         if m[1] <> 0 then
            expr:= QGPrivateFunctions.Multiply_K_Expr( expr );
         fi;
         expr:= QGPrivateFunctions.Multiply_Bin_Expr( m[2], expr );
         for j in [1,3..Length(expr)-1] do
            res:= add_elm( res, expr[j], expr[j+1]*ex1[i+1] );
         od;
     od;

     return res;
end;


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

############################################################################
##
#M   PrintObj( <wr> )
##
##   We need a new PrintObj method for weight rep elements because in the one
##   in the library there is a statement e[k+1] > 0, which will fail for
##   q-elements. 
##
InstallMethod( PrintObj,
        "for weight rep element",
        true,
       [ IsWeightRepElement and IsPackedElementDefaultRep ], 0,
       function( v )

    local e,k;

    e:= v![1];
    if e = [] then
        Print( "0*v0" );
    else
        for k in [1,3..Length(e)-1] do
            if k>1 and not (IsRat(e[k+1]) and e[k+1]<0) then
                Print("+" );
            fi;
            Print( e[k+1]*e[k][2], "*v0" );
        od;
    fi;

end );



############################################################################
##
#M  ObjByExtRep( <fam>, <list> )
#M  ExtRepOfObj( <obj> )
##
InstallMethod( ObjByExtRep,
   "for family of QEA elements, and list",
   true, [ IsQEAElementFamily, IsList ], 0,
   function( fam, list )
#+
    return Objectify( fam!.packedQEAElementDefaultType,
                    [ Immutable(list) ] );
end );

InstallMethod( ExtRepOfObj,
   "for an QEA element",
   true, [ IsQEAElement ], 0,
   function( obj )
#+
   return obj![1];

end );

###########################################################################
##
#M  PrintObj( <m> ) . . . . . . . . . . . . . . . . for an QEA element
##
InstallMethod( PrintObj,
        "for QEA element",
        true, [IsQEAElement and IsPackedElementDefaultRep], 0,
        function( x )

    local   lst,  k, i, n, rank;

    # This function prints an element of a quantized enveloping algebra.

    lst:= x![1];
    n:= FamilyObj( x )!.noPosRoots;
    rank:= FamilyObj( x )!.rank;
    if lst=[] then
        Print("0");
    else
        for k in [1,3..Length(lst)-1] do
            if k>1 then
                Print("+");
            fi;
            if lst[k+1] <> lst[k+1]^0 then
                Print( "(",lst[k+1],")*");
            fi;
            if lst[k] = [] then
                Print("1");
            else

                for i in [1,3..Length(lst[k])-1] do
                    if IsList( lst[k][i] ) then
                       if lst[k][i][2] > 0 then
                          Print( "K", lst[k][i][1]-n );
                       fi;
                       if lst[k][i+1] > 0 then
                          Print( "[ K",lst[k][i][1]-n," ; ", 
                                              lst[k][i+1], " ]");
                       fi;
                    elif lst[k][i] <=n then
                        Print("F",lst[k][i]);
                        if lst[k][i+1]>1 then
                            Print("^(",lst[k][i+1],")");
                        fi;
                    else
                        Print("E",lst[k][i]-n-rank);
                        if lst[k][i+1]>1 then
                            Print("^(",lst[k][i+1],")");
                        fi;
                    fi;
                    if i <> Length(lst[k])-1 then
                        Print("*");
                    fi;
                od;
            fi;

        od;

    fi;

end );

#############################################################################
##
#M  OneOp( <m> ) . . . . . . . . . . . . . . . . for a QEA element
#M  ZeroOp( <m> ) . . . . . . . . . . . . . . .  for a QEA element
#M  \<( <m1>, <m2> ) . . . . . . . . . . . . . . for two QEA elements
#M  \=( <m1>, <m2> ) . . . . . . . . . . . . . . for two QEA elements
#M  \+( <m1>, <m2> ) . . . . . . . . . . . . . . for two QEA elements
#M  \-( <m> )     . . . . . . . . . . . . . . for a QEA element
#M  \in( <U>, <u> )  . . . . . . . . . . . . . . for QEA, and element
##
InstallMethod( OneOp,
        "for QEA element",
        true, [ IsQEAElement and IsPackedElementDefaultRep ], 0,
        function( x )

    return ObjByExtRep( FamilyObj( x ), [ [], FamilyObj(x)!.quantumPar^0 ] );

end );

InstallMethod( ZeroOp,
        "for QEA element",
        true, [ IsQEAElement and IsPackedElementDefaultRep ], 0,
        function( x )

    return ObjByExtRep( FamilyObj( x ), [ ] );

end );


InstallMethod( \<,
                "for two QEA elements",
        IsIdenticalObj, [ IsQEAElement and IsPackedElementDefaultRep,
                IsQEAElement and IsPackedElementDefaultRep ], 0,
        function( x, y )
    return x![1]< y![1];
end );

InstallMethod( \=,
                "for two QEA elements",
        IsIdenticalObj, [ IsQEAElement and IsPackedElementDefaultRep,
                IsQEAElement and IsPackedElementDefaultRep ], 0,
        function( x, y )


    return x![1] = y![1];
end );


InstallMethod( \+,
        "for two QEA elements",
        true, [ IsQEAElement and IsPackedElementDefaultRep,
                IsQEAElement and IsPackedElementDefaultRep], 0,
        function( x, y )

    local   ex,  ey,  mons,  cfs,  i,  lst, len;
    
    # Insert one sorted list in the second one; 
    # can be done much more efficiently!
    
    ex:= x![1]; ey:= y![1];
    mons:= [ ]; cfs:= [ ];
    for i in [1,3..Length(ex)-1] do
        Add( mons, ex[i] ); Add( cfs, ex[i+1] );
    od;

    for i in [1,3..Length(ey)-1] do
        Add( mons, ey[i] ); Add( cfs, ey[i+1] );
    od;
    SortParallel( mons, cfs );
    lst:= [ ];
    for i in [1..Length( mons )] do
        len:= Length(lst);
        if len > 0 and lst[len-1] = mons[i] then
            lst[len]:= lst[len]+cfs[i];
            if lst[len] = 0*lst[len] then
                Unbind( lst[len-1] ); Unbind( lst[len] );
                lst:= Filtered( lst, x -> IsBound(x) );
            fi;

        else
            Add( lst, mons[i] ); Add( lst, cfs[i] );
        fi;
    od;
    return ObjByExtRep( FamilyObj(x), lst );
end );


InstallMethod( AdditiveInverseSameMutability,
        "for QEA element",
        true, [ IsQEAElement and IsPackedElementDefaultRep ], 0,
        function( x )

    local   ex,  i;

    ex:= ShallowCopy(x![1]);
    for i in [2,4..Length(ex)] do
        ex[i]:= -ex[i];
    od;
    return ObjByExtRep( FamilyObj(x), ex );
end );

InstallMethod( AdditiveInverseMutable,
        "for QEA element",
        true, [ IsQEAElement and IsPackedElementDefaultRep ], 0,
        function( x )

    local   ex,  i;

    ex:= ShallowCopy(x![1]);
    for i in [2,4..Length(ex)] do
        ex[i]:= -ex[i];
    od;
    return ObjByExtRep( FamilyObj(x), ex );
end );

#############################################################################
##
#M  \*( <scal>, <m> ) . . . . . . . . .for a scalar and a QEA element
#M  \*( <m>, <scal> ) . . . . . . . . .for a scalar and a QEA element
##
InstallMethod( \*,
        "for scalar and QEA element",
        true, [ IsScalar, IsQEAElement and
                IsPackedElementDefaultRep ], 0,
        function( scal, x )

    local   ex,  i;
    
    if IsZero( scal ) then return Zero(x); fi;
    ex:= ShallowCopy( x![1] );
    for i in [2,4..Length(ex)] do
        ex[i]:= scal*ex[i];
    od;
    return ObjByExtRep( FamilyObj(x), ex );
end);

InstallMethod( \*,
        "for QEA element and scalar",
        true, [ IsQEAElement and IsPackedElementDefaultRep,
                IsScalar ], 0,
        function( x, scal )

    local   ex,  i;
    
    if IsZero( scal ) then return Zero(x); fi;
    ex:= ShallowCopy( x![1] );
    for i in [2,4..Length(ex)] do
        ex[i]:= scal*ex[i];
    od;
    return ObjByExtRep( FamilyObj(x), ex );
end);

InstallMethod( \in,
        "for QEA element and QEA",
        true, [ IsQEAElement, IsQuantumUEA ], 0,
        function( u, U )
    
    return IsIdenticalObj( ElementsFamily( FamilyObj(U) ), FamilyObj(u) );
end );

#############################################################################
##
#F  IsSpaceOfQEAElements( <V> )
##
##  If <V> is a space of elements of a quantized universal enveloping algebra,
##  then the `NiceFreeLeftModuleInfo' value of <V> is a record with the
##  following components.
##  \beginitems
##  `family' &
##     the elements family of <V>,
##
##  `monomials' &
##     a list of monomials occurring in the generators of <V>,
##
##  `zerocoeff' &
##     the zero coefficient of elements in <V>,
##
##  `zerovector' &
##     the zero row vector in the nice free left module,
##
##  \enditems
##  The `NiceVector' value of $v \in <V>$ is defined as the row vector of
##  coefficients of $v$ w.r.t. the list `monomials'.
##
##
##  This code is based on code by Thomas Breuer for the similar case
##  of vector spaces spanned by polynomials.
##
DeclareHandlingByNiceBasis( "IsSpaceOfQEAElements",
    "for free left modules of elements of a quantized uea" );

#############################################################################
##
#M  NiceFreeLeftModuleInfo( <V> )
#M  NiceVector( <V>, <v> )
#M  UglyVector( <V>, <r> )
##
InstallHandlingByNiceBasis( "IsSpaceOfQEAElements", rec(
        detect := function( F, gens, V, zero )
                  return IsQEAElementCollection( V );
        end,

          NiceFreeLeftModuleInfo := function( V )
            local gens,
                  monomials,
                  gen,
                  list,
                  zero,
                  info;

            gens:= GeneratorsOfLeftModule( V );

            monomials:= [];

            for gen in gens do
                list:= ExtRepOfObj( gen );
                UniteSet( monomials, list{ [ 1, 3 .. Length( list ) - 1 ] } );
            od;
            
            zero:= Zero( LeftActingDomain( V ) );
            info:= rec( monomials := monomials,
                        zerocoeff := zero,
                        family    := ElementsFamily( FamilyObj( V ) ) );
            
        # For the zero row vector, catch the case of empty `monomials' list.
            if IsEmpty( monomials ) then
                info.zerovector := [ zero ];
            else
                info.zerovector := ListWithIdenticalEntries( 
                                           Length( monomials ), zero );
            fi;
            
            return info;
        end,
          
        NiceVector := function( V, v )
            local info, c, monomials, i, pos;
            info:= NiceFreeLeftModuleInfo( V );
            c:= ShallowCopy( info.zerovector );
            v:= ExtRepOfObj( v );
            monomials:= info.monomials;
            for i in [ 2, 4 .. Length( v ) ] do
                pos:= Position( monomials, v[ i-1 ] );
                if pos = fail then
                    return fail;
                fi;
                c[ pos ]:= v[i];
            od;
            return c;
        end,
          
        UglyVector := function( V, r )
            local info, list, i;
            info:= NiceFreeLeftModuleInfo( V );
            if Length( r ) <> Length( info.zerovector ) then
                return fail;
            elif IsEmpty( info.monomials ) then
                if IsZero( r ) then
                    return Zero( V );
                else
                    return fail;
                fi;
            fi;
            list:= [];
            for i in [ 1 .. Length( r ) ] do
                if r[i] <> info.zerocoeff then
                    Add( list, info.monomials[i] );
                    Add( list, r[i] );
                fi;
            od;
            return ObjByExtRep( info.family, list );
        end ) );


#############################################################################
##
#F  CollectQEAElement( <sim>, <rts>, <B>, <s>, <rank>, <Mtab>, <expr> )
##
##
InstallGlobalFunction( CollectQEAElement,
        
        function( fam, expr )

    # `sim' are the simple roots.
    # `rts' are the roots in convex order.
    # `B' is the matrix of the bilinear form.
    # `s' is the number of positive roots.
    # `rank' is the rank of the root system.
    # `Mtab' is the multiplication table.
    # `qpar' is the quantum parameter.
    # `expr' is the thing that needs to be collected.
    
    local   comm_rule,  todo,  res,  m,  cf,  k,  found,  pos,  k1,  
            k2,  r,  rel,  start,  tail,  i,  mn,  m1,  j, qp, coef, 
            list1, list2, binomial_with_cst, kbit, k_normal, ee, store,
            R, sim, rts, B, s, rank, Mtab, qpar, isgeneric;

    comm_rule:= function( rel, j, i, m, n, r )
        
        # commutation rule for x_j^mx_i^n, where x_jx_i=qpar^rx_ix_j+rel
  
        # We use the following formula (easily proved by induction):
        #
        # x_j^mx_i^n = q^{nmr}x_i^nx_j^m + \sum_{l=0}^{n-1} \sum_{k=0}^{m-1}
        #     q^{(lm+k)r} xi^l xj^{m-1-k}Rx_j^kx_i^{n-1-l}, where R = rel.
        
        local   rule,  l,  k,  cf,  u,  mn, start, tail, qi, qj, den, t;
        
        if j > s + rank then
            qj:= _q^( rts[j-s-rank]*( B*rts[j-s-rank] )/2 );
        else
            qj:= _q^( rts[j]*( B*rts[j] )/2 );
        fi;
        if i > s +rank then
            qi:= _q^( rts[i-s-rank]*( B*rts[i-s-rank] )/2 );
        else
            qi:= _q^( rts[i]*( B*rts[i] )/2 );
        fi;
        
        den:= GaussianFactorial( m, qj )*GaussianFactorial( n, qi );

        rule:= [ [ i, n, j, m], qpar^(n*m*r) ];
        for l in [0..n-1] do
            for k in [0..m-1] do
                cf:= _q^((l*m+k)*r)/den;
                start:= [ ];
                if l <> 0 then
                    Add( start, i ); Add( start, l );
                    cf:= cf*GaussianFactorial( l, qi );
                fi;
                if m-1-k <> 0 then
                    Add( start, j ); Add( start, m-1-k );
                    cf:= cf*GaussianFactorial( m-1-k, qj );
                fi;
                tail:= [];
                if k <> 0 then
                    Add( tail, j ); Add( tail, k );
                    cf:= cf*GaussianFactorial( k, qj );
                fi;
                if n-1-l <> 0 then
                    Add( tail, i ); Add( tail, n-1-l );
                    cf:= cf*GaussianFactorial( n-1-l, qi );
                fi;

                for u in [1,3..Length(rel)-1] do
                    mn:= ShallowCopy( start );
                    Append( mn, rel[u] );
                    Append( mn, tail );
                    Add( rule, mn ); Add( rule, cf*rel[u+1] );
                od;
            od;
        od;

        return rule;
    end;
    
    binomial_with_cst:= function( c, t )

     # The binomial
     #
     #     / K; c \
     #     |      |
     #     \  t   /
     #
     # expressed in the integral basis. We use relations from Lusztig's 
     # paper.

     local add_elm, i, j, res, Kmin, expr;

     add_elm:= function( ee, elm, cf )

          local pos;

          if cf = 0*cf then return ee; fi;

          pos:= Position( ee, elm );
          if pos = fail then
             Add( ee, elm ); Add( ee, cf );
          else
             ee[pos+1]:= ee[pos+1]+ cf;
             if ee[pos+1]=0*ee[pos+1] then
                Unbind( ee[pos] ); Unbind( ee[pos+1] );
                ee:= Filtered( ee, x -> IsBound(x) );
             fi;
          fi;
          return ee;
     end;


     res:= [ ];
     if c <= -1 then
        c:= -c;
        for j in [0..t] do
            expr:= [ [ 0, t-j ], (-1)^j*_q^( c*(t-j) )*
                        GaussianBinomial(  c+j-1, j, _q ) ];
            for i in [1..j] do
                expr:= QGPrivateFunctions.Multiply_K_Expr( expr );
            od;
            for i in [1,3..Length(expr)-1] do
                res:= add_elm( res, expr[i], expr[i+1] );
            od;
        od;
     else
        Kmin:= [ [ 1, 0 ], _q^0, [ 0, 1], _q^-1-_q ];
        for j in [0..t] do
            expr:= [ [ 0, t-j ],  _q^( c*(t-j) )*
                        GaussianBinomial(  c, j, _q ) ];
            for i in [1..j] do
                expr:= QGPrivateFunctions.Multiply_Exp_Exp( Kmin, expr );
            od;
            for i in [1,3..Length(expr)-1] do
                res:= add_elm( res, expr[i], expr[i+1] );
            od;
        od;
      fi;
      return res;
    end;

    R:= fam!.rootSystem;
    sim:= SimpleSystemNF(R);
    rts:= fam!.convexRoots;
    B:= BilinearFormMatNF(R);
    s:= fam!.noPosRoots;
    rank:= fam!.rank;
    Mtab:= fam!.multTab;
    qpar:= fam!.quantumPar;

    if qpar = _q then 
        isgeneric:= true;
    else
        isgeneric:= false;
    fi;

    # In the program we use ... [ i, d, a ], s ... for
    #
    #       / Ki; a \
    #  Ki^d |       |
    #       \   s   /
    #

    todo:= expr;

    for k in [1,3..Length(todo)-1] do
        for i in [1,3..Length(todo[k])-1] do
            if IsList( todo[k][i] ) and Length( todo[k][i] ) = 2 then
               todo[k][i]:= ShallowCopy( todo[k][i] );
               Add( todo[k][i], 0 );
            fi;
        od;
    od;

    res:= [ ];
    while todo <> [] do

        m:= todo[1];
        cf:= todo[2];

        for i in [1,3..Length(m)-1] do
            if IsList( m[i] ) and Length( m[i] ) = 2 then
               m[i]:= ShallowCopy( m[i] );
               Add( m[i], 0 );
            fi;
        od;

        # We try to find indices in the `wrong' order.

        k:= 1; found:= false;
        while k < Length(m)-2 do

            if IsList( m[k] ) then
               k1:= m[k][1];
               list1:= true;
            else
               k1:= m[k];
               list1:= false;
            fi;
            if IsList( m[k+2] ) then
               k2:= m[k+2][1];
               list2:= true;
            else
               k2:= m[k+2];
               list2:= false;
            fi;

            if k1 > k2 then
                found:= true;
                break;
            elif k1 = k2 then

                if not list1 then
                
                   if m[k] <= s then
                       qp:= qpar^( rts[m[k]]*(B*rts[m[k]])/2 );
                       cf:= cf*GaussianBinomial( m[k+1]+m[k+3], m[k+1], qp );
                   fi;
                
                   if m[k] > s + rank then
                       qp:= qpar^( rts[m[k]-s-rank]*(B*rts[m[k]-s-rank])/2 );
                       cf:= cf*GaussianBinomial( m[k+1]+m[k+3], m[k+1], qp );
                   fi; 
                
                   m[k+1]:= m[k+1]+m[k+3];
                
                   if m[k+1] = 0*m[k+1] then
                       Unbind( m[k] ); Unbind( m[k+1] );
                       Unbind( m[k+2] ); Unbind( m[k+3] );
                       m:= Filtered( m, x -> IsBound(x) );
                       if k > 1 then
                        
                           # there is a new elt on pos k, we have to check
                           # whether it is in the correct order with 
                           # the previous element.
                           k:= k-2;
                       fi;
                   else
                       Unbind( m[k+2] ); Unbind( m[k+3] );
                       m:= Filtered( m, x -> IsBound(x) );
                   fi;
                else
                   # both are K-elements, coming from the same K_{\alpha}
                   # we do nothing (for the moment).
                   k:= k+2;
                fi;
            else
                k:= k+2;
            fi;
            
        od;

        if not found then

            # We add the monomial to `res'. However, we must still 
            # normalise the K-part...

            start:= [ ];
            k:= 1;
            while k < Length( m ) and not IsList( m[k] ) do
               Add( start, m[k] );
               Add( start, m[k+1] );
               k:= k+2;
            od;

            kbit:= [ ];
            while k < Length( m ) and IsList( m[k] ) do 
               Add( kbit, m[k] );
               Add( kbit, m[k+1] );
               k:= k+2;
            od; 

            tail:= [ ];
            while k < Length( m ) do
               Add( tail, m[k] );
               Add( tail, m[k+1] );
               k:= k+2;
            od;  

            k_normal:= [ [], qpar^0];
            ee:= [ ];
            k:= 1;
            while k < Length( kbit ) do

               rel:= binomial_with_cst( kbit[k][3], kbit[k+1] );
               if kbit[k][2] > 0 then 
                  rel:= QGPrivateFunctions.Multiply_K_Expr( rel );
               fi;
               if ee <> [ ] then
                  ee:= QGPrivateFunctions.Multiply_Exp_Exp( ee, rel );
               else
                  ee:= rel;
               fi;
               
               if k = Length(kbit)-1 or kbit[k][1] <> kbit[k+2][1] then
                  # add everything in `ee' to `k_normal',
                  # and start a new `ee':

                  qp:= qpar^( B[kbit[k][1]-s][kbit[k][1]-s]/2 );
                  store:= [ ];
                  for i in [1,3..Length(k_normal)-1] do
                      for j in [1,3..Length(ee)-1] do
                          mn:= ShallowCopy( k_normal[i] );
                          if ee[j] <> [ 0, 0 ] then
                             # Otherwise we multiply by one....
                             Add( mn, [ kbit[k][1], ee[j][1] ] );
                             Add( mn, ee[j][2] );
                          fi;
                          Add( store, mn );
                          Add( store, k_normal[i+1]*Value( ee[j+1], qp ) );
                      od;
                   od;
                   k_normal:= store;
                   ee:= [ ];
               fi;
               k:= k+2;
            od;   

            for k in [1,3..Length(k_normal)-1] do
                
                m:= ShallowCopy( start );
                Append( m, k_normal[k] );
                Append( m, tail );
                coef:= cf*k_normal[k+1];

                pos:= Position( res, m );
                if pos <> fail then
                   res[pos+1]:= res[pos+1]+coef;
                   if res[pos+1] = 0*coef then
                      Unbind( res[pos] ); Unbind( res[pos+1] );
                      res:= Filtered( res, x -> IsBound(x) );
                   fi;
                else    
                   Add( res, m );
                   Add( res, coef );
                fi;
            od; 
            
            Unbind( todo[1] );
            Unbind( todo[2] );
            todo:= Filtered( todo, x -> IsBound(x) );
        else
            
            # we know k1 > k2...
            
            if k1 > s+rank then
                
                # i.e., k1 is an E
                
                if k2 > s+rank then
                    
                    # i.e., k2 is also an E, commutation from Mtab
                    
                    if isgeneric then
                       r:= rts[k1-s-rank]*( B*rts[k2-s-rank]);                 
                       rel:= comm_rule( Mtab[k1][k2], m[k], m[k+2], 
                                    m[k+1], m[k+3], -r );
                    else
                       rel:= CollectQEAElement( ElementsFamily( 
                                    FamilyObj( fam!.genericQUEA ) ), 
                                    [ m{[k..k+3]}, _q^0 ] );
                       for i in [2,4..Length(rel)] do
                           rel[i]:= Value( rel[i], qpar );
                       od;
                    fi;

                    start:= m{[1..k-1]};
                    tail:= m{[k+4..Length(m)]};

                    for i in [1,3..Length(rel)-1] do
                        mn:= ShallowCopy( start );
                        Append( mn, rel[i] ); Append( mn, tail );
                        if i = 1 then
                            todo[1]:= mn;
                            todo[2]:= cf*rel[i+1];
                        else
                            Add( todo, mn ); Add( todo, cf*rel[i+1] );
                        fi;

                    od;
                    
                elif k2 > s then

                    # i.e., k2 is a K:
                    r:= -2*rts[k1-s-rank]*( B*sim[k2-s] )/B[k2-s][k2-s];
                    r:= r*m[k+1];
                    qp:= qpar^(B[k2-s][k2-s]/2);
                    coef:= qp^(r*m[k+2][2]);
                    mn:= m{[1..k-1]};
                    Add( mn, ShallowCopy(m[k+2]) );
                    mn[k][3]:= mn[k][3] + r; 
                    Add( mn, m[k+3] );
                    Add( mn, m[k] ); Add( mn, m[k+1] );
                    Append( mn,m{[k+4..Length(m)]} );
                    todo[1]:= mn;
                    todo[2]:= cf*coef;

                else
                    # k2 is an F, commutation from Mtab
                    
                    if isgeneric then
                       rel:= comm_rule( Mtab[k1][k2], m[k], m[k+2], 
                                     m[k+1], m[k+3], 0 );
                    else
                       rel:= CollectQEAElement( ElementsFamily( 
                                    FamilyObj( fam!.genericQUEA ) ), 
                                    [ m{[k..k+3]}, _q^0 ] );
                       for i in [2,4..Length(rel)] do
                           rel[i]:= Value( rel[i], qpar );
                       od;
                       # change the K-elements back to slightly strange form...
                       for j in [1,3..Length(rel)-1] do
                           for i in [1,3..Length(rel[j])-1] do
          
                    if IsList( rel[j][i] ) and Length( rel[j][i] ) = 2 then
                       Add( rel[j][i], 0 );
                    fi;
                            od;
                       od;
                    fi;
      
                    start:= m{[1..k-1]};
                    tail:= m{[k+4..Length(m)]};
                    
                    for i in [1,3..Length(rel)-1] do
                        mn:= ShallowCopy( start );
                        Append( mn, rel[i] ); Append( mn, tail );
                        if i = 1 then
                            todo[1]:= mn;
                            todo[2]:= cf;
                        else
                            Add( todo, mn ); Add( todo, cf*rel[i+1] );
                        fi;
                        
                    od;                        
                    
                fi;
            elif k1 > s then
                    
                # i.e., k1 is a K, 
                
                if k2 > s then
                    
                    # i.e., k2 is also a K; they commute
                    
                    mn:= m{[1..k-1]};
                    Add( mn, m[k+2] ); Add( mn, m[k+3] );
                    Add( mn, m[k] ); Add( mn, m[k+1] );
                    Append( mn,m{[k+4..Length(m)]} );
                    todo[1]:= mn;
                    todo[2]:= cf;
                else
                    
                    # i.e., k2 is an F:

                    r:= -2*rts[k2]*( B*sim[k1-s] )/B[k1-s][k1-s];
                    r:= r*m[k+3];
                    qp:= qpar^(B[k1-s][k1-s]/2);
                    coef:= qp^(r*m[k][2]);
                    mn:= m{[1..k-1]};
                    Add( mn, m[k+2] );
                    Add( mn, m[k+3] );
                    Add( mn, ShallowCopy(m[k]) );
                    mn[k+2][3]:= mn[k+2][3] + r; 
                    Add( mn, m[k+1] );
                    Append( mn,m{[k+4..Length(m)]} );
                    todo[1]:= mn;
                    todo[2]:= cf*coef;

                fi;
            else
                # i.e., k1, k2 are both F's. 
                # commutation from Mtab

                if isgeneric then

                   r:= rts[k1]*( B*rts[k2]);
                
                   rel:= comm_rule( Mtab[k1][k2], m[k], m[k+2], 
                                     m[k+1], m[k+3], -r );
                else
                   rel:= CollectQEAElement( ElementsFamily( 
                                 FamilyObj( fam!.genericQUEA ) ), 
                                    [ m{[k..k+3]}, _q^0 ] );
                   for i in [2,4..Length(rel)] do
                       rel[i]:= Value( rel[i], qpar );
                   od;
                fi;
                
                start:= m{[1..k-1]};
                tail:= m{[k+4..Length(m)]};
                        
                for i in [1,3..Length(rel)-1] do
                    mn:= ShallowCopy( start );
                    Append( mn, rel[i] ); Append( mn, tail );
                    if i = 1 then
                        todo[1]:= mn; todo[2]:= cf*rel[i+1];
                    else
                        Add( todo, mn ); Add( todo, cf*rel[i+1] );
                    fi;
                    
                od;
                
                
            fi;
            
        fi;
    od;

    return res;
end);


#############################################################################
##
#M  \*( <x>, <y> ) . . . . . . . . . . . . . . for two QEA elements
##
##
InstallMethod( \*,
        "for two QEA elements",
        IsIdenticalObj, [ IsQEAElement and IsPackedElementDefaultRep,
                IsQEAElement and IsPackedElementDefaultRep ], 0,
        function( x, y )
    
    local   ex,  ey,  expr,  i,  j,  m,  mons,  cfs,  len;    
   
    ex:= ExtRepOfObj(x);
    ey:= ExtRepOfObj(y);

    # We build the expression that needs to be collected.

    expr:= [ ];
    for i in [1,3..Length(ex)-1] do
        for j in [1,3..Length(ey)-1] do
            m:= ShallowCopy( ex[i] );
            Append( m, ey[j] );
            Add( expr, m );
            Add( expr, ex[i+1]*ey[j+1] );
        od;
    od;    
    
    # We collect it.
    
    expr:= CollectQEAElement( FamilyObj( x ), expr );
    
    mons:= [ ]; cfs:= [ ];
    for i in [1,3..Length(expr)-1] do
        if not IsZero( expr[i+1] ) then
           Add( mons, expr[i] ); Add( cfs, expr[i+1] );
        fi;
    od;

    # Sort everything, take equal things together, wrap it up and return.

    SortParallel( mons, cfs );

    expr:= [ ];
    len:= 0;
    for i in [1..Length( mons )] do
        if len > 0 and expr[len-1] = mons[i] then
            expr[len]:= expr[len]+cfs[i];
            if expr[len] = 0*expr[len] then
                Unbind( expr[len-1] ); Unbind( expr[len] );
                expr:= Filtered( expr, x -> IsBound(x) );
                len:= len-2;
            fi;

        else
            Add( expr, mons[i] ); Add( expr, cfs[i] );
            len:= len+2;
        fi;
    od;

    return ObjByExtRep( FamilyObj(x), expr );
end );

#########################################################################
##
#M  QuantizedUEA( <R> )
##
InstallMethod( QuantizedUEA,
        "for a root system",
        true, [ IsRootSystem ], 0,
        function( R )
    
    local   n,  rank,  B,  fam,  mm,  Ftab,  FEtab,  tt,  k,  rel,  i,  
            j,  qp,  ii,  gens,  A, normalise_rel;    

     # This function returns the quantized uea with respect to the root
     # system R. This algebra is generated by F1...Fn, K1, K1^-1,
     # ... Kr, Kr^-1, E1...En, where 
     #    Fk = T_{i_1}...T_{i_{k-1}}(F_{\alpha_{ik}})
     #    Ek = T_{i_1}...T_{i_{k-1}}(E_{\alpha_{ik}}),
     # where [ i_1,....,i_n ] is a redcued expression for the longest element
     # in the Weyl group. 

     # The elements are represented as elements of the Lusztig
     # Z-form of the quantized uea. The elements of this basis have the form
     #
     #   F1^(k1)...Fn^(kn) K1^d1 [K1;m1] ... Kr^dr [Kr;mr] E1^(p1)..En^(pn)
     #
     # where di=0,1 and [Ki;mi] is the "binomial"
     #
     #   / Ki; 0 \
     #   |       |
     #   \  mi   /
     #
     # Internally, such a monomial is represented as a list of indices
     # and exponents: the F-s have indices 1,..,n and the E-s have indices
     # n+r+1...2*n+r. Furthermore, an element Ki^di [Ki;mi] is represented as
     # .... , [ i, di ], mi .... So, for example the monomial 
     # F2^(3) K2 [ K2;4 ] E6^(8) (in type G2) is represented as 
     # [ 2, 3, [ 2, 1 ], 4, 14, 8 ].
     # Finally, a general element is represented as a list of monomials
     # and coefficients.

     normalise_rel:= function( s, rank, B, rel )

     # writes the relation rel using the generalised binomials in Lusztig's
     # Z-form of the quea.

     local add_elm, i, j, k, l, res, m, mon, e, f, ks, k_piece,
           new_piece, elm, ee, qp;

     add_elm:= function( ee, elm, cf )

          local pos;

          pos:= Position( ee, elm );
          if pos = fail then
             Add( ee, elm ); Add( ee, cf );
          else
             ee[pos+1]:= ee[pos+1]+ cf;
             if ee[pos+1]=0*ee[pos+1] then
                Unbind( ee[pos] ); Unbind( ee[pos+1] );
                ee:= Filtered( ee, x -> IsBound(x) );
             fi;
          fi;
          return ee;
     end;
      
     res:= [ ];
     for i in [1,3..Length(rel)-1] do
         m:= rel[i];
         k:= 1;
         f:= [ ];
         while k <= Length( m ) and m[k] <= s do 
            Add( f, m[k] );
            Add( f, m[k+1] );
            k:= k+2;
         od;
         ks:= [ ];
         while k <= Length( m ) and m[k] <= s+rank do 
            Add( ks, m[k] );
            Add( ks, m[k+1] );
            k:= k+2;
         od;
         e:= [ ];
         while k <= Length( m ) do
            Add( e, m[k] );
            Add( e, m[k+1] );
            k:= k+2;
         od;

         k_piece:= [ [], _q^0 ];
         for j in [1,3..Length(ks)-1] do
             if ks[j+1] > 0 then
                elm:= [ [1,0], _q^0 ];
                ee:= elm;
                for k in [2..ks[j+1]] do
                    ee:= QGPrivateFunctions.Multiply_Exp_Exp( ee, elm );
                od;
             else
                elm:= [ [1,0], _q^0, [0,1], _q^-1-_q ];
                ee:= elm;
                for k in [2..-ks[j+1]] do
                    ee:= QGPrivateFunctions.Multiply_Exp_Exp( ee, elm );
                od;
             fi;

             qp:= _q^( B[ks[j]-s][ks[j]-s]/2 );
             for k in [2,4..Length(ee)] do
                 ee[k]:= Value( ee[k], qp );
             od;
             new_piece:= [ ];
             for k in [1,3..Length(ee)-1] do
                 if ee[k] <> [ 0, 0 ] then 
                    m:= [ ks[j], ee[k][1] ];
                    for l in [1,3..Length(k_piece)-1] do 
                        mon:= ShallowCopy( k_piece[l] );
                        Add( mon, m );
                        Add( mon, ee[k][2] );
                        Add( new_piece, mon );
                        Add( new_piece, ee[k+1]*k_piece[l+1] );
                    od; 
                 else
                    # we multiply by a scalar, effectively
                    for l in [1,3..Length(k_piece)-1] do 
                        mon:= ShallowCopy( k_piece[l] );
                        Add( new_piece, mon );
                        Add( new_piece, ee[k+1]*k_piece[l+1] );
                    od; 
                 fi;
             od;
             k_piece:= new_piece;
         od;

         for j in [1,3..Length(k_piece)-1] do
             m:= ShallowCopy( f );
             Append( m, k_piece[j] );
             Append( m, e );
             res:= add_elm( res, m, k_piece[j+1]*rel[i+1] );
         od;

     od;
     return res;
    end;
    
    # First we produce the PBW generators of the quantized enveloping 
    # algebra corresponding to R. It mainly boils down to installing a 
    # lot of data in the family.

    n:= Length(PositiveRoots(R));
    rank:= Length( CartanMatrix(R) );
    B:= BilinearFormMatNF( R );
    
    fam:= NewFamily( "QEAEltFam", IsQEAElement );
    fam!.packedQEAElementDefaultType:=
                  NewType( fam, IsPackedElementDefaultRep );
    fam!.noPosRoots:= Length( PositiveRoots(R) );
    fam!.rank:= Length( CartanMatrix(R) );
    fam!.rootSystem:= R;
    
    mm:= QGPrivateFunctions.E_Tab( R );
    
    fam!.convexRoots:= mm[1]; # i.e., pos roots in convex order...
    
    Ftab:= QGPrivateFunctions.F_tab( R, mm[2], mm[1] );
    FEtab:= QGPrivateFunctions.FE_table( R, mm[2], Ftab, mm[1] );
    
    # `tt' will contain all commutation relations (Etab, Ftab, FEtab) 
    tt:= List([1..n], x -> [] );  
    for k in [1..n] do
        tt[k+rank+n]:= [];
    od;
    
    # We normalise the relations in the tables (by using 
    # E_a^{(k)} = E_a^n/[k]_a!
    
    for k in [1..Length(mm[2])] do
        rel:= List( mm[2][k][2], ShallowCopy );
        for i in [1,3..Length(rel)-1] do
            for j in [1,3..Length(rel[i])-1] do
                qp:= _q^( mm[1][ rel[i][j] ]*(B*mm[1][rel[i][j]])/2);
                rel[i][j]:= rel[i][j] + n + rank; 
                rel[i+1]:= rel[i+1]*
                           GaussianFactorial(rel[i][j+1],qp);
            od;
        od; 

        ii:= mm[2][k][1];
        tt[ii[1]+rank+n][ii[2]+rank+n]:= rel;
    od;
    
    for k in [1..Length(Ftab)] do
        rel:= List( Ftab[k][2], ShallowCopy );
        for i in [1,3..Length(rel)-1] do
            for j in [1,3..Length(rel[i])-1] do
                qp:= _q^( mm[1][ rel[i][j] ]*(B*mm[1][rel[i][j]])/2); 
                rel[i+1]:= rel[i+1]*
                           GaussianFactorial(rel[i][j+1],qp);
            od;
        od; 
        tt[Ftab[k][1][1]][Ftab[k][1][2]]:= rel;
    od;
    
    for k in [1..Length(FEtab)] do
        rel:= List( FEtab[k][2], ShallowCopy );
        for i in [1,3..Length(rel)-1] do
            for j in [1,3..Length(rel[i])-1] do
                if rel[i][j] <= n then
                    qp:= _q^( mm[1][ rel[i][j] ]*
                             (B*mm[1][rel[i][j]])/2); 
                    rel[i+1]:= rel[i+1]*
                               GaussianFactorial(rel[i][j+1],qp);
                fi;
                
                if rel[i][j] > n+rank then
                    qp:= _q^( mm[1][ rel[i][j]-n-rank ]*
                             (B*mm[1][rel[i][j]-n-rank])/2); 
                    rel[i+1]:= rel[i+1]*
                               GaussianFactorial(rel[i][j+1],qp);
                fi;                         
            od;
        od;                    
        tt[FEtab[k][1][1]][FEtab[k][1][2]]:= 
                    normalise_rel( n, rank, B, rel );
    od;              
        
    fam!.multTab:= tt;

    fam!.quantumPar:= _q;
    
    # Finally construct the generators.

    gens:= [ ];
    for i in [1..n] do
        gens[i]:= ObjByExtRep( fam, [ [ i, 1 ], _q^0 ] );
    od;
    for i in [1..Length( CartanMatrix(R) )] do
        Add( gens,  ObjByExtRep( fam, [ [ [ n+i, 1 ], 0 ], _q^0 ] ) );
        qp:= _q^(B[i][i]/2);
        
        # we need to sort the monomials in K^-1, to accomodate
        # for changes in the sorting algorithm, which may lead to
        # surprises otherwise...
        if [ [n+i,1], 0 ] < [ [ n+i, 0 ], 1 ] then
            Add( gens,  ObjByExtRep( fam, [ [ [ n+i, 1 ], 0 ], _q^0, 
                    [ [ n+i, 0 ], 1 ], qp^-1-qp ] ) );
        else
            Add( gens,  ObjByExtRep( fam, [ [ [ n+i, 0 ], 1 ], qp^-1-qp, 
                    [ [ n+i, 1 ], 0 ], _q^0 ] ) );
        fi;
        
    od;
    for i in [1..n] do
        Add( gens, ObjByExtRep( fam, 
                [ [ n+Length(CartanMatrix(R)) +i, 1 ], _q^0 ] ) );
    od;
    
    A:= Objectify( NewType( CollectionsFamily( FamilyObj( gens[1] ) ),
                                IsMagmaRingModuloRelations
                            and IsQuantumUEA
                            and IsGenericQUEA
                            and IsAttributeStoringRep ),
                   rec() );
    SetIsAssociative( A, true );
    SetLeftActingDomain( A, QuantumField );
    SetGeneratorsOfLeftOperatorRing( A, gens );
    SetGeneratorsOfLeftOperatorRingWithOne( A, gens );
    SetOne( A, gens[1]^0 );
    SetRootSystem( A, R );
    SetQuantumParameter( A, _q );

    # add a pointer to `A' to the family of the generators:
    fam!.qAlgebra:= A;
    
    return A;
end);

#########################################################################
##
#M  QuantizedUEA( <R> )
##
InstallOtherMethod( QuantizedUEA,
        "for a root system a ring, and a parameter",
        true, [ IsRootSystem, IsField, IsObject ], 0,
        function( R, F, v )
    
    local   n,  rank,  B,  fam,  tt,  tt_new,  k,  rel,  i,  
            j,  qp,  gens,  A,  uu;    


    n:= Length(PositiveRoots(R));
    rank:= Length( CartanMatrix(R) );
    B:= BilinearFormMatNF( R );
    
    fam:= NewFamily( "QEAEltFam", IsQEAElement );
    fam!.packedQEAElementDefaultType:=
                  NewType( fam, IsPackedElementDefaultRep );
    fam!.noPosRoots:= Length( PositiveRoots(R) );
    fam!.rank:= Length( CartanMatrix(R) );
    fam!.rootSystem:= R;
    
    uu:= QuantizedUEA( R );

    fam!.genericQUEA:= uu;

    tt:= ElementsFamily( FamilyObj( uu ) )!.multTab;
    # copy tt and substitute v for q:
    tt_new:= List([1..n], x -> [] );  
    for k in [1..n] do
        tt_new[k+rank+n]:= [];
    od;
    for i in [1..Length(tt)] do
        if IsBound( tt[i] ) then
           for j in [1..Length(tt[i]) ] do
               if IsBound( tt[i][j] ) then
                  rel:= List( tt[i][j], ShallowCopy );
                  for k in [2,4..Length(rel)] do
                      rel[k]:= Value( rel[k], v );
                  od;
                  tt_new[i][j]:= rel;
               fi;
           od;
        fi;
    od;

    fam!.multTab:= tt_new;

    # some more data:
    fam!.convexRoots:= ElementsFamily( FamilyObj( uu ) )!.convexRoots;
    fam!.quantumPar:= v;
 
    # Finally construct the generators.

    gens:= [ ];
    for i in [1..n] do
        gens[i]:= ObjByExtRep( fam, [ [ i, 1 ], v^0 ] );
    od;
    for i in [1..Length( CartanMatrix(R) )] do
        Add( gens,  ObjByExtRep( fam, [ [ [ n+i, 1 ], 0 ], v^0 ] ) );
        qp:= v^(B[i][i]/2);
        if IsZero( qp^-1-qp ) then
           Add( gens,  ObjByExtRep( fam, [ [ [ n+i, 1 ], 0 ], qp^0 ] ) );
       else
           # we need to sort the monomials in K^-1, to accomodate
            # for changes in the sorting algorithm, which may lead to
            # surprises otherwise...
           if [ [n+i,1], 0 ] < [ [ n+i, 0 ], 1 ] then
               Add( gens,  ObjByExtRep( fam, [ [ [ n+i, 1 ], 0 ], qp^0, 
                       [ [ n+i, 0 ], 1 ], qp^-1-qp ] ) );
           else
               Add( gens,  ObjByExtRep( fam, [ [ [ n+i, 0 ], 1 ], qp^-1-qp, 
                       [ [ n+i, 1 ], 0 ], qp^0 ] ) );
           fi;
        fi;
    od;
    for i in [1..n] do
        Add( gens, ObjByExtRep( fam, 
                [ [ n+Length(CartanMatrix(R)) +i, 1 ], v^0 ] ) );
    od;
    
    A:= Objectify( NewType( CollectionsFamily( FamilyObj( gens[1] ) ),
                                IsMagmaRingModuloRelations
                            and IsQuantumUEA
                            and IsAttributeStoringRep ),
                   rec() );
    SetIsAssociative( A, true );
    SetLeftActingDomain( A, F );
    SetGeneratorsOfLeftOperatorRing( A, gens );
    SetGeneratorsOfLeftOperatorRingWithOne( A, gens );
    SetOne( A, gens[1]^0 );
    SetRootSystem( A, R );
    SetQuantumParameter( A, v );
    
    # add a pointer to `A' to the family of the generators:
    fam!.qAlgebra:= A;
    
    return A;
end);

#########################################################################
##
#M  QuantizedUEA( <L> )
##
InstallOtherMethod( QuantizedUEA,
        "for a semisimple Lie algebra",
        true, [ IsLieAlgebra ], 0,
        function( L )
    return QuantizedUEA( RootSystem(L) );
end );

#########################################################################
##
#M  QuantizedUEA( <L> )
##
InstallOtherMethod( QuantizedUEA,
        "for a semisimple Lie algebra, a ring and a parameter",
        true, [ IsLieAlgebra, IsField, IsObject ], 0,
        function( L, F, qp )
    return QuantizedUEA( RootSystem(L), F, qp );
end );

############################################################################
##
#M  PrintObj( <QA> )
#M  ViewObj( <QA> )
##
InstallMethod( PrintObj,
        "for a QuantumUEA",
        true, [ IsQuantumUEA ], 0,
        
        function( A )
    
    Print("QuantumUEA( ",RootSystem(A),", Qpar = ",QuantumParameter(A)," )" );
end );

InstallMethod( ViewObj,
        "for a QuantumUEA",
        true, [ IsQuantumUEA ], 0,
        
        function( A )
    
    PrintObj( A );
end );

#############################################################################
##
#M  LeadingUEALatticeMonomial( <novar>, <f> )
##
##
InstallMethod( LeadingQEAMonomial,
        "for an integer and a QEA element",
        true, [ IS_INT, IsQEAElement ], 0,

        function ( novar, p )

    local e,max,cf,m,n,j,k,o,pos,deg,ind, degn;

    # Reverse lexicographical ordering...

    e:= p![1];
    max:= e[1];
    ind:= 1;
    cf:= e[2];
    m:= ListWithIdenticalEntries( novar, 0 );
    for k in [1,3..Length(max)-1] do
        m[max[k]]:= max[k+1];
    od;

    for k in [3,5..Length(e)-1] do

        n:= ListWithIdenticalEntries( novar, 0 );
        for j in [1,3..Length(e[k])-1] do
            n[e[k][j]]:= e[k][j+1];
        od;

        o:= n-m;
        
        # pos will be the last nonzero position
        pos:= PositionProperty( Reversed(o), x -> x <> 0 );
        pos:= novar-pos+1;
        if o[pos] > 0 then
            max:= e[k];
            ind := k;
            cf:= e[k+1];
            m:= n;
        fi;
    od;

    return [max, m, cf, ind];
end );


#############################################################################
##
#F  LeftReduceQEEALatticeElement( <novar>, <G>, <lms>, <lmtab>, <p> )
##
##
##
InstallGlobalFunction( LeftReduceQEAElement,
        function( novar, G, lms, lmtab, p )

    local   fam,  reduced,  rem,  res,  m1,  k,  g,  diff,  cme,  mon,  
            cflmg,  j,  fac,  fac1,  cf,  lm;

    # We left-reduce the QUEA element `p' modulo the elements in `G'.
    # Here `lms' is a list of leading monomial-indices; if the index `k'
    # occurs somewhere in `lms', then g![1][k] is the leading monomial
    # of `g', where `g' is the corresponding element of `G'. `novar'
    # is the number of variables.

    fam:= FamilyObj( p );
    reduced:= false;
    rem:= p;
    res:= 0*p;

    while rem <> 0*rem do

        m1:= LeadingQEAMonomial( novar, rem );
        k:= 1;
            
        k:= Search( lmtab, m1[2] );
        if k <> fail then
            
            g:= G[k];
            diff:= ShallowCopy( m1[2] );
            cme:= g![1];
            mon:= cme[ lms[k] ];
            cflmg:= cme[ lms[k]+1 ];
            for j in [1,3..Length(mon)-1] do
                diff[mon[j]]:= diff[mon[j]] - mon[j+1];
            od;

            fac:= [ ];
            for j in [1..novar] do
                if diff[j] <> 0 then
                    Add( fac, j ); Add( fac, diff[j] );
                fi;
            od;
            fac1:= ObjByExtRep( fam, [ fac, _q^0 ] )*g;
            cf:= LeadingQEAMonomial( novar, fac1 )[3];
            rem:= rem - (m1[3]/cf)*fac1;
            
        else
            lm:= ObjByExtRep( fam, [ m1[1], m1[3] ] );
            res:= res + lm;
            rem:= rem-lm; 
        fi;
        
            
    od;

    return res;

end );


QGPrivateFunctions.ActionCollect:=
        function( sim, rts, B, s, rank, Mtab, qpar, expr )

    # `sim' are the simple roots.
    # `rts' are the roots in convex order.
    # `B' is the matrix of the bilinear form.
    # `s' is the number of positive roots.
    # `rank' is the rank of the root system.
    # `Mtab' is the multiplication table.
    # `qpar' is the quantum parameter.
    # `expr' is the thing that needs to be collected.

    # Does the same as normal collection, except that monomials
    # ending on an E are immediately discarded, and K-elements are
    # not normalised...
    
    local   comm_rule,  todo,  res,  m,  cf,  k,  found,  pos,  k1,  
            k2,  r,  rel,  start,  tail,  i,  mn,  m1,  j, qp, coef, 
            list1, list2;

    comm_rule:= function( rel, j, i, m, n, r )
        
        # commutation rule for x_j^mx_i^n, where x_jx_i=qpar^rx_ix_j+rel
        
        local   rule,  l,  k,  cf,  u,  mn, start, tail, qi, qj, den, t;
        
        if j > s + rank then
            qj:= qpar^( rts[j-s-rank]*( B*rts[j-s-rank] )/2 );
        else
            qj:= qpar^( rts[j]*( B*rts[j] )/2 );
        fi;
        if i > s +rank then
            qi:= qpar^( rts[i-s-rank]*( B*rts[i-s-rank] )/2 );
        else
            qi:= qpar^( rts[i]*( B*rts[i] )/2 );
        fi;
        
        den:= GaussianFactorial( m, qj )*GaussianFactorial( n, qi );

        rule:= [ [ i, n, j, m], qpar^(n*m*r) ];
        for l in [0..n-1] do
            for k in [0..m-1] do
                cf:= qpar^((l*m+k)*r)/den;
                start:= [ ];
                if l <> 0 then
                    Add( start, i ); Add( start, l );
                    cf:= cf*GaussianFactorial( l, qi );
                fi;
                if m-1-k <> 0 then
                    Add( start, j ); Add( start, m-1-k );
                    cf:= cf*GaussianFactorial( m-1-k, qj );
                fi;
                tail:= [];
                if k <> 0 then
                    Add( tail, j ); Add( tail, k );
                    cf:= cf*GaussianFactorial( k, qj );
                fi;
                if n-1-l <> 0 then
                    Add( tail, i ); Add( tail, n-1-l );
                    cf:= cf*GaussianFactorial( n-1-l, qi );
                fi;

                for u in [1,3..Length(rel)-1] do
                    mn:= ShallowCopy( start );
                    Append( mn, rel[u] );
                    Append( mn, tail );
                    Add( rule, mn ); Add( rule, cf*rel[u+1] );
                od;
            od;
        od;

        return rule;
    end;
    
    # In the program we use ... [ i, d, a ], s ... for
    #
    #       / Ki; a \
    #  Ki^d |       |
    #       \   s   /
    #

    todo:= expr;

    for k in [1,3..Length(todo)-1] do
        for i in [1,3..Length(todo[k])-1] do
            if IsList( todo[k][i] ) and Length( todo[k][i] ) = 2 then
               todo[k][i]:= ShallowCopy( todo[k][i] );
               Add( todo[k][i], 0 );
            fi;
        od;
    od;

    res:= [ ];
    while todo <> [] do

        found:= false;
        while todo <> [] and not found do
           m:= todo[1];
           cf:= todo[2];
           if m = [ ] then
              found:= true;
              break;
           fi;
           k:= m[Length(m)-1];
           if IsList( k ) or k <= s then
              # m ends with K or F:
              found:= true;
           else
              Unbind( todo[1] ); Unbind( todo[2] );
              todo:= Filtered( todo, x -> IsBound(x) );
           fi;
        od;
        if todo = [ ] then break; fi;

        for i in [1,3..Length(m)-1] do
            if IsList( m[i] ) and Length( m[i] ) = 2 then
               m[i]:= ShallowCopy( m[i] );
               Add( m[i], 0 );
            fi;
        od;

        # We try to find indices in the `wrong' order.

        k:= 1; found:= false;
        while k < Length(m)-2 do

            if IsList( m[k] ) then
               k1:= m[k][1];
               list1:= true;
            else
               k1:= m[k];
               list1:= false;
            fi;
            if IsList( m[k+2] ) then
               k2:= m[k+2][1];
               list2:= true;
            else
               k2:= m[k+2];
               list2:= false;
            fi;

            if k1 > k2 then
                found:= true;
                break;
            elif k1 = k2 then

                if not list1 then
                
                   if m[k] <= s then
                       qp:= qpar^( rts[m[k]]*(B*rts[m[k]])/2 );
                       cf:= cf*GaussianBinomial( m[k+1]+m[k+3], m[k+1], qp );
                   fi;
                
                   if m[k] > s + rank then
                       qp:= qpar^( rts[m[k]-s-rank]*(B*rts[m[k]-s-rank])/2 );
                       cf:= cf*GaussianBinomial( m[k+1]+m[k+3], m[k+1], qp );
                   fi; 
                
                   m[k+1]:= m[k+1]+m[k+3];
                
                   if m[k+1] = 0*m[k+1] then
                       Unbind( m[k] ); Unbind( m[k+1] );
                       Unbind( m[k+2] ); Unbind( m[k+3] );
                       m:= Filtered( m, x -> IsBound(x) );
                       if k > 1 then
                        
                           # there is a new elt on pos k, we have to check
                           # whether it is in the correct order with 
                           # the previous element.
                           k:= k-2;
                       fi;
                   else
                       Unbind( m[k+2] ); Unbind( m[k+3] );
                       m:= Filtered( m, x -> IsBound(x) );
                   fi;
                else
                   # both are K-elements, coming from the same K_{\alpha}
                   # we do nothing (for the moment).
                   k:= k+2;
                fi;
            else
                k:= k+2;
            fi;
            
        od;

        if not found then

            # We add the monomial to `res'. 

            pos:= Position( res, m );
            if pos <> fail then
               res[pos+1]:= res[pos+1]+cf;
               if res[pos+1] = 0*cf then
                  Unbind( res[pos] ); Unbind( res[pos+1] );
                  res:= Filtered( res, x -> IsBound(x) );
               fi;
            else    
               Add( res, m );
               Add( res, cf );
            fi;

            Unbind( todo[1] );
            Unbind( todo[2] );
            todo:= Filtered( todo, x -> IsBound(x) );
        else
            
            # we know k1 > k2...
            
            if k1 > s+rank then
                
                # i.e., k1 is an E
                
                if k2 > s+rank then
                    
                    # i.e., k2 is also an E, commutation from Mtab
                    
                    r:= rts[k1-s-rank]*( B*rts[k2-s-rank]);
                    
                    rel:= comm_rule( Mtab[k1][k2], m[k], m[k+2], 
                                  m[k+1], m[k+3], -r );
                    start:= m{[1..k-1]};
                    tail:= m{[k+4..Length(m)]};

                    for i in [1,3..Length(rel)-1] do
                        mn:= ShallowCopy( start );
                        Append( mn, rel[i] ); Append( mn, tail );
                        if i = 1 then
                            todo[1]:= mn;
                            todo[2]:= cf*rel[i+1];
                        else
                            Add( todo, mn ); Add( todo, cf*rel[i+1] );
                        fi;

                    od;
                    
                elif k2 > s then

                    # i.e., k2 is a K:
                    r:= -2*rts[k1-s-rank]*( B*sim[k2-s] )/B[k2-s][k2-s];
                    r:= r*m[k+1];
                    qp:= qpar^(B[k2-s][k2-s]/2);
                    coef:= qp^(r*m[k+2][2]);
                    mn:= m{[1..k-1]};
                    Add( mn, ShallowCopy(m[k+2]) );
                    mn[k][3]:= mn[k][3] + r; 
                    Add( mn, m[k+3] );
                    Add( mn, m[k] ); Add( mn, m[k+1] );
                    Append( mn,m{[k+4..Length(m)]} );
                    todo[1]:= mn;
                    todo[2]:= cf*coef;

                else
                    # k2 is an F, commutation from Mtab
                    
                    rel:= comm_rule( Mtab[k1][k2], m[k], m[k+2], 
                                  m[k+1], m[k+3], 0 );
                    start:= m{[1..k-1]};
                    tail:= m{[k+4..Length(m)]};
                    
                    for i in [1,3..Length(rel)-1] do
                        mn:= ShallowCopy( start );
                        Append( mn, rel[i] ); Append( mn, tail );
                        if i = 1 then
                            todo[1]:= mn;
                            todo[2]:= cf;
                        else
                            Add( todo, mn ); Add( todo, cf*rel[i+1] );
                        fi;
                        
                    od;                        
                    
                fi;
            elif k1 > s then
                    
                # i.e., k1 is a K, 
                
                if k2 > s then
                    
                    # i.e., k2 is also a K; they commute
                    
                    mn:= m{[1..k-1]};
                    Add( mn, m[k+2] ); Add( mn, m[k+3] );
                    Add( mn, m[k] ); Add( mn, m[k+1] );
                    Append( mn,m{[k+4..Length(m)]} );
                    todo[1]:= mn;
                    todo[2]:= cf;
                else
                    
                    # i.e., k2 is an F:

                    r:= -2*rts[k2]*( B*sim[k1-s] )/B[k1-s][k1-s];
                    r:= r*m[k+3];
                    qp:= qpar^(B[k1-s][k1-s]/2);
                    coef:= qp^(r*m[k][2]);
                    mn:= m{[1..k-1]};
                    Add( mn, m[k+2] );
                    Add( mn, m[k+3] );
                    Add( mn, ShallowCopy(m[k]) );
                    mn[k+2][3]:= mn[k+2][3] + r; 
                    Add( mn, m[k+1] );
                    Append( mn,m{[k+4..Length(m)]} );
                    todo[1]:= mn;
                    todo[2]:= cf*coef;

                fi;
            else
                # i.e., k1, k2 are both F's. 
                # commutation from Mtab
                
                r:= rts[k1]*( B*rts[k2]);
                
                rel:= comm_rule( Mtab[k1][k2], m[k], m[k+2], 
                                  m[k+1], m[k+3], -r );
                start:= m{[1..k-1]};
                tail:= m{[k+4..Length(m)]};
                        
                for i in [1,3..Length(rel)-1] do
                    mn:= ShallowCopy( start );
                    Append( mn, rel[i] ); Append( mn, tail );
                    if i = 1 then
                        todo[1]:= mn; todo[2]:= cf*rel[i+1];
                    else
                        Add( todo, mn ); Add( todo, cf*rel[i+1] );
                    fi;
                    
                od;
                
                
            fi;
            
        fi;
    od;

    return res;
end;


QGPrivateFunctions.Calc_Image:=  function( qpar, x, v )

        # a function for calculating the image of v under x. This is used
        # at the end for calculating a list of images of the generators,
        # if the dimension of the module is not too big. 
        # If the dimension is higher,
        # then this function is also called from the method for \^.

         local   rank,  s,  hw,  ev,  qelm,  ee,  eres,  k,  i,  cf,  pos,  
                 gb,  p,  wvecs,  mons,  cfts,  ep,  im, B, ind, qp,
                 ex, fam, R, m, j;
    
         if IsZero( v ) then return v; fi;
         rank:= FamilyObj( x )!.rank;
         s:= FamilyObj( x )!.noPosRoots;
         hw:= FamilyObj( v )!.highestWeight;
    
         B:= BilinearFormMatNF( FamilyObj( x )!.rootSystem );
    
         # qelm will be x*v1, where v1 is the corresponding element in U^-
         # (corresponding to v). We reduce this element modulo the Groebner
         # basis.
         ev:= ExtRepOfObj( v );    
         qelm:= Sum( [1,3..Length(ev)-1], ii -> ev[ii+1]*ev[ii][2] );
         qelm:= ExtRepOfObj( qelm );
         ex:= ExtRepOfObj( x );

         # We build the expression that needs to be collected.

         ee:= [ ];
         for i in [1,3..Length(ex)-1] do
            for j in [1,3..Length(qelm)-1] do
               m:= ShallowCopy( ex[i] );
               Append( m, qelm[j] );
               Add( ee, m );
               Add( ee, ex[i+1]*qelm[j+1] );
           od;
         od;    

         fam:= FamilyObj(x);
         R:= fam!.rootSystem;

         # We collect it.

         ee:= QGPrivateFunctions.ActionCollect(SimpleSystemNF(R), 
                   fam!.convexRoots, 
                   BilinearFormMatNF(R), fam!.noPosRoots, fam!.rank, 
                   fam!.multTab, fam!.quantumPar, ee );

         eres:= [ ];
         for k in [1,3..Length(ee)-1] do
        
            if ee[k] = [] then
            
               # i.e., the monomial is 1, can go straight to eres
               pos:= Position( eres, ee[k] );
               if pos = fail then
                  Add( eres, ee[k] ); Add( eres, ee[k+1] );
               else
                  eres[pos+1]:= eres[pos+1]+ee[k+1];
--> --------------------

--> maximum size reached

--> --------------------

[ Dauer der Verarbeitung: 0.91 Sekunden  (vorverarbeitet)  ]