# PROCEDURES FOR CALCULATING THE RING OF INVARIANTS

# Written by Gregor Kemper, 
# email kemper@kalliope.iwr.uni-heidelberg.de
# Version 2 (June 1993)

# This script defines the invar-package containing a bunch of procedures
# and some permutation groups


macro(alglinsolve=invar['alglinsolve'],aver_orb=invar['aver_orb'],
      showbasis=invar['showbasis'],check_nullspace=invar['check_nullspace'],
      classes=invar['classes'],derive=invar['derive'],
      elements=invar['elements'],elesym=invar['elesym'],
      generators=invar['generators'],inv=invar['inv'],
      invarspace=invar['invarspace'],invring=invar['invring'],
      lingroup=invar['lingroup'],mklingroup=invar['mklingroup'],
      monoms=invar['monoms'],monoms_base=invar['monoms_base'],
      mul=invar['mul'],onesoff=invar['onesoff'],out=invar['out'],
      poincare=invar['poincare'],poinrep=invar['poinrep'],
      showprims=invar['showprims'],primaries=invar['primaries'],
      proj=invar['proj'],reci=invar['reci'],reduce=invar['reduce'],
      showrels=invar['showrels'],relations=invar['relations'],
      renormal=invar['renormal'],repfrac=invar['repfrac'],
      represent=invar['represent'],showseconds=invar['showseconds'],
      secondaries=invar['secondaries'],throw=invar['throw'],
      valueof=invar['valueof']);


# INVRING
# The master-routine of the package which does everything and calls all
# the other routines.
# Ex.: known:=invring(permgroup({[[1,2],[3,4]]}));
# or   known:=invring(lingroup({[[a,0],[0,-a]],[[0,1],[1,0]]},a**2+1),inter);
# Include 'norels' in flags if you don't want the relations.
# Include 'inter' in flags if you want to have the possibility to interfere if
# difficulties arise when searching good primary generators!
# Set global variable protocol_level 0 or -1 if you want less or no protocol!

invring:=proc(G,flags,Known)
    local n,i,x,y,t,known,Inter,Norels,T;
    global _known;
    
    if not ( type(G,function) and
    ( op(0,G)=permgroup or
      op(0,G)=` lingroup` )) then
        ERROR(`Wrong argument type!`)
    fi;
    if op(0,G)=permgroup then readlib(group); permgroup(op(G)) fi;
    if nargs>=3 then Known:=table(); known:=Known
    else 
        if assigned(_known) then 
            if type(_known,table) then print(`Warning: _known is overwritten!`)
            else ERROR(`_known should be unassigned or a table!`)
            fi
        fi;
        _known:=table(); known:=_known
    fi;
    if op(0,G)=permgroup then known[group][permgroup]:=[op(G)]; n:=op(1,G)
    else
        known[group][geners]:=op(1,G);
        n:=nops(op(1,G)[1]);
        if nops(G)>=2 then known[group][minpol]:=op(2,G) fi
    fi;
    Inter:=0;
    if nargs>=2 then
        if has(flags,'inter') then Inter:='inter' fi;
        if has(flags,'norels') then Norels:='norels' fi
    fi;
    
    out(); out(`CALCULATING THE INVARIANT RING OF`);
    out(`   G`=G); out(); out();
    
    T:=time();
    
    for i from 1 to n do
        if assigned(x.i) then ERROR(``.(evaln(x.i)).
                    ` must be an unassigned name!`) fi;
        if assigned(y.i) then ERROR(``.(evaln(y.i)).
                    ` must be an unassigned name!`) fi;
        if assigned(s.i) then ERROR(``.(evaln(s.i)).
                    ` must be an unassigned name!`) fi
    od;
    known[xvars]:=[x.(1..n)];
    if assigned(s) then ERROR(`s must be an unassigned name!`) fi;
    known[svar]:=s;
    onesoff(s,known);
    primaries(",Inter,known);
    secondaries(",t,known);
    if Norels<>'norels' then
        relations(known)
        else out(`Calculation of relations not desired.`); out()
    fi;
    out(`Time used:`,time()-T,'seconds'); out();
    RETURN()
end:


# LINGROUP
# check if a linear group is well defined

lingroup:=proc(gens,minpol)
    local n,ele,row,alg;
    
    if nargs>=2 then
        indets(minpol);
        if nops(")<>1 then ERROR(`Bad second argument!`) fi;
        alg:="[1];
        if not type(minpol,polynom(rational,alg)) then
                                ERROR(`Bad second argument!`) fi
    fi;
    if ( not type(gens,set(listlist)) or nops(gens)=0 )then
                                    ERROR(`Bad first argument!`) fi;
    n:=nops(gens[1]);
    for ele in gens do
        if nops(ele)<>n then ERROR(`Bad first argument!`) fi;
        for row in ele do
            if nops(row)<>n then ERROR(`Bad first argument!`) fi
        od;
        if not type(ele,list(list(polynom(rational,alg)))) then
                                        ERROR(`Bad first argument!`) fi
    od;
    '` lingroup`(args)'
end:
    

# OUT
# Output of a piece of protocol

out:=proc()
    local lev,narg,pl;
    
    if nargs>0 and type(args[1],integer) then
        lev:=args[1]; narg:=[args[2..nargs]]
    else lev:=1; narg:=[args]
    fi;
    if assigned(protocol_level) then pl:=protocol_level else pl:=1 fi;
    if pl<lev then RETURN() fi;
    if nops(narg)>0 and narg[1]=print then print(narg[2..nops(narg)])
    else lprint(narg[])
    fi;
    RETURN()
end:


# MKLINGROUP
# If G is given as a permutation group, it is transformed into a matrix-group,
# matrices being (here) lists of lists.

mklingroup:=proc(G)
local n,g,conv;
    
    conv:=proc(p,n)
        local i,pl;
        pl:=convert(p,permlist,n);
        array(sparse,1..n,1..n,[seq((pl[i],i)=1, i=1..n)]);
        convert(",listlist)
    end;
    
    if nargs>=1 then g:=G else g:=_known[group] fi;
    if not type(g,table) then ERROR(`Wrong argument-type!`) fi;
    if not assigned(g[geners]) then
        if type(g[permgroup],function) then g[permgroup]:=[op(g[permgroup])]
        elif type(g[permgroup],list) then
        else ERROR(`G[permgroup] is not what it should be!`)
        fi;
        n:=g[permgroup][1]; readlib(group);
        g[geners]:=(map(conv,g[permgroup][2],n));
        if assigned(g[eles]) then g[eles]:=map(conv,g[eles],n) fi
    elif assigned(g[degree]) and assigned(g[identity]) then RETURN()
    else n:=nops(g[geners][1])
    fi;
    g[degree]:=n; g[identity]:=convert(array(identity,1..n,1..n),listlist);
    if assigned(g[minpol]) then g[alg]:=indets(g[minpol])[] fi;
    RETURN()
end:


# REDUCE
# reduce a polynomial in an algebraic number

reduce:=proc(expr,G)
    if nargs>=2 then G else _known[group] fi;
    rem(expr,"[minpol],"[alg])
end:


# MUL
# a times b; they might contain an algebaic number!

mul:=proc(a,b,minpol)
    local i,j,k,n,res;
    option remember;
    
    n:=nops(a);
    res:=[seq([seq(expand(sum('a[i][k]*b[k][j]', k=1..n)), j=1..n)], i=1..n)];
    if nargs>=3 then
        indets(minpol)[];
        if has(res,") then res:=[seq(map(rem,res[i],minpol,"), i=1..n)] fi
    fi;
    RETURN(res)
end:


# ELEMENTS
# assigns to G[eles] a set of all elements of G.

elements:=proc(G)
    local x,g,mipo;
    
    if nargs>=1 then g:=G else g:=_known[group] fi;
    if assigned(g[eles]) then RETURN() fi;
    out(`Calculating all elements of the group G ...`);
    if assigned(g[permgroup]) then                  # can take a shortcut!
        if type(g[permgroup],function) then
                g[permgroup]:=[op(g[permgroup])] fi;
        readlib(group);
        g[eles]:=group[cosets](permgroup(g[permgroup][]),
                        permgroup(op(1,g[permgroup]),{}))
    fi;
    mklingroup(g);
    if assigned(g[minpol]) then mipo:=g[minpol] else mipo:=NULL fi;
    if not assigned(g[eles]) then
        g[eles]:={g[identity]}; g[order]:=0;
    
        while g[order]<nops(g[eles]) do
            g[order]:=nops(g[eles]);
            for x in g[geners] do
                g[eles]:=g[eles] union map(mul,g[eles],x,mipo)
            od
        od
    else g[order]:=nops(g[eles])
    fi;
    out(`It has `.(g[order]).` elements.`); out();
    out(2,`They are`); out(2,print,map(array,g[eles])[]);
    RETURN()
end:


# RECI
# 1/x, where x is a polynomial in an algebraic number

reci:=proc(x,minpol)
    local alg,ret;
    option remember;
    
    if nargs<2 then RETURN(1/x) fi;
    alg:=indets(minpol)[];
    if not has(x,alg) then RETURN(1/x) fi;
    if gcdex(x,minpol,alg,'ret')<>1 then ERROR(`Zero-division`) fi;
    ret
end:


# INV
# The inverse of the matrix x

inv:=proc(x,G)
    local g,res,A;
    
    if nargs>=2 then g:=G else g:=_known[group] fi;
    if assigned(g[inverses][x]) then g[inverses][x]
    else
        A:=array(x);
        res:=linalg[inverse](A);
        if has(eval(res),g[alg]) then
            linalg[det](A);
            "*reci(",g[minpol]);
            res:=map((x,d,g)->reduce(normal(d*x),g),res,",g)
        fi;
        g[inverses][x]:=convert(res,listlist)
    fi
end:    


# CLASSES
# calculates the conjugacy-classes of G and writes them into G[ccls],
# returns an element of each class.

classes:=proc(G)
    local still,res,ladder,class,g,mipo;
    
    if nargs>=1 then g:=G else g:=_known[group] fi;
    if not assigned(g[ccls]) then
        elements(g);
        out(`Calculating the conjugacy classes of G ...`);
        if assigned(g[minpol]) then mipo:=[g[minpol]] else mipo:=[] fi;
        still:=g[eles]; res:=NULL;
        ladder:=map((x,g)->[inv(x,g),x],g[eles],g);
        while still<>{} do
            class:=map((a,x,m)->mul(a[1],mul(x,a[2],m[]),m[]),
                                        ladder,still[1],mipo);
            res:=res,class;
            still:=still minus class
        od;
        g[ccls]:=sort([res],(a,b)->evalb(nops(a)<nops(b)));
        g[classlengths]:=map(nops,g[ccls]);
        out(`G has conjugacy classes of lengths`,g[classlengths]); out();
        out(2,`They are`);
        for class in g[ccls] do out(2,print,map(array,class)[]) od
    fi;
    map(x->x[1],g[ccls])
end:


# THROW
# applies a matrix to an expression in the variables 'vars'.
# CAVEAT: In literature, throwing a linear map s to a polynomial functoin f is 
# often defined as: sf(x):=f(s^(-1)(x)). Here, we take the transpose s^t and 
# define sf(x):=f(s^t(x)). These are isomorphic ways to have a linear group 
# operate on the space of polynomial functions.

throw:=proc(expr,mat,o_and_n_vars,Known)
    local ex,i,j,ovars,nvars,known,mipo,alg;
    
    if nargs<4 or type(Known,table) then
        if nargs>=4 then known:=Known else known:=_known fi;
        if nargs>=3 and type(o_and_n_vars,list) then
            ovars:=o_and_n_vars[1]; nvars:=o_and_n_vars[2]
        else ovars:=known[vars]; nvars:=ovars
        fi;
        if assigned(known[group][minpol]) then known[group][minpol] else 0 fi;
        RETURN(throw(expr,mat,[ovars,nvars],"))
    fi;
    ex:=expand(expr);
    mipo:=Known; if "<>0 then alg:=indets(")[] fi;
    if not has([ex,mat],alg) then
        ovars:=o_and_n_vars[1]; nvars:=o_and_n_vars[2];
        expand(subs([seq(ovars[i]=sum('mat[j][i]*nvars[j]', j=1..nops(nvars)), 
                                                        i=1..nops(ovars))],ex))
    elif type(ex,`+`) then map(throw,ex,args[2..4])
    elif type(ex,`*`) then
        throw(convert([op(2..nops(ex),ex)],`*`),args[2..4]);
        expand(throw(op(1,ex),args[2..4])*");
        if has(",alg) then expand(rem(",mipo,alg)) fi;
        "
    elif type(ex,`^`) then
        throw(op(1,ex)^(op(2,ex)-1),args[2..4]);
        expand(throw(op(1,ex),args[2..4])*");
        if has(",alg) then expand(rem(",mipo,alg)) fi;
        "
    else
        ovars:=o_and_n_vars[1]; nvars:=o_and_n_vars[2];
        subs([seq(ovars[i]=sum('mat[j][i]*nvars[j]', j=1..nops(nvars)), 
                                                    i=1..nops(ovars))],ex)
    fi;
    
    throw(args):="
end:


# AVER_ORB
# ('char'^(-1))-weighted sum of s out of known[group] applied to 'expr'.
# This is a pseudo-invriant of weight 'char'!

aver_orb:=proc(expr,char,Known)
    local chi,i,j,known;
    
    if nargs>=3 then known:=Known else known:=_known fi;
    if nargs>=2 then chi:=char else chi:=1 fi;
    if type(chi,list) then
        classes(known[group]);
        sum('chi[i]*sum('throw(expr,
            inv(known[group][ccls][i][j],known[group]),0,known)',
            j=1..known[group][classlengths][i])',
            i=1..nops(known[group][ccls]))
    else
        elements(known[group]);
        sum('throw(expr,inv(known[group][eles][i],known[group]),0,known)',
                                                    i=1..known[group][order])
    fi;
    expand(");
    if "<>0 then "/icontent(") fi;
    "
end:


# INVARSPACE
# A basis for the subspace of all pseudo-invariants of weight 'char'
# (default 1) and degree 'deg'. If the group comes from a permgroup, the
# projection of the deg'th elementary symmetric polynomial (if nonzero) is
# taken as first basis polynomial.
# 'char' can be either a set or list of equations relating the generators of
# the group to the character-values, or a list of character-values
# corresponding to the conjugacy classes.

invarspace:=proc(deg,char,Known)
    local i,n,alfa,ans,unknowns,pars,chi,res,I,mons,eq,known;
    
    if nargs>=3 then known:=Known else known:=_known fi;
    mklingroup(known[group]);
    
    if nargs>=2 and char<>1 then
        if type(char,{list(`=`),set(`=`)}) and 
        {map(lhs,char)[]}=known[group][geners] then chi:=char
        elif type(char,list(polynom(rational,known[group][alg]))) then
            proc(mat,known)
                local k;
                member(true,map((c,mat)->member(mat,c),
                            known[group][ccls],mat),k);
                k
            end;
            chi:=map((mat,char,pro,known)->(mat=char[pro(mat,known)]),
                                known[group][geners],char,",known)
        else ERROR(`Third argument is not what it should be!`)
        fi
    else chi:=map(x->(x=1),known[group][geners])
    fi;
    if assigned(known[group][invspace][deg,chi]) then
        RETURN(known[group][invspace][deg,chi])
    fi;
    
    if nargs>=2 and char<>1 then `pseudo-` else `` fi;
    out(`Calculating the space of `.".`invariants of degree `.deg.`.`);
    
    mons:=monoms(deg,map(x->[x,1],known[vars]));
    if known[trans][permgroup]=true then
        # smuggle in an elementary symmetric polynomial!
        proj(elesym(deg,known[xvars]),known);
        if "<>0 then
            coeffs(",known[vars],'res');
            mons:=[["",deg],op({mons[]} minus {[op(1,[res]),deg]})]
        fi
    fi;
    ans:=sum('alfa[i]*mons[i][1]', i=1..nops(mons));
    unknowns:={seq(alfa[i], i=1..nops(mons))};
    # Condition for ans being an invariant
    eq:=map((mat,expr,chi,known)->coeffs(expand(throw(expr,mat,0,known)-
        subs(chi,mat)*expr),known[vars]),
        known[group][geners],ans,chi,known);
    out(2,`Solving a system of `.(nops(")).
            ` linear equations in `.(nops(unknowns)).` unknowns ...`);
    if has(eq,known[group][alg]) then
        alglinsolve(eq,unknowns,known[group][minpol])
    else
        readlib(`solve/linear`)(eq,unknowns)
    fi;
    
    ans:=expand(subs(",ans));
    pars:=indets(ans) intersect unknowns;
    if member(alfa[1],pars) then pars:=[alfa[1],op(pars minus {alfa[1]})]
                            # true if an elementary symmetric was smuggled in!
    else pars:=[pars[]]
    fi;
    n:=nops(pars);
    
    [seq(subs(pars[i]=1,map(x->(x=0),pars),ans), i=1..n)];
    res:=map(f->f/icontent(f),");
    for i from 1 to n do
        if assigned(I.deg._.i) then
            ERROR(``.(evaln(I.deg._.i)).` must be an unassigned name!`)
        fi
    od;
    if n=0 then `is none!`
    elif n=1 then `is one linearly independent invariant ("I`.deg.`_1").`
    else `are `.n.` linearly independent invariants ("I`.deg.
                                    `_1" through "I`.deg._.n.`").`
    fi;
    out(`There `.");
    if n>0 then
        if n>1 then out(2,`They are`) else out(2,`It is`) fi;
        for i from 1 to n do out(2,`   I`.deg._.i=res[i]) od
    fi;
    out();
    known[group][invspace][deg,chi]:=res
end:


# MONOMS
# all products of elements of 'polys_with_degrees' of degree 'deg'

monoms:=proc(deg,polys_with_degrees)
    local d,f,res,factor,reduced,pwd;
    option remember;
    
    if nargs>=2 then pwd:=polys_with_degrees
    else pwd:=map(x->[x,1],_known[vars])
    fi;
    if nops(pwd)=0 then
        if deg=0 then RETURN([[1,0]])
                 else RETURN([])
        fi
    fi;

    f:=pwd[1];
    reduced:=[pwd[2..nops(pwd)]];
    if f[2]=0 then RETURN(monoms(deg,reduced)) fi;    
                                                    # avoiding infinite loop!
    res:=NULL; d:=0; factor:=1;

    while d<=deg do
        monoms(deg-d,reduced);                      # recursion!!
        res:=res,map((f,fac,deg)->[fac*f[1],deg],",factor,deg)[];
        d:=d+f[2]; factor:=factor*f[1]
    od;
    [res]
end:


# MONOMS_BASE
# a Q-basis for the subspace of polynomials of degree 'deg' in the 
# free module with basis 'base' over the polynomial ring generated 
# by 'indep'

monoms_base:=proc(deg,indep,base)
    local f,res;
    option remember;

    res:=NULL;
    for f in base do
        deg-f[2];       
        if ">=0 then 
            monoms(",indep);
        res:=res,map((f,fac,deg)->[f[1]*fac,deg],",f[1],deg)[]
        fi
    od;
    [res]
end:


# ELESYM
# the 'k'-th elementary symmetric function in variables 'var'

elesym:=proc(k,Var)
    local n,i,res,var;
    
    if nargs>=2 then var:=Var else var:=_known[xvars] fi;
    if k=0 then RETURN(1) fi;
    res:=0; n:=nops(var);
    for i from 1 to n-k+1 do
        res:=res+var[i]*elesym(k-1,[var[i+1..n]])
    od;
    expand(res)
end:


# ONESOFF
# splits off unit-characters from known[group]

onesoff:=proc(s_name,Known)
    local i,k,n,d,pr,copr,S,T,bc,sname,s,G,select_base, known;
    
    select_base:=proc(A,d,known)
        local S,i,j,k,x,n,cand;
        n:=known[group][degree]; i:=0;
        for j while i<d do
            cand:=linalg[submatrix](A,1..n,[j]);
            icontent(sum('cand[k,1]*x[k]', k=1..n));
            if "<>0 then map((v,d)->v/d,cand,") else cand fi;
            if assigned(S) then linalg[concat](S,") else " fi;
            if linalg[kernel](subs(known[group][alg]=
                                RootOf(known[group][minpol]),"))={} then
                S:=";
                i:=i+1
            fi
        od;
        op(S)
    end;
    
    if nargs>=2 then known:=Known else known:=_known fi;
    if nargs>=1 then sname:=s_name else sname:=s fi;
    elements(known[group]);
    known[trans][permgroup]:=assigned(known[group][permgroup]);
    out(0,`Splitting off unit-characters ...`);
    
    n:=known[group][degree];
    pr:=evalm(convert(map(array,known[group][eles]),`+`));
    # |G| times a G-invariant projection onto the space fixed by G
    
    d:=linalg[trace](pr)/known[group][order];       # dimension of fixed space
    known[prim]:=[seq([``.sname.i,1,'outer'], i=1..d)];
    known[values]:=NULL;
    
    if d=0 then
        known[vars]:=known[xvars];
        known[trans][newbasis]:=known[group][identity];
        known[trans][proj]:=";
        out(`There is none.`); out();
        RETURN([sname,0])
    fi;
    
    copr:=evalm(known[group][order]-pr); # |G| times complementary projection
    
    # Now choose independent columns to form transition matrix
    d:=n-d;
    S:=linalg[concat](select_base(copr,d,known),select_base(pr,n-d,known));
    T:=array(inv(convert(S,listlist),known[group]));
    
    out(); out(`"Outer" primary generators:`);
    for i from 1 to n-d do
        sum('S[k,d+i]*known[xvars][k]', k=1..n);
        known[values]:=known[values],``.sname.i=";
        out(`   `.sname.i="")
    od;
    out();
    
    S:=linalg[submatrix](S,1..n,1..d);
    T:=linalg[submatrix](T,1..d,1..n);
    known[trans][newbasis]:=convert(S,listlist);
    known[trans][proj]:=convert(T,listlist);
    
    # make new linear group
    bc:=proc(m,G,s,t)
        evalm(t&*array(m)&*s);
        if has(",G[alg]) then map(reduce,",G) fi;
        convert(",listlist)
    end;
    
    if assigned(known[group][minpol]) then
        G[minpol]:=known[group][minpol]; G[alg]:=known[group][alg]
    fi;
    G[geners]:=map(bc,known[group][geners],G,S,T);
    G[eles]:=map(bc,known[group][eles],G,S,T);
    G[order]:=known[group][order];
    mklingroup(G);
    known[group]:=G;
    
    known[vars]:=[y.(1..d)];
    out(`New variables:`);
    for i to d do
        known[vars][i];
        out(`   `."=renormal(",known))
    od;
    out(2); out(2,`Action of G on new variables generated by`);
    out(2,print,map(array,known[group][geners])[]);
    out(`Done with splitting off unit-characters!`); out();
    RETURN([sname,n-d])
end:


# PROJ
# projects 'expr' to the non-1-subspace and expresses it in the y-variables.

proj:=proc(expr,Known)
    local known;
    
    if nargs>=2 then known:=Known else known:=_known fi;
    throw(expr,known[trans][proj],[known[xvars],known[vars]],known)
end:
    

# RENORMAL
# expresses 'expr' in the x-variables.

renormal:=proc(expr,Known)
    local known;
    
    if nargs>=2 then known:=Known else known:=_known fi;
    throw(expr,known[trans][newbasis],[known[vars],known[xvars]],known)
end:


# POINCARE
# The "weighted poincare-series" of G with character 'char' (default 1);
# 'char' given as a list corresponding to G[ccls].

poincare:=proc(char,svar,G)
    local chi,S,i,g,mipo;
    
    if nargs>=3 then g:=G else g:=_known[group] fi;
    if nargs>=1 then chi:=char else chi:=1 fi;
    if type(chi,list) then classes(g)
    else elements(g)
    fi;
    if nargs>=2 and type(svar,name) then S:=svar else S:='s' fi;
    if assigned(g[poinseries][chi]) then
        g[poinseries][chi];
        RETURN(subs(indets(")[1]=S,"))
    fi;
    if assigned(g[minpol]) then mipo:=[g[minpol]] else mipo:=[] fi;
    out(`Calculating the poincare-series ...`);
    if assigned(g[ccls]) then
        if chi=1 then chi:=[1 $ nops(g[ccls])] fi;
        [seq(chi[i]*g[classlengths][i]*
            reci(linalg[det](evalm(&*()-S*array(g[ccls][i][1]))),mipo[]), 
                                                    i=1..nops(g[ccls]))];
        normal(convert(",`+`)/g[order])
    else
        map((mat,mipo,S)->reci(linalg[det](evalm(&*()-S*array(mat))),mipo[]),
                                                    [g[eles][]],mipo,S);
        normal(convert(",`+`)/g[order])
    fi;
    if has(",g[alg]) then
        {coeffs(reduce(expand(numer(")-i*denom(")),g),g[alg])};
        normal(subs(solve(",i),i))
    fi;
    i:=";
    out(`It is`); out(print,'P'(S)=i); out();
    g[poinseries][chi]:=i
end:


# POINREP
# The best representation of the poincare-series relevant to invariant theory 
# with exponents > last_exp[1][1],...,last_exp[1][G[degree]] (default [0...0]).

poinrep:=proc(last_exp,Known)
    local n,i,nvec,num,next,pnum,pden,known;
    
    next:=proc(vec)
        local i,j,n,r,d,q;
        n:=nops(vec);
        if vec[1]=0 then RETURN([1$n]) fi;
        d:=sum('vec[j]', j=1..n);
        for i from n-1 by -1 to 1 do
            if i=1 then 1 else vec[i-1] fi;
            if vec[i]>" then
                q:=iquo(d-sum('vec[j]', j=1..i)+1,n-i,'r');
                RETURN([vec[1..i-1],vec[i]-1,q $ n-i-r,q+1 $r])
            fi
        od;
        q:=iquo(d+1,n,'r');
        [q $ n-r,q+1 $r]
    end;
    
    if nargs>=2 then known:=Known else known:=_known fi;
    poincare(1,known[svar],known[group]);
    pnum:=numer("); pden:=denom("");
    n:=known[group][degree];
    if nargs>=1 and type(last_exp,list) then
            nvec:=last_exp[1] else nvec:=[0 $n] fi;
    do
        nvec:=next(nvec);
        if divide(pnum*product('1-known[svar]^nvec[i]', i=1..n),pden,'num')
        and not member(true,map(<evalb(x<0)>,[coeffs(num,known[svar])]))
        then
            out(`Try the representation`);
            out(print,'P'(known[svar])=
                    num/product('1-known[svar]^nvec[i]', i=1..n));
            out();
            RETURN([nvec,[seq(i $ coeff(num,known[svar],i), 
                                    i=0..degree(num,known[svar]))]])
        fi
    od
end:


# PRIMARIES
# Finds primary generators. If Inter='inter', 'primaries' might ask the user
# what to do.

primaries:=proc(s_name,Inter,Known)
    local n,i,j,k,poin,max,cand,pattern,s,mat,base,trials,idea,I,i0,known;
    
    if nargs>=3 then known:=Known else known:=_known fi;
    if nargs>=1 and type(s_name,list) then s:=s_name[1]; i0:=s_name[2]
    else i0:=0
    fi;
    out(0,`Finding primary generators ...`); out();
    #readlib(select);
    poin:=0; n:=known[group][degree];
    
    do              # try to make the different representations of P(s) real
        poin:=poinrep(poin,known);
        max:=poin[1][n];
        pattern:=[seq(nops(select((x,y)->x=y,poin[1],i)), i=1..max)];
        mat:=NULL; base:=NULL;
        for i to max do
            if pattern[i]>0 then
                invarspace(i,1,known);
                mat:=mat,[pattern[i],nops(")];
                base:=base,[seq(I.i._.j=""[j], j=1..nops(""))];
            fi
        od;
        mat:=[mat]; base:=[base];
        
        for trials do
            if assigned(idea) then idea
            else
                mat:=`primaries/nextmat`(mat);
                if "=`no more!` then
                    out(`No more linearly independent choices!`);
                    mat:=0; break
                fi;
                [seq(seq(sum('mat[i][j,k]*lhs(base[i][k])',k=1..nops(base[i])), 
                                j=1..linalg[rowdim](mat[i])), i=1..nops(mat))]
            fi;
            cand:=subs(base[],");
            out(`Trying invariants`,seq(``.s.(i+i0)=""[i], i=1..n));
            idea:='idea';
        
            # Checking if cand is a good list of primaries ...
            if known[trans][permgroup]=true and
                    poin[1]=[seq(i, i=2..nops(known[xvars]))] then
                out(`They come from the elementary-symmetric functions:`);
                out(`No further checking necessary!!`);
                break
            # Have to check for zeroes<>(0..0)
            elif check_nullspace(cand,known) then
                out(`OH HAPPY DAY: They qualify!! `.
                                `(No common zeroes other than [0,..,0])`);
                break
            else
                out(
                `Not so good: There are common zeroes other than (0,..,0))`);
                if nargs>=2 and Inter='inter' then
                    lprint();
                    lprint(`'invar' is troubled and seeks counsel: Shall it`);
                    lprint(`- proceed as before and try another combination `.
                            `(type ";" and RETURN)`);
                    lprint(`- give up and pass to the next representation of `.
                                `the poincare-series`);
                    lprint(`     (type "hopeless;" and RETURN)`);
                    lprint(`- try a combination of Ii_j's of your desire `.
                            `(type it in with ";" and RETURN)`);
                    [readstat(`So ...> `)];
                    if "=[] then
                    elif "=['hopeless'] then mat:=0; break
                    else idea:="
                    fi
                elif trials>=10 then mat:=0; break
                fi
            fi
        od;
            
        if mat=0 then
            out(`WEEP AND MOURN: `.
                `Pass to next representation of the poincare-series ...`)
        else break
        fi
    od;
    if assigned(known[prim]) then known[prim] else [] fi;
    known[prim]:=["[],seq([``.s.(i+i0),poin[1][i]], i=1..n)];
    known[used]:={cand[]};
    if not assigned(known[values]) then known[values]:=NULL fi;
    for i from 1 to n do
        known[values]:=known[values],``.s.(i+i0)=cand[i];
    od;
    out(); out(0,`The primaries are (by their symbolic names):`);
    for i in known[prim] do
        if nops(i)=2 then out(0,`   `.(i[1]).` of degree `.(i[2]))
        else out(0,`   `.(i[1]).` of degree `.(i[2]).`  (outer primary)`)
        fi
    od;
    out(0); out(`Done with finding primary generators`); out();
    RETURN(poin[2])
end:


# CHECK_NULLSPACE
# checks whether the members of 'ideal' have no common zero other than (0..0)

check_nullspace:=proc(ideal,Known)
    local n,i,temp,known,var;
    
    if nargs>=2 then known:=Known else known:=_known fi;
    if assigned(known[vars]) then var:=known[vars]
    else var:=[indets(ideal)[]]
    fi;
    n:=nops(var);
    if has(ideal,known[group][alg]) then temp:=[ideal[],known[group][minpol]]
    else temp:=ideal
    fi;
    for i to n do
        subs(var[i]=1,temp);
        if {"[]}={0} then RETURN(false) fi;
        readlib(`grobner/gsolve`)(")=[];
        if " then temp:=subs(var[i]=0,temp)     # no zero with var[i]<>0!
        else RETURN(false)
        fi
    od;
    true
end:


# PRIMARIES/NEXTMAT
# Service-routine for 'primaries':
# Succession of lists of matrices for building combinations.

`primaries/nextmat`:=proc(last)
    local i,j,k,vec,newmat,ans,a,upsidedown,isregular,vectomat;
    global nextmat_remember;
    
    upsidedown:=proc(A)
        local i;
        linalg[rowdim](A);
        linalg[submatrix](A,[seq("-i, i=0.."-1)],1..linalg[coldim](A))
    end;
    
    isregular:=
        A->evalb(linalg[coldim](A)-nops(linalg[kernel](A))=linalg[rowdim](A));
    
    vectomat:=proc(pat,vec)
    global nextmat_remember;
        if pat[1]=pat[2] then
            array(identity,1..pat[1],1..pat[1]);
            RETURN(array(convert(",listlist)))
        fi;
        nextmat_remember[point];
        nextmat_remember[point]:="+pat[1]*pat[2];
        linalg[matrix](pat[1],pat[2],[vec["".."-1]])
    end;
        

    if type(last,listlist) then                     # Start new sequence
        readlib(`solve/linear`);
        nextmat_remember[form]:=[seq(last[nops(last)-i], i=0..nops(last)-1)];
        [seq(array([seq([seq(a[i,j,k], k=1.."[i][1])], j=1.."[i][1])]), 
                                                            i=1..nops("))];
        nextmat_remember[multipier]:=
            subs('form'=",mat->zip((x,y)->evalm(x&*y),form,mat));
        proc(par)
            local i;
            [seq([0$(par[1]-i),1,0$(par[2]-par[1]+i-1)], i=1..par[1])]
        end;
        map(",nextmat_remember[form]); map(array,");
        nextmat_remember[yet]:={nextmat_remember[multipier](")};
        RETURN(map(upsidedown,[seq(""[nops("")-i], i=0..nops("")-1)]))
    fi;
    
    if {map(x->evalb(x[1]=x[2]),nextmat_remember[form])[]}={true} then 
        RETURN(`no more!`)
    fi;
    
    proc(mat)
        if linalg[rowdim](mat)=linalg[coldim](mat) then RETURN() fi;
        convert(mat,listlist);
        op(map(op,"))
    end;
    vec:=map(",map(upsidedown,[seq(last[nops(last)-i], i=0..nops(last)-1)]));
        
    do                                                  # Find next good one!
        vec:=`primaries/nextvec`(vec);
        nextmat_remember[point]:=1;
        newmat:=map(vectomat,nextmat_remember[form],vec);
        
        if member(false,map(isregular,newmat)) then next fi;
        
        for ans in nextmat_remember[yet] do
            map(op,{seq(entries(linalg[add](newmat[i],ans[i])), 
                                            i=1..nops(newmat))});       
            [`solve/linear`(",indets("))];
            if "<>[] then newmat:=0; break fi
        od;
        if newmat<>0 then 
            nextmat_remember[yet]:=nextmat_remember[yet] union
                            {nextmat_remember[multipier](newmat)};
            RETURN(map(upsidedown,[seq(newmat[nops(newmat)-i], 
                                            i=0..nops(newmat)-1)]))
        fi
    od
end:


# PRIMARIES/NEXTVEC
# A service-routine to 'primaries/nextmat':
# yields a nice succession of all vectors over the integrals if you do
# `primaries/nextvec`([0,0,0,0]): `primaries/nextvec`("): 
# `primaries/nextvec`(") ... or alike.

`primaries/nextvec`:=proc(vec)
    local n,i,new;
    
    n:=nops(vec);
    new:=array(vec);
    for i from 1 to n do
        if new[i]>0 then new[i]:=-new[i]; RETURN(convert(new,list))
        else new[i]:=-new[i]
        fi
    od;
    for i from 1 to n-1 do
        if new[i]>0 then
            new[1]:=new[i]-1;
            if i>1 then new[i]:=0 fi;
            new[i+1]:=new[i+1]+1;
            RETURN(convert(new,list))
        fi
    od;
    new[1]:=new[n]+1;
    new[n]:=0;
    RETURN(convert(new,list))
end:


# REPRESENT
# represents 'expr' in terms of the Cohen-Macaulay-basis contained in 'known'.
# Core-routine of the package!

represent:=proc(expr,Known)
    local alfa,n,i,deg,terms,ansatz,leftside,rightside,equ,sol,unknowns,known;
    
    if nargs>=2 then known:=Known else known:=_known fi;
    deg:=expr[2];

    if expr[1]=0 then RETURN(0)
    elif has(expr[1],known[vars]) then expand(subs(known[vars][1]=1,expr[1]))
                                        # expr is already evaluated
    else `represent/evalprod`(expr,known)
    fi;
    if indets(") minus {known[vars][],known[group][alg]} <> {} then
            ERROR(`Expression contains outer primaries or x-variables!`) fi;
    rightside:=";
    
    if (type(known[leftsides][deg],list) and 
            known[leftsides][deg][1]=nops(known[base])) then                                                        
                                                  # left side known!
        unknowns:=known[leftsides][deg][2];
        ansatz:=known[leftsides][deg][3];
        leftside:=known[leftsides][deg][4];
        n:=nops(unknowns)
    else
        select(p->nops(p)=2,known[prim]);       # leave out outer primaries     
        terms:=monoms_base(deg,",known[base]);
        n:=nops(terms);
        if n=0 then RETURN('FAIL') fi;
        unknowns:=[seq(alfa[i], i=1..n)];
        ansatz:=convert([seq(unknowns[i]*terms[i][1], i=1..n)],`+`);
        leftside:=expand(convert([seq(unknowns[i]*
                        `represent/evalprod`(terms[i],known), i=1..n)],`+`))
    fi;

    equ:={coeffs(leftside-rightside,[known[vars][2..known[group][degree]]])};
    out(`Solving a system of `.(nops(equ)).` linear equations in `.
                                                n.` unknowns ...`);
    if assigned(known[group][minpol]) then
        sol:=[alglinsolve(equ,{unknowns[]},known[group][minpol])]
    else sol:=[alglinsolve(equ,{unknowns[]})]
    fi;

    if nops(sol)=0 then RETURN('FAIL') fi;
    if nops(sol)>1 then RETURN(`MULTIPLE SOLUTIONS!`) fi;
    for equ in op(sol) do              # still a bunch of solutions!
        if has(rhs(equ),unknowns) then 
            RETURN(`MULTIPLE SOLUTIONS!`)
        fi 
    od;
    known[leftsides][deg]:=
                [nops(known[base]),unknowns,ansatz,leftside];
                         # remember, since this will turn up again!
    subs(op(sol),ansatz);
end:


# ALGLINSOLVE
# solves a system of linear equations, possibly over a number field given by 
# 'minpol'. Presumably quicker than solve/linear/algnum.

alglinsolve:=proc(equations,unknowns,Minpol)
local i,k,t,eqn,eqns,var,vars,pivot,sol,sols,a,ans,x,minpol;
    
    readlib(`solve/linear`);
    if nargs>=3 then minpol:=Minpol
    else if assigned(_known[group][minpol]) then
                        minpol:=_known[group][minpol] fi
    fi;
    a:=indets(minpol)[];
    if not ( assigned(minpol) and has(equations,a) ) then 
        RETURN(`solve/linear`(equations,unknowns))
    fi;
    ans:=sum('x[i]*a**i', i=0..degree(minpol,a)-1);
    eqns := equations minus {0};
    for k while eqns <> {} do
        if 2 < printlevel then
            lprint(`solve: linear: # equations is:`,nops(eqns))
        fi;
        eqn := eqns[1];
        for i from 2 to nops(eqns) do
            if length(eqns[i]) < length(eqn) then eqn := eqns[i] fi
        od;
        vars := indets(eqn) intersect unknowns;
        if vars = {} then RETURN() else var := vars[1] fi;
        eqns := eqns minus {eqn};
        pivot := coeff(eqn,var,1);
        for i from 2 to nops(vars) do
            t := coeff(eqn,vars[i],1);
            if length(t) < length(pivot) then pivot := t; var := vars[i] fi
        od;
        if 3 < printlevel then
            lprint(`solve: linear: best unknown/equation`,var,eqn)
        fi;
        if type(pivot,integer) then pivot:=1/pivot
        else
            {coeffs(rem(ans*pivot-1,minpol,a),a)};
            `solve/linear`(",{seq(x[i-1], i=1..degree(minpol,a))});
            pivot:=subs(",ans)
        fi;
        eqn := -expand(rem(pivot*subs(var = 0,eqn),minpol,a));
        sol[k] := var,eqn;
        proc(x,m,a)
            expand(rem(x,m,a));
            if "<>0 then "/icontent(") fi
        end;
        eqns:=map(",subs(var = eqn,eqns),minpol,a) minus {0}
    od;
    
    sols:={};
    for i from k-1 by -1 to 1 do
        if 2 < printlevel then
            lprint(`solve: linear: backsubstitution at:`,i)
        fi;
        var := sol[i][1];
        eqn := sol[i][2];
        eqn := expand(rem(subs(sols,eqn),minpol,a));
        sols := sols union {var = eqn}
    od;
    map(x->x=x,unknowns minus map(lhs,sols)) union sols
end:


# REPRESENT/EVALPROD
# evaluates a symbolic product.

`represent/evalprod`:=proc(expr,Known)
    local known;
    
    if nargs>=2 then known:=Known else known:=_known fi;
    subs(known[vars][1]=1,[known[values]]);
    if assigned(known[group][minpol]) then
        expand(reduce(subs(",expr[1]),known[group]))
    else
        expand(subs(",expr[1]),known[group])
    fi
end:


# REPFRAC
# Represent a homogeneous rational invariant in terms of a Cohen-Macaulay basis
# stored in 'known'. 'expr' is just the invariant, without degree.

repfrac:=proc(expr,Known)
    local i,numden,what,deg,ans,alfa,unknowns,known;
    
    if nargs>=2 then known:=Known else known:=_known fi;
    normal(expr);
    if indets(") minus {known[vars][],known[group][alg]} <> {} then
            ERROR(`Expression contains outer primaries or x-variables!`) fi;
    numden:=[numer("),denom(")];
    {known[vars][]};
    if degree(""[1],")<degree(""[2],") then what:=1 else what:=2 fi;
    
    # find a factor to extend numer or denom to an invariant
    for deg from 0 do
        monoms(deg,map(x->[x,1],known[vars]));
        ans:=sum('alfa[i]*"[i][1]', i=1..nops("));
        unknowns:={seq(alfa[i], i=1..nops(""))};
        expand(ans*numden[what]);
        map((mat,expr,known)->coeffs(expand(throw(expr,mat,0,known)-expr),
                            known[vars]),known[group][geners],",known);
        if has(",known[group][alg]) then
            alglinsolve(",unknowns,known[group][minpol])
        else
            readlib(`solve/linear`)(",unknowns)
        fi;
        ans:=expand(subs(",ans));
        indets(ans) intersect unknowns;
        if "<>{} then
            subs("[1]=1,map(x->x=0,"),ans);
            ans:="/icontent(");
            break
        fi
    od;
    out(`fraction extended by a factor of degree `.deg);
    out(2,`It is`,ans);
    
    proc(x,ext,known)
        expand(ext*x);
        if has(",known[group][alg]) then reduce(",known[group]) fi;
        represent([",degree(",{known[vars][]})],known)
    end;
    map(",numden,ans,known);
    if has(",'FAIL') then 'FAIL'
    elif has(",`MULTIPLE SOLUTIONS!`) then `MULTIPLE SOLUTIONS!`
    else normal("[1]/"[2])
    fi
end:


# SECONDARIES
# finds secondary generators.

secondaries:=proc(degrees,t_name,Known)
    local t,i,deg,candset,cand,shortest,temp,ready,yet,known;

    shortest:=proc(cands)
        local n,res,i;
        res:=cands[1]; n:=length(res);
        for i from 2 to nops(cands) do
            if length(cands[i])<n then res:=cands[i]; n:=length(res) fi
        od;
        res
    end;
    
    if nargs>=3 then known:=Known else known:=_known fi;
    if nargs>=2 and type(t_name,name) then t:=t_name fi;

    out(0,`Finding secondary generators ...`); out();
    out(cat(`We'll get a basis of invariants of degrees `,
                degrees[1],seq(`,`.(degrees[i]), i=2..nops(degrees)),`.`));

    out();
    
    known[base]:=[]; yet:=0;                # index of last new invariant
    known[second]:=[]; known[rel]:=[];

    # M A I N   L O O P

    for deg in degrees do
        if deg=0 then 
            out(`It starts with 1 (ever so amazing!)`); out();
            known[base]:=[op(known[base]),[1,0]]
        else                                    # degree not zero
            ready:=false; 
            do                      # try combinations of old invariants first
                candset:={monoms(deg,known[second])[]} minus known[used];
                if "={} then break fi;
                cand:=shortest(candset);
                known[used]:={known[used][],cand};
                out(`Trying `,cand[1],` of degree `.deg);
                derive(cand,known);     # maybe it can be derived!
                if "<>'FAIL' then                           # relation found!
                    #known[equ][cand[1]]:=";
                    known[equ][cand[1]]:=`known[equ] disabled!`;
                    if not ready then out(`Not good:`) fi;
                    out(cand[1]=collect("",
                        map(x->x[1],known[second]),distributed),`(derived)`);
                    out();
                    next
                fi;

                represent(cand,known);                  # derive failed!
                if "=`MULTIPLE SOLUTIONS!` then
                            ERROR(`Invariants not independent!`)
                elif "<>'FAIL' then                     # relation found!
                    #known[equ][cand[1]]:=";
                    known[equ][cand[1]]:=`known[equ] disabled!`;
                    if not ready then out(`Not good:`) fi;
                    out(cand[1]=collect("",
                        map(x->x[1],known[second]),distributed));
                    out();
                    cand[1]-"";
                    known[rel]:=[known[rel][],["/icontent("),deg]]
                else                # cand is good (=independent) invariant!
                    out(`Next invariant: `,cand[1]); out();
                    known[base]:=[op(known[base]),cand];
                    ready:=true;
                    break
                fi
            od;             # end of `while nops(candset)>0`

            while not ready do
                        # didn't get through with combinations of old,
                        # have to search new invariant

                candset:={invarspace(deg,1,known)[]} minus known[used];
                if candset={} then 
                    ERROR(`Not enough independent invariants!`)
                fi;
                cand:=shortest(candset);
                known[used]:={known[used][],cand};
                member(cand,invarspace(deg,1,known),'temp');
                out(`Trying invariant I`.deg._.temp.` of degree `.deg);
                represent([cand,deg],known);
                if "=`MULTIPLE SOLUTIONS!` then
                    ERROR(`Invariants not independent!`)
                elif "='FAIL' then
                           # cand is good (=independent) invariant!
                    yet:=yet+1;
                    if assigned(``.t.yet) then
                        ERROR(``.(evaln(``.t.yet)).
                            ` must be an unassigned name!`)
                    fi;
                    temp:=``.t.yet;
                    known[second]:=[known[second][],[temp,deg]];
                    known[used]:={known[used][],[temp,deg]};
                    known[values]:=known[values],temp=cand;
                    out(`We take it and call it "`.temp.`"!`); out();
                    known[base]:=[op(known[base]),[temp,deg]];
                    ready:=true
                else                                # cand was dependent
                    out(`Not good:`); out('Candidate'="); out()
                fi
            od                        # end of `didn't get through`
        fi
    od;                                          # end of `for deg`

    out(0,`The secondary generators are:`);
    for i in known[second] do out(0,`   `.(i[1]).` of degree `.(i[2])) od;
    out(0);
    out(`The invariant ring is  a free module over Q[{primaries}] with basis`);
    for i in known[base] do out(` `,i[1],` of degree `.(i[2])) od;
    out(); out(`Ready with finding secondary generators!`); out();
    RETURN(known[base])
end:

# RELATIONS
# calculates the syzygies.

relations:=proc(Known)
    local deg,wanted,missing,temp,shortest,i,known;

    shortest:=proc(cands)
        local n,res,i;
        res:=cands[1]; n:=length(res);
        for i from 2 to nops(cands) do
            if length(cands[i])<n then res:=cands[i]; n:=length(res) fi
        od;
        res
    end;
    
    if nargs>=1 then known:=Known else known:=_known fi;
    out(0,`Finding relations between secondary generators ...`);
    #for temp in known[base] do known[equ][temp[1]]:=temp[1] od;
    for temp in known[base] do known[equ][temp[1]]:=`known[equ] disabled!` od;
    
    # MAIN LOOP
    known[second];
    if "=[] then 0 else "[nops(")][2]*known[base][nops(known[base])][2] fi;
    for deg to " do
        wanted:=NULL;
        for temp in known[second] do
            wanted:=wanted,
                map(proc(f,s,d) if f[2]+s[2]=d then [s[1]*f[1],d] fi end,
                                            known[base],temp,deg)[]
        od;
        wanted:=
            select((f,s)->not member([f[1]],s),{wanted},{indices(known[equ])});
        if "<>{} then out(); out(`Degree `.deg.`:`) fi;
        while wanted<>{} do                     # treating members of wanted
            missing:=shortest(wanted);
            wanted:=wanted minus {missing};
            out(`calculating `,missing[1]);
            temp:=derive(missing,known);
            if temp<>'FAIL' then
                #known[equ][missing[1]]:=temp;
                known[equ][missing[1]]:=`known[equ] disabled!`;
                out(missing[1]=collect(temp,
                    map(x->x[1],known[second]),distributed),`(derived)`);
            else             # bad luck: have to calculate it!
                temp:=represent(missing,known);
                if temp=`MULTIPLE SOLUTIONS!` then 
                                    ERROR(`Invariants not independent!`)
                elif temp='FAIL' then ERROR(`Not enough invariants!`)
                else
                    #known[equ][missing[1]]:=temp;
                    known[equ][missing[1]]:=`known[equ] disabled!`;
                    out(missing[1]=collect(temp,
                        map(x->x[1],known[second]),distributed));
                    missing[1]-temp;
                    known[rel]:=[known[rel][],["/icontent("),deg]]
                fi
            fi                              # end of 'bad luck'
            
        od                # end of 'treating members of wanted'
    od;                                           # end of MAIN LOOP
    
    out(); out(`The relations are:`);
    for i in known[rel] do out(` `,i[1],`of degree `.(i[2])) od;
    out(); out(`Ready with finding relations!`); out();
    RETURN(known[rel])
end:


# DERIVE
# Tries to derive a relation from the ones already known.

derive:=proc(wanted,Known)
    local terms,n,ansatz,alfa,mons,equ,temp,s,t,b,i,known;
    
    if nargs>=2 then known:=Known else known:=_known fi;
    out(2,`Try to derive`,wanted[1],`from the known relations ...`);
    s:=map(x->x[1],known[prim]);
    t:=map(x->x[1],known[second]);
    b:=map(x->x[1],known[base]);
    terms:=monoms_base(wanted[2],[known[prim][],known[second][]],known[rel]);
    n:=nops(terms);
    ansatz:=expand(wanted[1]-
                    convert([seq(alfa[i]*terms[i][1], i=1..n)],`+`));
    terms:=[coeffs(ansatz,[s[],t[]],'mons')]; mons:=[mons];
    equ:={};
    for i from 1 to nops(terms) do
        temp:=coeffs(mons[i],s);
        if not member(temp,b) then equ:=equ union {terms[i]} fi
    od;
    out(2,`   Solving a system of `.(nops(equ)).` linear equations in `.
                                                n.` unknowns ...`);
    if assigned(known[group][minpol]) then [known[group][minpol]] else [] fi;
    equ:=alglinsolve(equ,{seq(alfa[i], i=1..n)},"[]);
    if equ=NULL then out(2,`... failed!`); RETURN('FAIL') fi;
    out(2,`... succeeded!`);
    RETURN(expand(subs(equ,ansatz)))
end:


# Retrieval-functions PRIM, SECOND, BASE, REL, GENERATORS

showprims:=proc(Known)
    if nargs>=1 then Known else _known fi;
    "[prim]
end:

showseconds:=proc(Known)
    if nargs>=1 then Known else _known fi;
    "[second]
end:

showbasis:=proc(Known)
    if nargs>=1 then Known else _known fi;
    "[base]
end:

showrels:=proc(Known)
    if nargs>=1 then Known else _known fi;
    "[rel]
end:

generators:=proc(G)
    if nargs>=1 then G else _known[group] fi;
    "[geners]
end:


# VALUEOF
# the value of some expression in the s1,s2... and t1,t2...

valueof:=proc(expr,Known)
    if nargs>=2 then Known else _known fi;
    subs("[values],expr)
end:


macro(alglinsolve=alglinsolve,aver_orb=aver_orb,
      check_nullspace=check_nullspace,classes=classes,derive=derive,
      elements=elements,elesym=elesym,generators=generators,inv=inv,
      invarspace=invarspace,invring=invring,lingroup=lingroup,
      mklingroup=mklingroup,monoms=monoms,monoms_base=monoms_base,mul=mul,
      onesoff=onesoff,out=out,poincare=poincare,poinrep=poinrep,
      primaries=primaries,proj=proj,reci=reci,reduce=reduce,
      relations=relations,renormal=renormal,repfrac=repfrac,
      represent=represent,secondaries=secondaries,showbasis=showbasis,
      showprims=showprims,showrels=showrels,showseconds=showseconds,
      throw=throw,valueof=valueof):


# DEFINITION OF A COUPLE OF PERMUTATION GROUPS

Z2:=permgroup(2,{[[1,2]]}):

Z3:=permgroup(3,{[[1,2,3]]}):
S3:=permgroup(3,{[[1,2,3]],[[1,2]]}):

V4:=permgroup(4,{[[1,2],[3,4]],[[1,3],[2,4]]}):
Z4:=permgroup(4,{[[1,2,3,4]]}):
D4:=permgroup(4,{[[1,2,3,4]],[[1,3]]}):
A4:=permgroup(4,{[[1,2],[3,4]],[[1,3],[2,4]],[[1,2,3]]}):
S4:=permgroup(4,{[[1,2]],[[1,3]],[[1,4]]}):

Z5:=permgroup(5,{[[1,2,3,4,5]]}):
D5:=permgroup(5,{[[1,2,3,4,5]],[[2,5],[3,4]]}):
F20:=permgroup(5,{[[1,2,3,4,5]],[[2,3,5,4]]}):
A5:=permgroup(5,{[[1,2,3,4,5]],[[1,2,3]]}):
S5:=permgroup(5,{[[1,2]],[[2,3]],[[3,4]],[[4,5]]}):




# ******************************************************************* #
#                                                                     #
#                            The help-files                           #
#                                                                     #
# ******************************************************************* #



`help/text/invar`:=TEXT(
    ``,
    `HELP FOR: The invar package`,
    ``,
    `SYNOPSIS:`,
    `- The invar package is a function package mainly for computing the invariant `,
    `  ring of permutation groups or finite linear groups over Q or a number field.`,
    ``,
    `- The functions available are:`,
    ``,
    `      alglinsolve       invarspace        poincare          represent`,
    `      aver_orb          invring           poinrep           secondaries`,
    `      check_nullspace   lingroup          primaries         showbasis`,
    `      classes           mklingroup        proj              showprims`,
    `      derive            monoms            reci              showrels`,
    `      elements          monoms_base       reduce            showseconds`,
    `      elesym            mul               relations         throw`,
    `      generators        onesoff           renormal          valueof`,
    `      inv               out               repfrac`,
    ``,
    `- The statement with(invar) also defines a handful of permutation groups, which `,
    `  are Z2, Z3, S3, V4, Z4, D4, A4, S4, Z5, D5, F20, A5 and S5.`,
    ``,
    `- To get help for particular functions type ?<function>. Some of them are `,
    `  documented in short form.`,
    `  The main routine is invring. To get the ring of invariants of Z3, type `,
    `  invring(Z3);`,
    `  Not all of the routines check the types of their arguments. invring does, `,
    `  though.`,
    ``,
    `- For some background, information about the algorithms and some references see `,
    `  the LaTeX-file which should come along with the invar package.`,
    ``,
    `- The invar package was written by Gregor Kemper, e-mail`,
    `  kemper@kalliope.iwr.uni-heidelberg.de`,
    ``,
    `SEE ALSO:  with, invring`
    ):
`help/text/grouptable`:=TEXT(
    `   `,
    `HELP FOR: grouptable`,
    `   `,
    `SYNOPSIS:   `,
    `- grouptable is an object-oriented data structure for defining linear groups `,
    `  over Q or an algebraic number field and for storing information about them. `,
    `  When invring is invoked, it produces a grouptable in  _known[group] (as `,
    `  fault).`,
    ``,
    `- A grouptable is a table with special entries. For a closer look, have it `,
    `  produced by invring and then look at the indices! The procedures in the `,
    `  invar-package that deal with groups are given this table and write their `,
    `  results into it.`,
    ``,
    `- A group can be defined by assigning appropriate values to the index permgroup `,
    `  or generators. In the first case, the permutation group will be transformed `,
    `  into a linear group. `,
    ``,
    `EXAMPLES:`,
    `> G[permgroup]:=[3,{[[1,2,3]],[[1,2]]}]:              # This is S6!`,
    `> classes(G);`,
    `Calculating all elements of the group G ...`,
    `It has 6 elements.`,
    ``,
    `Calculating the conjugacy classes of G ...`,
    `G has conjugacy classes of lengths   [1, 2, 3]`,
    ``,
    `    [[[1, 0, 0], [0, 1, 0], [0, 0, 1]], [[0, 0, 1], [1, 0, 0], [0, 1, 0]],`,
    ``,
    `        [[0, 1, 0], [1, 0, 0], [0, 0, 1]]]`,
    ``,
    `> indices(G);`,
    ``,
    `    [classes], [permgroup], [order], [identity], [geners], [eles],`,
    ``,
    `        [classlengths], [inverses], [degree]`,
    ``,
    `> # G has grown!`,
    `> `,
    `> # Taking a linear group over Q(i) now ...`,
    `> H[geners]:={[[i,0],[0,-i]],[[0,1],[1,0]]}:`,
    `> H[minpol]:=i**2+1:`,
    `> elements(H);`,
    `Calculating all elements of the group G ...`,
    `It has 8 elements.`,
    ``,
    `SEE ALSO:  invring, known, invar`
    ):
`help/invar/text/grouptable`:=`help/text/grouptable`:
`help/text/known`:=TEXT(
    `   `,
    `HELP FOR: known`,
    `   `,
    `SYNOPSIS:   `,
    `- In the invar package, the (growing) knowledge about the group and its ring of `,
    `  invariants is stored in a big, big table. This "known"-table is dragged along `,
    `  between the different routines during the course of calculation. This gives `,
    `  the routines easy access to the information they need, instead of passing `,
    `  elongated lists of arguments. The procedures then write their results into `,
    `  the known-table. So this is a convenient way of sharing information and `,
    `  avoiding double calculating!`,
    ``,
    `- invring produces a known-table, which is called  _known as default. When the `,
    `  specification of the known-table is omitted in calls of other routines, they `,
    `  will expect their information in  _known.`,
    ``,
    `- For example, one of the entries of the known-table is  _known[group], which `,
    `  is a grouptable-data structure describing the group. For more details, just `,
    `  have  _known produced by invring and browse through the entries!`,
    ``,
    `EXAMPLES:`,
    `> protocol_level:=-1:`,
    `> invring(S3);`,
    `> indices(_known);`,
    ``,
    `  [xvars], [vars], [svar], [prim], [trans], [second], [base], [rel], [used],`,
    ``,
    `      [values], [group], [equ]`,
    ``,
    `> _known[values];`,
    ``,
    `                                  2             2           2           2`,
    `      s1 = x1 + x2 + x3, s2 = - y1  - y1 y2 - y2 , s3 = - y1  y2 - y1 y2`,
    ``,
    `> # _known[values] relates the symbols si, ti to the polynomials they stand for`,
    `> classes();`,
    ``,
    `           [[[1, 0], [0, 1]], [[0, -1], [1, -1]], [[-1, 0], [-1, 1]]]`,
    ``,
    `> # classes takes its input from _known[group] as default`,
    ``,
    `SEE ALSO:  invar, invring, grouptable, protocol_level`
    ):
`help/invar/text/known`:=`help/text/known`:
`help/text/protocol_level`:=TEXT(
    `   `,
    `HELP FOR: protocol_level - how much protocol wanted`,
    `   `,
    `CALLING SEQUENCE:`,
    `   protocol_level:=<integer>;`,
    `   `,
    `SYNOPSIS:   `,
    `- Many of the routines of the invar package print out some protocol during `,
    `  execution which tells the user what's going on at the moment. If you are `,
    `  annoyed by all this information stuffing up your screen, set the global `,
    `  variable protocol_level:=0 and you'll get a slimmer protocol, or `,
    `  protocol_level:=-1 and there will be no protocol at all. If you want more `,
    `  protocol than is usually printed, set protocol_level:=2.`,
    `   `,
    `EXAMPLES:   `,
    `> protocol_level:=0:`,
    `> invring(Z4);`,
    `Splitting off unit-characters ...`,
    `Finding primary generators ...`,
    `The primaries are (by their symbolic names):`,
    `   s1 of degree 1  (outer primary)`,
    `   s2 of degree 2`,
    `   s3 of degree 2`,
    `   s4 of degree 4`,
    ``,
    `Finding secondary generators ...`,
    `The secondary generators are:`,
    `   t1 of degree 3`,
    `   t2 of degree 3`,
    `   t3 of degree 4`,
    ``,
    `Finding relations between secondary generators ...`,
    ``,
    `SEE ALSO:  invar`
    ):
`help/invar/text/protocol_level`:=`help/text/protocol_level`:
`help/text/alglinsolve`:=TEXT(
    ``,
    `FUNCTION: alglinsolve - solve a system of linear equations over a number field`,
    `   `,
    `CALLING SEQUENCE:`,
    `   alglinsolve(equations,unknowns,minpol);`,
    `   `,
    `PARAMETERS:`,
    `   equations - a set of polynomials, linear in the unknowns`,
    `   unknowns  - a set of names`,
    `   minpol    - (optional) a univariate polynomial defining the number field `,
    `                                         (default is  _known[group][minpol])`,
    `   `,
    `SYNOPSIS:   `,
    `- alglinsolve solves a system of linear equations over an algebraic number `,
    `  field or (if parametrized) over the field of rational functions in the `,
    `  parameters over a number field. Just like solve, it returns a set of `,
    `  equations for the unknowns, or the NULL-sequence if there is no solution.`,
    ``,
    `- The algorithm used is the same as in the usual solver for systems of linear `,
    `  equations in Maple, but alglinsolve seems to be quicker than `,
    `  solve/linear/algnum since it has to handle only one equation specifying the `,
    `  number field.`,
    `  `,
    `- alglinsolve is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> alglinsolve({x-i*y,y+i*x},{x},i**2+1);`,
    ``,
    `                                   {x = i y}`,
    ``,
    `> alglinsolve({x-i*y,y+i*x},{x,y,z},i**2+1);`,
    ``,
    `                            {x = i y, y = y, z = z}`,
    ``,
    `> alglinsolve({x-i*y+a,y+i*x},{x,y},i**2+1);`,
    `> # no solution`,
    ``,
    `SEE ALSO:  invar`
    ):
`help/invar/text/alglinsolve`:=`help/text/alglinsolve`:
`help/text/aver_orb`:=TEXT(
    ``,
    `FUNCTION: aver_orb - sum over the orbit of some polynomial`,
    `   `,
    `CALLING SEQUENCE:`,
    `   aver_orb(expr,char,known);`,
    `   `,
    `PARAMETERS:`,
    `   expr  - a polynomial in known[vars] (y1,y2,... or x1,x2,...)`,
    `   char  - (optional) list defining a character (default is 1)`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- aver_orb takes the sum over all chi(sigma)^(-1)*sigma(expr), where sigma runs `,
    `  through all of G, and G is the group defined in the known-table known. chi is `,
    `  a linear character of G defined by char, which must be a list of character `,
    `  values matching classes(known[group]).`,
    `  The result is then divided by the lcm of its coefficients.`,
    ``,
    `- The result is a pseudo-invariant of weight chi (an invariant if chi=1).`,
    ``,
    `- known must have at least the entries known[group][permgroup] or `,
    `  known[group][geners] and known[vars].`,
    ``,
    `- aver_orb is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> known[group][permgroup]:=Z4:`,
    `> known[vars]:=[x.(1..4)]:`,
    `> classes(known[group]):`,
    `Calculating all elements of the group G ...`,
    `It has 4 elements.`,
    ``,
    `Calculating the conjugacy classes of G ...`,
    `G has conjugacy classes of lengths   [1, 1, 1, 1]`,
    `> map(m->linalg[det](array(m)),");`,
    ``,
    `                                 [1, -1, 1, -1]`,
    ``,
    `> # This defines a linear character of G.`,
    `> aver_orb(x1**2,",known);`,
    ``,
    `                               2     2     2     2`,
    `                             x3  - x2  + x1  - x4`,
    ``,
    ``,
    `SEE ALSO:  invar, known, grouptable, classes`
    ):
`help/invar/text/aver_orb`:=`help/text/aver_orb`:
`help/text/showbasis`:=TEXT(
    ``,
    `FUNCTION: showbasis   - retrieve the module-basis of a Cohen-Macaulay ring`,
    `FUNCTION: showprims   - retrieve the primary generators of a C-M ring`,
    `FUNCTION: showrels    - retrieve the relations of a C-M ring`,
    `FUNCTION: showseconds - retrieve the secondary generators of a C-M ring`,
    `   `,
    `CALLING SEQUENCE:`,
    `   showbasis(known);`,
    `   showprims(known);`,
    `   showrels(known);`,
    `   showseconds(known);`,
    `   `,
    `PARAMETERS:`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- All that these functions do is reading out the requested entry from the `,
    `  known-table, which has usually been produced by invoking invring.`,
    ``,
    `- The known-table contains information describing a graded Cohen-Macaulay `,
    `  algebra A over a field K (Q or number field). This information includes`,
    `  * the primaries, which are algebraically independent, homogeneous elements.`,
    `  * the secondaries, also homogeneous elements`,
    `  * the module-basis, which consists of product of secondaries, such that`,
    `    A = the direct sum of all K[{primaries}]*b, where b runs through the basis`,
    `  * the relations that hold between the secondaries`,
    ``,
    `- These functions return a list of basis-elements, primaries, relations or `,
    `  secondaries, with each item represented by a list containing its symbolic `,
    `  name (si or ti) or an expression in these names, and its degree. The list for `,
    `  a primary may also have the string outer third entry, signifying that this `,
    `  primary doesn't occur in any of the relations.`,
    ``,
    `- On how to get the actual values that the symbolic names stand for, see `,
    `  ?valueof.`,
    ``,
    `- These functions are part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> protocol_level:=-1:`,
    `> invring(Z3);`,
    `> showprims();`,
    ``,
    `                       [[s1, 1, outer], [s2, 2], [s3, 3]]`,
    ``,
    `> showseconds();`,
    ``,
    `                                   [[t1, 3]]`,
    ``,
    `> showbasis();`,
    ``,
    `                               [[1, 0], [t1, 3]]`,
    ``,
    `> showrels();`,
    ``,
    `                           2       2     3`,
    `                       [[t1  + 9 s3  + s2  + 3 s3 t1, 6]]`,
    ``,
    `> # Interpretation: There are algebraically independent invariants s1, s2       > #    and s3 of degrees 1,2,3. There is an invariant t1 of degree 3 such`,
    `> #    that the ring of invariants is the direct sum of`,
    `> #    Q[s1,s2,s3] and Q[s1,s2,s3]*t1.`,
    `> #    There is one relation of degree 6 between the secondaries. s1 doesn't`,
    `> #    occur in the relation; it is algebraically independent over Q(s2,s3,t1).`,
    ``,
    `SEE ALSO:  invar, known, protocol_level, primaries, secondaries,`,
    `           relations, valueof`
    ):
`help/invar/text/showbasis`:=`help/text/showbasis`:
`help/text/check_nullspace`:=TEXT(
    ``,
    `FUNCTION: check_nullspace - is [0...0] the only zero of a homogeneous ideal?`,
    `   `,
    `CALLING SEQUENCE:`,
    `   check_nullspace(ideal,known);`,
    `   `,
    `PARAMETERS:`,
    `   ideal - set or list of homogeneous, nonconstant polynomials`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- check_nullspace returns true if [0,...,0] is the only common zeroes of the `,
    `  polynomials in ideal, otherwise false.`,
    ``,
    `- If known[vars] is assigned (and a list of names), these names are taken to be `,
    `  the indeterminates of the polynomials. If not, indets(ideal) is taken `,
    `  instead.`,
    ``,
    `- The polynomials must have coefficients in the rational numbers or in an `,
    `  algebraic number field. In the latter case, this number field is defined by `,
    `  the equation known[group][minpol] in the indeterminate known[group][alg].`,
    ``,
    `- check_nullspace is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> check_nullspace([x*y,x**2+y**2]);`,
    ``,
    `                                      true`,
    ``,
    `> check_nullspace([x*y,x**2]);`,
    ``,
    `                                     false`,
    ``,
    `> known[vars]:=[x,y]: known[group][alg]:=a: known[group][minpol]:=a**2+1:`,
    `> check_nullspace([x+a*y,a*x+y],known);`,
    ``,
    `                                      true`,
    ``,
    `> check_nullspace([x+a*y,a*x-y],known);`,
    ``,
    `                                     false`,
    ``,
    ``,
    `SEE ALSO:  invar, known`
    ):
`help/invar/text/check_nullspace`:=`help/text/check_nullspace`:
`help/text/classes`:=TEXT(
    ``,
    `FUNCTION: classes - the conjugacy classes of a finite linear group`,
    `   `,
    `CALLING SEQUENCE:`,
    `   classes(G);`,
    `   `,
    `PARAMETERS:`,
    `   G - (optional) a table defining the group (see ?grouptable; default is`,
    `                                                             _known[group])`,
    `   `,
    `SYNOPSIS:   `,
    `- classes calculates the conjugacy classes of G and writes them into G[ccls] as `,
    `  a list of sets ordered by cardinality. The cardinalities are also stored. `,
    `  classes returns a list containing one representative for each class.`,
    ``,
    `- G is given as a grouptable. It is a finite linear group over Q or an `,
    `  algebraic number field. classes will work if G[geners] or G[permgroup] is `,
    `  defined. In the latter case, G is transformed into a linear group.`,
    ``,
    `- classes is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> G[permgroup]:=[3,{[[1,2,3]],[[1,2]]}]:              # This is S6!`,
    `> classes(G);`,
    `Calculating all elements of the group G ...`,
    `It has 6 elements.`,
    ``,
    `Calculating the conjugacy classes of G ...`,
    `G has conjugacy classes of lengths   [1, 2, 3]`,
    ``,
    `    [[[1, 0, 0], [0, 1, 0], [0, 0, 1]], [[0, 0, 1], [1, 0, 0], [0, 1, 0]],`,
    ``,
    `        [[0, 1, 0], [1, 0, 0], [0, 0, 1]]]`,
    ``,
    `> # Taking a representation of D4 over Q(i) now ...`,
    `> protocol_level:=-1:`,
    `> invring(lingroup({[[i,0],[0,-i]],[[0,1],[1,0]]},i**2+1));`,
    `> protocol_level:=2:`,
    `> classes():`,
    `Calculating the conjugacy classes of G ...`,
    `G has conjugacy classes of lengths   [1, 1, 2, 2, 2]`,
    ``,
    `They are`,
    `                                    [ 1  0 ]`,
    `                                   {[      ]}`,
    `                                    [ 0  1 ]`,
    ``,
    `                                   [ -1   0 ]`,
    `                                  {[        ]}`,
    `                                   [  0  -1 ]`,
    ``,
    `                              [  0  -1 ]  [ 0  1 ]`,
    `                             {[        ], [      ]}`,
    `                              [ -1   0 ]  [ 1  0 ]`,
    ``,
    `                             [ - i  0 ]  [ i   0  ]`,
    `                            {[        ], [        ]}`,
    `                             [  0   i ]  [ 0  - i ]`,
    ``,
    `                             [  0   i ]  [ 0  - i ]`,
    `                            {[        ], [        ]}`,
    `                             [ - i  0 ]  [ i   0  ]`,
    ``,
    ``,
    `SEE ALSO:  invar, grouptable, protocol_level, invring`
    ):
`help/invar/text/classes`:=`help/text/classes`:
`help/text/derive`:=TEXT(
    ``,
    `FUNCTION: derive - try to derive a relation from the ones already known`,
    `   `,
    `CALLING SEQUENCE:`,
    `   derive(wanted,known);`,
    `   `,
    `PARAMETERS:`,
    `   wanted - a list containing a product of secondary generators and its degree`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- After having computed a Cohen-Macaulay basis of a graded algebra (normally `,
    `  the invariant ring), a product of secondaries can be expressed in this basis, `,
    `  yielding a relation for this product. Now in many cases such a relation can `,
    `  be derived from the relations that are already known and stored in `,
    `  known[rel]. `,
    `  derive tries to do this and so may shortcut the actual calculation the wanted `,
    `  relation.`,
    ``,
    `- The first component of wanted contains a product of secondaries given by `,
    `  their symbolical name (t1,t2,...). If successful, derive returns the `,
    `  representation of this product in the Cohen-Macaulay basis stored in known. `,
    `  Otherwise, it returns the string FAIL.`,
    ``,
    `- What is derivability? - Here the algorithm sticks to the concept of defining `,
    `  a ring by a presentation: The derivable relations are exactly those in the `,
    `  ideal generated by the relations already known. Then by degree considerations `,
    `  we get a procedure for extracting the relation we want.`,
    ``,
    `- derive is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> protocol_level:=-1:`,
    `> invring(Z3);`,
    `> showseconds();`,
    ``,
    `                                   [[t1, 3]]`,
    ``,
    `> showrels();`,
    ``,
    `                           2       2     3`,
    `                       [[t1  + 9 s3  + s2  - 3 s3 t1, 6]]`,
    ``,
    `> protocol_level:=2:`,
    `> # The hard way:`,
    `> represent([t1**3,9]);`,
    `Solving a system of 10 linear equations in 4 unknowns ...`,
    `                                 3       3        3`,
    `                          - 27 s3  - 3 s2  s3 - s2  t1`,
    ``,
    `> # The shortcut:`,
    `> derive([t1**3,9]);`,
    `Try to derive   t1**3   from the known relations ...`,
    `... succeeded!`,
    `                                 3       3        3`,
    `                          - 27 s3  - 3 s2  s3 - s2  t1`,
    ``,
    ``,
    ``,
    `SEE ALSO:  invar, known, protocol_level, showseconds, showrels, represent`
    ):
`help/invar/text/derive`:=`help/text/derive`:
`help/text/elements`:=TEXT(
    ``,
    `FUNCTION: elements - compute all elements of a finite linear group`,
    `   `,
    `CALLING SEQUENCE:`,
    `   elements(G);`,
    `   `,
    `PARAMETERS:`,
    `   G - (optional) a table defining the group (see ?grouptable; default is`,
    `                                                             _known[group])`,
    `   `,
    `SYNOPSIS:   `,
    `- elements computes all elements of G and writes them as a set into G[eles]. It `,
    `  returns nothing. If G turns out to be infinite, you will sit for ages ...`,
    ``,
    `- G is given as a grouptable. It is a finite linear group over Q or an `,
    `  algebraic number field. classes will work if G[geners] or G[permgroup] is `,
    `  defined. In the latter case, G is transformed into a linear group.`,
    ``,
    `- classes is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> G[geners]:={[[i,0],[0,-i]],[[0,1],[1,0]]}: G[minpol]:=i**2+1:`,
    `> protocol_level:=2:`,
    `> elements(G);`,
    `Calculating all elements of the group G ...`,
    `It has 8 elements.`,
    ``,
    `They are`,
    `      [  0  -1 ]  [ i   0  ]  [ 0  1 ]  [ 1  0 ]  [  0   i ]  [ -1   0 ]`,
    `      [        ], [        ], [      ], [      ], [        ], [        ],`,
    `      [ -1   0 ]  [ 0  - i ]  [ 1  0 ]  [ 0  1 ]  [ - i  0 ]  [  0  -1 ]`,
    ``,
    `          [ 0  - i ]  [ - i  0 ]`,
    `          [        ], [        ]`,
    `          [ i   0  ]  [  0   i ]`,
    ``,
    `SEE ALSO:  invar, grouptable, protocol_level`
    ):
`help/invar/text/elements`:=`help/text/elements`:
`help/text/elesym`:=TEXT(
    ``,
    `SHORT HELP FOR: elesym`,
    ``,
    `elesym(k,vars) - the k'th elementary symmetric function in the variables vars`,
    `                 (optional, default  _known[xvars])`,
    ``
    ):
`help/invar/text/elesym`:=`help/text/elesym`:
`help/text/generators`:=TEXT(
    ``,
    `FUNCTION: generators - retrieve the generators from a grouptable`,
    `   `,
    `CALLING SEQUENCE:`,
    `   generators(G);`,
    `   `,
    `PARAMETERS:`,
    `   G - (optional) a table defining the group (see ?grouptable; default is`,
    `                                                             _known[group])`,
    `   `,
    `SYNOPSIS:   `,
    `- generators does nothing but read the entry G[geners] which contains a set of `,
    `  generators of G. Each generator is a matrix represented as a list of lists.`,
    ``,
    `- generators is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> protocol_level:=-1:`,
    `> invring(V4);`,
    `> generators();`,
    ``,
    `  {[[-1, 0, 0], [-1, 0, 1], [-1, 1, 0]], [[0, -1, 1], [0, -1, 0], [1, -1, 0]]}`,
    ``,
    `> map(array,");`,
    ``,
    `                           [ -1  0  0 ]  [ 0  -1  1 ]`,
    `                           [          ]  [          ]`,
    `                          {[ -1  0  1 ], [ 0  -1  0 ]}`,
    `                           [          ]  [          ]`,
    `                           [ -1  1  0 ]  [ 1  -1  0 ]`,
    ``,
    `SEE ALSO:  invar, grouptable, invring, protocol_level`
    ):
`help/invar/text/generators`:=`help/text/generators`:
`help/text/inv`:=TEXT(
    ``,
    `SHORT HELP FOR: inv`,
    ``,
    `inv(x,G) - the inverse of a matrix x which lies in the group defined by G`,
    `           (optional, default  _known[group])`,
    ``
    ):
`help/invar/text/inv`:=`help/text/inv`:
`help/text/invarspace`:=TEXT(
    ``,
    `FUNCTION: invarspace - a basis for the space of invariants of given degree`,
    `   `,
    `CALLING SEQUENCE:`,
    `   invarspace(deg,char,known);`,
    `   `,
    `PARAMETERS:`,
    `   deg   - the degree, a non negative integer`,
    `   char  - (optional) linear character (see below; default is one)`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- The set of pseudo-invariants of some weight of given degree deg is a finite `,
    `  dimensional vector space over the ground field. invarspace calculates a basis `,
    `  of this vector space.`,
    ``,
    `- invarspace takes the information about the group and the variables to `,
    `  calculate with from the argument known, which must contain at least entries `,
    `  at known[group][geners] or known[group][permgroup] and at known[vars]. A `,
    `  linear character is defined either as a set or list of equations relating the `,
    `  elements of known[group][geners] to character values or as a list of `,
    `  character values matching known[group][classes]. invarspace returns a list of `,
    `  polynomials.`,
    ``,
    `- If char=one and the group comes from a permutation group, then the projection `,
    `  (see ?proj) of the elementary symmetric polynomial of degree deg is taken as `,
    `  first basis element, if unequal to zero.`,
    ``,
    `- invarspace is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> known[group][geners]:={[[0,1],[-1,0]]}:`,
    `> known[vars]:=[x,y]:`,
    `> invarspace(2,1,known);`,
    `Calculating the space of invariants of degree 2.`,
    `There is one linearly independent invariant ("I2_1").`,
    ``,
    `                                     2    2`,
    `                                   [y  + x ]`,
    ``,
    `> invarspace(2,{[[0,1],[-1,0]]=-1},known);`,
    `Calculating the space of pseudo-invariants of degree 2.`,
    `There are 2 linearly independent invariants ("I2_1" through "I2_2").`,
    ``,
    `                                         2    2`,
    `                                [x y, - y  + x ]`,
    ``,
    ``,
    `SEE ALSO:  invar, known, proj`,
    ``
    ):
`help/invar/text/invarspace`:=`help/text/invarspace`:
`help/text/invring`:=TEXT(
    ``,
    `FUNCTION: invring - calculate the ring of invariants of a group`,
    `   `,
    `CALLING SEQUENCE:`,
    `   invring(G);`,
    `   invring(G,flags,'known');`,
    `   `,
    `PARAMETERS:`,
    `   G       - the group, given as permgroup(...) or lingroup(...)`,
    `   inter   - (optional) string or set of strings`,
    `   'known' - (optional) unevaluated name (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- invring calculates a Cohen-Macaulay basis and syzygies for the ring of `,
    `  invariants of a group G, which is either a permutation group or a finite `,
    `  linear group over the rationals or an algebraic number field. These are the `,
    `  steps that invring performs:`,
    `  `,
    `  * First, if G is given as a permutation group, invring transforms it into a `,
    `    linear group.`,
    `  * Then it splits off unit-characters which might be contained in G. (For `,
    `    example, if G is given as a permutation group, it always acts trivially on `,
    `    the vector (1,..,1)). In other words, some primary generators are chosen a `,
    `    priory and new variables are introduced (see ?onesoff). I call these `,
    `    primary generators "outer primaries", since they won't occur in any of the `,
    `    relations. The benefit of this step is fewer variables and much simpler `,
    `    relations to deal with. From now on, the new G is taken.`,
    `  * invring computes the poincare-series and goes through its representations `,
    `    that might mirror a Cohen-Macaulay basis, starting with the one containing `,
    `    the lowest degrees.`,
    `  * For each form of the poincare-series, invring tries to find a system of `,
    `    primary generators corresponding to it. Only if this attempt is `,
    `    unsuccessful (which seems to be exceedingly rare) does invring proceed to `,
    `    the next form at all. The primaries will be algebraically independent.`,
    `  * invring finds a basis of invariants, such that the invariant ring is a free `,
    `    module over the polynomial ring generated by the primary generators with `,
    `    this basis. The basis elements themselves are products of invariants `,
    `    t1,t2, ...("secondary generators"), and the number of ti's will be minimal `,
    `    with respect to the system of primaries chosen.`,
    `  * At last, invring calculates a minimal system of relations between the `,
    `    secondary generators. This system will be rich enough to yield a `,
    `    presentation for the ring of invariants. So now you can consider the ring `,
    `    of invariants as being calculated.`,
    `    This last step is not performed if you set the flag 'norels' (see below).`,
    ``,
    `- The ground field is Q or the algebraic number field specified in the `,
    `  definition of the group.`,
    ``,
    `- invring gives symbolic names to the primary and secondary generators `,
    `  (s1,s2,... and t1,t2,...). This is very convenient for displaying syzygies `,
    `  and so on. For information on how to get their real values, see ?valueof.`,
    ``,
    `- During execution, invring prints out a protocol informing the user about `,
    `  everything that is happening. If you want less protocol, set the global `,
    `  variable protocol_level:=0, and if you want none at all, set `,
    `  protocol_level:=-1.`,
    ``,
    `- invring creates a table in which all knowledge acquired so far is stored. If `,
    `  a name is specified as third argument, it is assigned this knowledge-table. `,
    `  Otherwise, the table will be called  _known. In this way, all subroutines `,
    `  called by invring receive all available information, and the user has an easy `,
    `  access to it for subsequent calculations. On the other hand, invring doesn't `,
    `  return any result. (See ?known)`,
    ``,
    `- You can use a second argument to set flags. Flags are the strings 'inter' and `,
    `  'norels'. For the meaning of 'inter' see below. To set a flag you pass it as `,
    `  second argument, or you pass a set of flags.`,
    ``,
    `- There might arise problems when searching primary generators: Since it can't `,
    `  be known whether for a given representation of the poincare-series there is a `,
    `  corresponding system of primaries, invar just makes ten attempts to construct `,
    `  such a system and then gives up and proceeds to the next representation. Now `,
    `  if you aren't quite satisfied with this policy, you can set the flag 'inter'. `,
    `  Then invar will interactively seek council after each unsuccessful guess for `,
    `  the primaries. (see ?primaries)`,
    ``,
    `- For some background, information about the algorithms and some references see `,
    `  the LaTeX-file which should come along with the invar package.`,
    ``,
    `- invring is part of the invar package.`,
    `   `,
    `EXAMPLES:   `,
    `> # A few permutation group such as Z3 are defined within the invar package`,
    `> invring(Z3);`,
    ``,
    `CALCULATING THE INVARIANT RING OF`,
    `   G = permgroup(3,{[[1, 2, 3]]})`,
    ``,
    ``,
    `Calculating all elements of the group G ...`,
    `It has 3 elements.`,
    ``,
    `Splitting off unit-characters ...`,
    ``,
    `"Outer" primary generators:`,
    `   s1 = x1+x2+x3`,
    ``,
    `New variables:`,
    `   y1 = 2*x1-x2-x3`,
    `   y2 = -x1+2*x2-x3`,
    `Done with splitting off unit-characters!`,
    ``,
    `Finding primary generators ...`,
    ``,
    `Calculating the poincare-series ...`,
    `It is`,
    `                                                 2`,
    `                                      - s + 1 + s`,
    `                          P(s) = ---------------------`,
    `                                        2           2`,
    `                                 (s - 1)  (s + 1 + s )`,
    ``,
    ``,
    `Try the representation`,
    `                                          3`,
    `                                         s  + 1`,
    `                            P(s) = -----------------`,
    `                                         2        3`,
    `                                   (1 - s ) (1 - s )`,
    ``,
    ``,
    `Calculating the space of invariants of degree 2.`,
    `There is one linearly independent invariant ("I2_1").`,
    ``,
    `Calculating the space of invariants of degree 3.`,
    `There are 2 linearly independent invariants ("I3_1" through "I3_2").`,
    ``,
    `Trying invariants   s2 = I2_1   s3 = I3_1`,
    `They come from the elementary-symmetric functions:`,
    `No further checking necessary!!`,
    ``,
    `The primaries are (by their symbolic names):`,
    `   s1 of degree 1  (outer primary)`,
    `   s2 of degree 2`,
    `   s3 of degree 3`,
    ``,
    `Done with finding primary generators`,
    ``,
    `Finding secondary generators ...`,
    ``,
    `We'll get a basis of invariants of degrees 0,3.`,
    ``,
    `It starts with 1 (ever so amazing!)`,
    ``,
    `Trying invariant I3_2 of degree 3`,
    `Solving a system of 4 linear equations in 1 unknowns ...`,
    `We take it and call it "t1"!`,
    ``,
    `The secondary generators are:`,
    `   t1 of degree 3`,
    ``,
    `The invariant ring is  a free module over Q[{primaries}] with basis`,
    `    1    of degree 0`,
    `    t1    of degree 3`,
    ``,
    `Ready with finding secondary generators!`,
    ``,
    `Finding relations between secondary generators ...`,
    ``,
    `Degree 6:`,
    `calculating    t1**2`,
    `Solving a system of 6 linear equations in 3 unknowns ...`,
    `t1**2 = -9*s3**2-s2**3-3*t1*s3`,
    ``,
    `The relations are:`,
    `    t1**2+9*s3**2+s2**3+3*t1*s3   of degree 6`,
    ``,
    `Ready with finding relations!`,
    ``,
    `Time used:   6.400   seconds`,
    ``,
    `> invring(lingroup({[[i,0],[0,-i]]},i**2+1),norels,'known');    # non-interrupt`,
    ``,
    `CALCULATING THE INVARIANT RING OF`,
    `   G = lingroup({[[i, 0], [0, -i]]},i**2+1)`,
    ``,
    ``,
    `Calculating all elements of the group G ...`,
    `It has 4 elements.`,
    ``,
    `Splitting off unit-characters ...`,
    `There is none.`,
    ``,
    `Finding primary generators ...`,
    ``,
    `Calculating the poincare-series ...`,
    `It is`,
    `                                             4`,
    `                                        1 + s`,
    `                       P(s) = --------------------------`,
    `                                     2       2         2`,
    `                              (s - 1)  (1 + s ) (s + 1)`,
    ``,
    ``,
    `Try the representation`,
    `                                              4`,
    `                                         1 + s`,
    `                            P(s) = -----------------`,
    `                                         2        4`,
    `                                   (1 - s ) (1 - s )`,
    ``,
    ``,
    `Calculating the space of invariants of degree 2.`,
    `There is one linearly independent invariant ("I2_1").`,
    ``,
    `Calculating the space of invariants of degree 4.`,
    `There are 3 linearly independent invariants ("I4_1" through "I4_3").`,
    ``,
    `Trying invariants   s1 = I2_1   s2 = I4_1`,
    `Not so good: There are common zeroes other than (0,..,0))`,
    `Trying invariants   s1 = I2_1   s2 = I4_2`,
    `Not so good: There are common zeroes other than (0,..,0))`,
    `Trying invariants   s1 = I2_1   s2 = I4_3`,
    `Not so good: There are common zeroes other than (0,..,0))`,
    `Trying invariants   s1 = I2_1   s2 = I4_1+I4_2`,
    `Not so good: There are common zeroes other than (0,..,0))`,
    `Trying invariants   s1 = I2_1   s2 = -I4_1+I4_2`,
    `Not so good: There are common zeroes other than (0,..,0))`,
    `Trying invariants   s1 = I2_1   s2 = I4_1+I4_3`,
    `OH HAPPY DAY: They qualify!! (No common zeroes other than [0,..,0])`,
    ``,
    `The primaries are (by their symbolic names):`,
    `   s1 of degree 2`,
    `   s2 of degree 4`,
    ``,
    `Done with finding primary generators`,
    ``,
    `Finding secondary generators ...`,
    ``,
    `We'll get a basis of invariants of degrees 0,4.`,
    ``,
    `It starts with 1 (ever so amazing!)`,
    ``,
    `Trying invariant I4_1 of degree 4`,
    `Solving a system of 3 linear equations in 2 unknowns ...`,
    `We take it and call it "t1"!`,
    ``,
    `The secondary generators are:`,
    `   t1 of degree 4`,
    ``,
    `The invariant ring is  a free module over Q[{primaries}] with basis`,
    `    1    of degree 0`,
    `    t1    of degree 4`,
    ``,
    `Ready with finding secondary generators!`,
    ``,
    `Calculation of relations not desired.`,
    ``,
    `Time used:   14.433   seconds`,
    ``,
    `SEE ALSO:  invar, permgroup, lingroup, known, protocol_level`
    ):
`help/invar/text/invring`:=`help/text/invring`:
`help/text/lingroup`:=TEXT(
    `   `,
    `FUNCTION: lingroup - definition of a linear group`,
    `   `,
    `CALLING SEQUENCE:`,
    `   lingroup(gens,minpol);`,
    `   `,
    `PARAMETERS:`,
    `   gens   - a set of matrices given as lists of lists`,
    `   minpol - (optional) the polynomial defining an algebraic number field`,
    `   `,
    `SYNOPSIS:   `,
    `- A linear group is defined by giving a generating set of square matrices. The `,
    `  matrices are given as lists of lists:`,
    ``,
    `        [ a  b ]`,
    `        [      ]   is represented as [[a,b],[c,d]].`,
    `        [ c  d ]`,
    ``,
    `- The entries of the matrices must be rational or in an algebraic number field. `,
    `  In the latter case, they are polynomials in some variable a which generates `,
    `  the number field, and minpol is the polynomial which is satisfied by a.`,
    ``,
    `- lingroup checks if the arguments are of the right types and then either exits `,
    `  with an error or returns `` lingroup``(args), the unassigned function name `,
    `  `` lingroup`` with the arguments you gave. lingroup does not check the group `,
    `  for finiteness!`,
    ``,
    `- lingroup is part of the invar package.`,
    `   `,
    `EXAMPLES:   `,
    `> lingroup({[[0,1],[-1,0]],[[-1,0],[0,1]]});`,
    ``,
    `                lingroup({[[0, 1], [-1, 0]], [[-1, 0], [0, 1]]})`,
    ``,
    `> lingroup({[[i,0],[0,-i]]},i**2+1);`,
    ``,
    `                                                     2`,
    `                     lingroup({[[i, 0], [0, - i]]}, i  + 1)`,
    ``,
    `> lingroup({[[0,1],[1,0,1]]});`,
    `Error, (in lingroup) Bad first argument!`,
    ``,
    `SEE ALSO:  invar, permgroup`
    ):
`help/invar/text/lingroup`:=`help/text/lingroup`:
`help/text/mklingroup`:=TEXT(
    `   `,
    `FUNCTION: mklingroup - make a linear group from a permutation group`,
    `   `,
    `CALLING SEQUENCE:`,
    `   mklingroup(G);`,
    `   `,
    `PARAMETERS:`,
    `   G - (optional) a table defining the group (see ?grouptable; default is`,
    `                                                             _known[group])`,
    `   `,
    `SYNOPSIS:   `,
    `- mklingroup makes a group G which is possibly given as a permutation group `,
    `  into a linear group. It then adds some additional information, namely the `,
    `  degree and the identity element. If G already is a linear group, only this `,
    `  additional information is added.`,
    ``,
    `- G must be a table having (at least) values at G[permgroup] or G[geners]. `,
    `  In the first case, G[permgroup] must be of the form permgroup(<deg>, <gens>) `,
    `  or a list [<deg>, <gens>]. mklingroup returns nothing but assigns values to `,
    `  certain entries of G.`,
    ``,
    `- mklingroup is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> G[permgroup]:=[3,{[[1,2]]}]:`,
    `> mklingroup(G);`,
    `> print(G);`,
    ``,
    `              table([`,
    `                  identity = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]`,
    `                  permgroup = [3, {[[1, 2]]}]`,
    `                  geners = {[[0, 1, 0], [1, 0, 0], [0, 0, 1]]}`,
    `                  degree = 3`,
    `              ])`,
    ``,
    `> array(G[geners][1]);`,
    ``,
    `                                  [ 0  1  0 ]`,
    `                                  [         ]`,
    `                                  [ 1  0  0 ]`,
    `                                  [         ]`,
    `                                  [ 0  0  1 ]`,
    ``,
    ``,
    ``,
    `SEE ALSO:  invar, grouptable, permgroup`
    ):
`help/invar/text/mklingroup`:=`help/text/mklingroup`:
`help/text/monoms`:=TEXT(
    `   `,
    `FUNCTION: monoms - all products of given degree`,
    `   `,
    `CALLING SEQUENCE:`,
    `   monoms(deg,(deg,polys_with_degrees));`,
    `   `,
    `PARAMETERS:`,
    `   deg                - a non negative integer`,
    `   polys_with_degrees - (optional) a list of lists containing expressions and`,
    `                                      their degrees (default is _known[vars])`,
    `   `,
    `SYNOPSIS:   `,
    `- Given a list of expressions (usually just names) and degrees that the user `,
    `  assigns to them, monoms calculates all products of these expressions which `,
    `  are of a given degree deg.`,
    ``,
    `- The argument polys_with_degrees is a list of lists. Each list consists of an `,
    `  expression and the degree assigned to it. The result of monoms is of the same `,
    `  format.`,
    ``,
    `- monoms is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> monoms(3,[[s1,1],[s2,2]]);`,
    ``,
    `                                             3`,
    `                             [[s1 s2, 3], [s1 , 3]]`,
    ``,
    `SEE ALSO:  invar, `
    ):
`help/invar/text/monoms`:=`help/text/monoms`:
`help/text/monoms_base`:=TEXT(
    ``,
    `FUNCTION: monoms_base - a combinatorial function`,
    `   `,
    `CALLING SEQUENCE:`,
    `   monoms_base(deg,indep,base);`,
    `   `,
    `PARAMETERS:`,
    `   deg   - a nonnegative integer`,
    `   indep - a list of lists containing expressions and their degrees`,
    `   base  - same as indep (other semantics, though!)`,
    `   `,
    `SYNOPSIS:   `,
    `- Given expressions f_1...f_n and g_1..g_m, monoms_base calculates all products `,
    `  of the form`,
    `    (product of some f_j's) * g_i`,
    `  which are of a given degree deg.`,
    ``,
    `- The argument indep is a list with one entry for each f_i. The f_i's are `,
    `  represented by lists consisting of an expression (usually just a name) and `,
    `  the degree that the user assigns to it. The g_i's are given by the argument `,
    `  base in the same format, and the output of monoms_base is also of this `,
    `  format.`,
    ``,
    `- monoms_base is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> monoms_base(3,[[s1,1],[s2,2]],[[1,0],[t2,2]]);`,
    ``,
    `                                       3`,
    `                       [[s1 s2, 3], [s1 , 3], [s1 t2, 3]]`,
    ``,
    `SEE ALSO:  invar, `
    ):
`help/invar/text/monoms_base`:=`help/text/monoms_base`:
`help/text/mul`:=TEXT(
    ``,
    `SHORT HELP FOR: mul`,
    ``,
    `mul(a,b,minpol) - multiply matrices a and b, reduce result modulo minpol `,
    `                                                               (optional)`,
    ``
    ):
`help/invar/text/mul`:=`help/text/mul`:
`help/text/onesoff`:=TEXT(
    ``,
    `FUNCTION: onesoff - split off unit characters from G`,
    `   `,
    `CALLING SEQUENCE:`,
    `   onesoff(s_name,known);`,
    `   `,
    `PARAMETERS:`,
    `   s_name - (optional) name (default is s)`,
    `   known  - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- onesoff is one of the subroutines called by invring during the calculation of `,
    `  the ring of invariants. It splits off unit characters that are possibly `,
    `  included in the linear group G. If this is the case, a change of variables is `,
    `  performed, getting new variables s1,s2,... and y1,y2,... , such that G acts `,
    `  trivially on the si's and G acts as a linear group on the yi's. This linear `,
    `  group is taken as the new G, and the si's are taken as ("outer") primary `,
    `  generators. This step provides two enormous computational benefits: First, `,
    `  there are fewer variables to be calculated with from now on, and second, the `,
    `  outer primaries will not occur in any relation (thence "outer").`,
    ``,
    `- known must (at least) have the values known[xvars] and `,
    `  known[group][permgroup] or known[group][geners]. onesoff adds information `,
    `  to the known-table and returns a list containing the s_name and the number of `,
    `  unit characters that it has split off.`,
    ``,
    `- onesoff is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> known[group][permgroup]:=[4,{[[1,2]],[[3,4]]}]: `,
    `> known[xvars]:=[x.(1..4)]:`,
    `> onesoff(s,known);`,
    `Calculating all elements of the group G ...`,
    `It has 4 elements.`,
    ``,
    `Splitting off unit-characters ...`,
    ``,
    `"Outer" primary generators:`,
    `   s1 = x1+x2`,
    `   s2 = x3+x4`,
    ``,
    `New variables:`,
    `   y1 = x1-x2`,
    `   y2 = x3-x4`,
    `Done with splitting off unit-characters!`,
    ``,
    `                                     [s, 2]`,
    ``,
    `> # Let's have a look at the new group!`,
    `> map(array,known[group][geners]);`,
    ``,
    `                              [ -1  0 ]  [ 1   0 ]`,
    `                             {[       ], [       ]}`,
    `                              [  0  1 ]  [ 0  -1 ]`,
    ``,
    `SEE ALSO:  invar, invring, known`
    ):
`help/invar/text/onesoff`:=`help/text/onesoff`:
`help/text/out`:=TEXT(
    ``,
    `SHORT HELP FOR: out`,
    ``,
    `out(n,expr_1,expr_2,...) or out(n,print,expr_1,expr_2,...) -`,
    `       line-print or pretty-print a line of protocol if protocol_level>=n`,
    `                                                        (optional; default 1)`,
    ``
    ):
`help/invar/text/out`:=`help/text/out`:
`help/text/poincare`:=TEXT(
    `   `,
    `FUNCTION: poincare - compute the poincare-series of a group`,
    `   `,
    `CALLING SEQUENCE:`,
    `   poincare();`,
    `   poincare(char,svar,G);`,
    `   `,
    `PARAMETERS:`,
    `   char - (optional) list defining a character (default is one)`,
    `   svar - (optional) name specifying the variable for the series (default 's')`,
    `   G    - (optional) a table defining the group (see ?grouptable; default is`,
    `                                                             _known[group])`,
    `   `,
    `SYNOPSIS:   `,
    `- poincare computes the poincare-series of the ring of invariants of G by using `,
    `  Molien's formula.`,
    ``,
    `- G can be a finite linear group over a number field or a permutation group, `,
    `  given by a table containing a grouptable data structure. G must have at least `,
    `  the entry G[permgroup] or G[geners]. You don't have to supply G if you `,
    `  have run invring(...) before with no third argument.`,
    ``,
    `- The calculation may be "weighted" by a character of G. Then the coefficients `,
    `  of the taylor expansion of the poincare-series count the multiplicities of `,
    `  this character in the representation of G acting on the homogeneous subspaces `,
    `  of the polynomial ring. char is given as a list of values corresponding to `,
    `  the entries of classes(G).`,
    `  `,
    `- poincare prints out a protocol during execution which can be suppressed by `,
    `  setting the global variable protocol_level:=-1.`,
    `  `,
    `- poincare is part of the invar package.`,
    `   `,
    `EXAMPLES:   `,
    `> protocol_level:=-1:`,
    `> Z2:=permgroup(2,{[[1,2]]}):`,
    `> G[permgroup]:=Z2:`,
    `> poincare(1,s,G);`,
    ``,
    `                                        1`,
    `                                ----------------`,
    `                                       2`,
    `                                (s - 1)  (s + 1)`,
    ``,
    `> taylor(",s);`,
    ``,
    `                              2      3      4      5      6`,
    `                   1 + s + 2 s  + 2 s  + 3 s  + 3 s  + O(s )`,
    ``,
    `> invring(Z2);`,
    `> classes();`,
    ``,
    `                                [[[-1]], [[1]]]`,
    ``,
    `> taylor(poincare([-1,1]),s);                   # The non trivial character`,
    ``,
    `                                   3    5      6`,
    `                              s + s  + s  + O(s )`,
    ``,
    ``,
    `SEE ALSO:  grouptable, protocol_level, invar`
    ):
`help/invar/text/poincare`:=`help/text/poincare`:
`help/text/poinrep`:=TEXT(
    ``,
    `FUNCTION: poinrep - find special representations of the poincare-series`,
    `   `,
    `CALLING SEQUENCE:`,
    `   poinrep(last_exp,known);`,
    `   `,
    `PARAMETERS:`,
    `   last_exp - (optional) list of two lists of integers`,
    `                                          (default [[0,..,0],[...]])`,
    `   known    - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- For the poincare-series of a given group there are many representations which `,
    `  might mirror a Cohen-Macaulay basis of the ring of invariants, namely `,
    `  representations of the form`,
    ``,
    `                              t_1          t_r`,
    `                             s    + ... + s`,
    `                         ------------------------`,
    `                               s_1          s_n     ,`,
    `                          (1-s)    ... (1-s)`,
    ``,
    `  with nonnegative s_i and t_i. Now the smaller the s_i, the better the `,
    `  corresponding Cohen-Macaulay basis (because the s_i's are the degrees of the `,
    `  primary generators). Given some values for the s_i, poinrep finds the `,
    `  "next-worse" representation. The best representation is obtained by giving `,
    `  zeroes for the s_i.`,
    ``,
    `- poinrep returns a list of two lists: The first one are the s_i and the second `,
    `  one the t_i. The first argument (last_exp) should be of the same form. The `,
    `  second argument (known) must (at least) have values known[svar] and `,
    `  known[group][permgroup] or known[group][geners].`,
    ``,
    `- poinrep is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> known[group][permgroup]:=[4,{[[1,2],[3,4]],[[1,3],[2,4]]}]:                `,
    `> known[svar]:=s:`,
    `> # Get the best representation`,
    `> poinrep([[0,0,0,0],[irrelevant]],known);`,
    `Calculating all elements of the group G ...`,
    `It has 4 elements.`,
    ``,
    `Calculating the poincare-series ...`,
    `It is`,
    `                                       2`,
    `                                      s  - s + 1`,
    `                        P(s) = ------------------------`,
    `                                       2    4         2`,
    `                               (1 - 2 s  + s ) (s - 1)`,
    ``,
    ``,
    `Try the representation`,
    `                                          3`,
    `                                         s  + 1`,
    `                           P(s) = -------------------`,
    `                                                  2 3`,
    `                                  (- s + 1) (1 - s )`,
    ``,
    ``,
    `                             [[1, 2, 2, 2], [0, 3]]`,
    ``,
    `> # ... and the second-best`,
    `> poinrep(",known);`,
    `Try the representation`,
    `                                     4    3`,
    `                                    s  + s  + s + 1`,
    `                             P(s) = ---------------`,
    `                                             2 4`,
    `                                       (1 - s )`,
    ``,
    ``,
    `                          [[2, 2, 2, 2], [0, 1, 3, 4]]`,
    ``,
    `> poinrep(",known);`,
    `Try the representation`,
    `                                       4    2`,
    `                                      s  + s  + 1`,
    `                      P(s) = ----------------------------`,
    `                                             2 2       3`,
    `                             (- s + 1) (1 - s )  (1 - s )`,
    ``,
    ``,
    `                           [[1, 2, 2, 3], [0, 2, 4]]`,
    ``,
    `SEE ALSO:  invar, known, poincare`
    ):
`help/invar/text/poinrep`:=`help/text/poinrep`:
`help/text/showprims`:=`help/text/showbasis`:
`help/invar/text/showprims`:=`help/text/showprims`:
`help/text/primaries`:=TEXT(
    ``,
    `FUNCTION: primaries - find primary generators for the ring of invariants`,
    `   `,
    `CALLING SEQUENCE:`,
    `   primaries(s_name,inter,known);`,
    `   `,
    `PARAMETERS:`,
    `   s_name - (optional) list containing a name and an integer (default [s,0])`,
    `   inter  - (optional) interrupt-flag; set iff then string 'inter' is passed`,
    `   known  - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- primaries is one of the subroutines called by invring during the calculation `,
    `  of the ring of invariants. It finds primary generators, i. e. homogeneous `,
    `  invariants s1,...,sn which are algebraically independent and such that the `,
    `  ring of invariants is a free module over K[s1,...,sn].`,
    ``,
    `- The degrees of the primaries are mirrored by some form of the `,
    `  poincare-series, so this is calculated first. Then primaries tries to find `,
    `  primary generators corresponding to the best of these forms (see ?poinrep). `,
    `  primaries does ten attempts for this and, if unsuccessful, passes to the next `,
    `  form. But if the interrupt-flag is given, primaries will stop after each `,
    `  unsuccessful attempt and interactively ask the user if it shall go on trying, `,
    `  give up and pass to the next form of the poincare-series or make a guess of `,
    `  the user's desire.`,
    ``,
    `- The first argument should be a list containing the name which the primaries `,
    `  shall be given and the number of "outer primaries" (see ?onesoff). the table `,
    `  known must (at least) have values known[svar], known[group][permgroup] or `,
    `  known[group][geners] and known[vars]. primaries adds information to known `,
    `  and returns a list containing the degrees of the elements of the basis which `,
    `  spans the ring of invariants as a free module over K[{primaries}].`,
    ``,
    `- primaries is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> known[svar]:=s: known[group][geners]:={[[i,0],[0,-i]]}:`,
    `> known[group][minpol]:=i**2+1: known[vars]:=[x1,x2]:`,
    `> protocol_level:=2:`,
    `> primaries([s,0],inter,known);`,
    `Finding primary invariants ...`,
    ``,
    `Calculating all elements of the group G ...`,
    `It has 4 elements.`,
    ``,
    `They are`,
    `                  [ i   0  ]  [ 1  0 ]  [ -1   0 ]  [ - i  0 ]`,
    `                  [        ], [      ], [        ], [        ]`,
    `                  [ 0  - i ]  [ 0  1 ]  [  0  -1 ]  [  0   i ]`,
    ``,
    `Calculating the poincare-series ...`,
    `It is`,
    `                                         4`,
    `                                        s  + 1`,
    `                           P(s) = ------------------`,
    `                                     4    2        6`,
    `                                  - s  - s  + 1 + s`,
    ``,
    ``,
    `Try the representation`,
    `                                          4`,
    `                                         s  + 1`,
    `                            P(s) = -----------------`,
    `                                         2        4`,
    `                                   (1 - s ) (1 - s )`,
    ``,
    ``,
    `Calculating the space of invariants of degree 2.`,
    `There is one linearly independent invariant ("I2_1").`,
    `It is`,
    `   I2_1 = x1*x2`,
    ``,
    `Calculating the space of invariants of degree 4.`,
    `There are 3 linearly independent invariants ("I4_1" through "I4_3").`,
    `They are`,
    `   I4_1 = x2**4`,
    `   I4_2 = x1**4`,
    `   I4_3 = x1**2*x2**2`,
    ``,
    `Trying invariants   s1 = I2_1   s2 = I4_1`,
    `Not so good: There are common zeroes other than (0,..,0))`,
    ``,
    `'invar' is troubled and seeks counsel: Shall it`,
    `- proceed as before and try another combination (type ";" and RETURN)`,
    `- give up and pass to the next representation of the poincare-series`,
    `         (type "hopeless;" and RETURN)`,
    `- try a combination of Ii_j's of your desire (type it in with ";" and RETURN)`,
    `So ...> I2_1,I4_1+I4_2;                                       # I typed this!       `,
    `Trying invariants   s1 = I2_1   s2 = I4_1+I4_2`,
    `OH HAPPY DAY: They qualify!! (No common zeroes other than [0,..,0])`,
    ``,
    `The primaries are (by their symbolic names):`,
    `   s1 of degree 2`,
    `   s2 of degree 4`,
    ``,
    `Done with finding primary invariants`,
    ``,
    `                                     [0, 4]`,
    ``,
    `SEE ALSO:  invar, poinrep, onesoff, protocol_level, known`
    ):
`help/invar/text/primaries`:=`help/text/primaries`:
`help/text/proj`:=TEXT(
    ``,
    `FUNCTION: proj`,
    `   `,
    `CALLING SEQUENCE:`,
    `   proj(expr,known);`,
    `   `,
    `PARAMETERS:`,
    `   expr  - an expression in the x-variables`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- After onesoff has been applied to a group in the course of calculating its `,
    `  ring of invariants, there are possibly new variables s1,...,sr (the "outer `,
    `  primaries") and y1,y2,...`,
    `  proj expresses an expression in the x-variables x1,x2,... by these new `,
    `  variables and then sets s1=...=sr=0. Thus the expression is actually changed, `,
    `  not just a mere change of variables!`,
    ``,
    `- The table known must (at least) have values known[vars], known[xvars] and `,
    `  known[trans][proj]. The last of these gives the projection onto the subspace `,
    `  that has the one-characters of the group stripped off.`,
    ``,
    `- proj is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> known[group][permgroup]:=[4,{[[1,2]],[[3,4]]}]: known[xvars]:=[x.(1..4)]:`,
    `> onesoff(s,known);`,
    `Calculating all elements of the group G ...`,
    `It has 4 elements.`,
    ``,
    `Splitting off unit-characters ...`,
    ``,
    `"Outer" primary invariants:`,
    `   s1 = x1+x2`,
    `   s2 = x3+x4`,
    ``,
    `New variables:`,
    `   y1 = x1-x2`,
    `   y2 = x3-x4`,
    `Done with splitting off unit-characters!`,
    ``,
    `                                     [s, 2]`,
    ``,
    `> array(known[trans][proj]);`,
    ``,
    `                            [ 1/2  -1/2   0     0  ]`,
    `                            [                      ]`,
    `                            [  0     0   1/2  -1/2 ]`,
    ``,
    `> proj(x1^2,known);`,
    ``,
    `                                          2`,
    `                                    1/4 y1`,
    ``,
    `> proj(x2^2,known);`,
    ``,
    `                                          2`,
    `                                    1/4 y1`,
    ``,
    `SEE ALSO:  invar, known, onesoff, renormal`
    ):
`help/invar/text/proj`:=`help/text/proj`:
`help/text/reci`:=TEXT(
    ``,
    `FUNCTION: reci - reciprocal of an algebraic number`,
    `   `,
    `CALLING SEQUENCE:`,
    `   reci(x,minpol);`,
    `   `,
    `PARAMETERS:`,
    `   x      - a polynomial over the rationals`,
    `   minpol - (optional) univariate polynomial defining the number field`,
    ``,
    `SYNOPSIS:`,
    `- Let a number field be given by an irreducible polynomial of degree d in a `,
    `  variable alpha over the rationals. If x is given as a polynomial in alpha, `,
    `  then reci returns a representation of 1/x as `,
    `    1/x = q_0+q_1*alpha+...+q_(d-1)*alpha^(d-1), `,
    `  where the q_i's don't contain alpha.`,
    ``,
    `- If there's no second argument, reci just returns 1/x. In the standard case, x `,
    `  is a polynomial in alpha over the rationals, but it may also contain other `,
    `  variables.`,
    ``,
    `- reci is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> reci(a,a^2+a+1);`,
    ``,
    `                                    - 1 - a`,
    ``,
    `SEE ALSO:  invar`
    ):
`help/invar/text/reci`:=`help/text/reci`:
`help/text/reduce`:=TEXT(
    ``,
    `FUNCTION: reduce - standard representation of an algebraic number`,
    `   `,
    `CALLING SEQUENCE:`,
    `   reduce(expr,G);`,
    `   `,
    `PARAMETERS:`,
    `   x - a polynomial over the rationals in the variable G[alg]`,
    `   G - (optional) a table defining the group (see ?grouptable; default is`,
    `                                                             _known[group])`,
    `   `,
    `SYNOPSIS:   `,
    `- Let G[alg] be a name, say a, and G[minpol] be a polynomial of degree d over `,
    `  the rationals satisfied by a. Then for a member of Q(a) given as a polynomial `,
    `  x over the rationals in a we have x=q_0+q_1*a+...+q_(d-1)*a^(d-1) with q_i `,
    `  in Q. reduce calculates this representation.`,
    ``,
    `- reduce is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> G[alg]:=a: G[minpol]:=a^2+a+1:`,
    `> reduce(a^2,G);`,
    ``,
    `                                    - 1 - a`,
    ``,
    `SEE ALSO:  invar,  grouptable`
    ):
`help/invar/text/reduce`:=`help/text/reduce`:
`help/text/showrels`:=`help/text/showbasis`:
`help/invar/text/showrels`:=`help/text/showrels`:
`help/text/relations`:=TEXT(
    ``,
    `FUNCTION: relations - calculate the syzygies`,
    `   `,
    `CALLING SEQUENCE:`,
    `   relations(known);`,
    `   `,
    `PARAMETERS:`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- relations is one of the subroutines called by invring during the calculation `,
    `  of the ring of invariants. After a Cohen-Macaulay basis of the ring of `,
    `  invariants has been found, relations calculates the relations between the `,
    `  secondary generators. More precisely: It calculates so many relations that `,
    `  all products ti*tj of secondaries are known in terms of the Cohen-Macaulay `,
    `  basis.`,
    `  Also, the result of relations is a set of minimal order which spans the `,
    `  kernel of `,
    `    K[S1,s2,... ,T1,T2,...] -> (ring of invariants), Si |-> si, Ti |-> ti. `,
    `  So this yields a presentation of the ring of invariants!`,
    ``,
    `- The argument known is a table with quite a few entries providing information `,
    `  about the Cohen-Macaulay basis. relations writes its result into this table, `,
    `  too, and has the relations as output value. Since the s1,s2,... and t1,t2,... `,
    `  in the invar package are only names which stand for some invariants, the `,
    `  relations can be polynomials in these names (see ?showrels and ?valueof).`,
    ``,
    `- relations is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `   (See the examples for invring.)`,
    ``,
    `SEE ALSO:  invar, invring, known, showrels`
    ):
`help/invar/text/relations`:=`help/text/relations`:
`help/text/renormal`:=TEXT(
    `   `,
    `FUNCTION: renormal - express a polynomial in the x-variables`,
    `   `,
    `CALLING SEQUENCE:`,
    `   renormal(expr,known);`,
    `   `,
    `PARAMETERS:`,
    `   expr  - a polynomial in the y-variables y1,y2,...`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- After onesoff has been applied to a group in the course of calculating its `,
    `  ring of invariants, there are possibly new variables y1,y2,...`,
    `  renormal expresses a polynomial in the y-variables by the x-variables `,
    `  x1,x2,...`,
    ``,
    `- known must be a table containing at least the entries known[trans][newbasis], `,
    `  known[vars] and known[xvars]. This is provided for if known was generated by `,
    `  invring. known[trans][newbasis] contains the information how to get from the `,
    `  yi's to the xi's.`,
    `  `,
    `- renormal is part of the invar package.`,
    `   `,
    `EXAMPLES:   `,
    `> known[group][permgroup]:=[4,{[[1,2]],[[3,4]]}]: known[xvars]:=[x.(1..4)]:`,
    `> onesoff(s,known);`,
    `Calculating all elements of the group G ...`,
    `It has 4 elements.`,
    ``,
    `Splitting off unit-characters ...`,
    ``,
    `"Outer" primary invariants:`,
    `   s1 = x1+x2`,
    `   s2 = x3+x4`,
    ``,
    `New variables:`,
    `   y1 = x1-x2`,
    `   y2 = x3-x4`,
    `Done with splitting off unit-characters!`,
    ``,
    `                                     [s, 2]`,
    ``,
    `> array(known[trans][newbasis]);`,
    ``,
    `                                   [  1   0 ]`,
    `                                   [        ]`,
    `                                   [ -1   0 ]`,
    `                                   [        ]`,
    `                                   [  0   1 ]`,
    `                                   [        ]`,
    `                                   [  0  -1 ]`,
    ``,
    `> renormal(y1^2,known);`,
    ``,
    `                                2               2`,
    `                              x1  - 2 x1 x2 + x2`,
    ``,
    `SEE ALSO:  invar, known, onesoff`
    ):
`help/invar/text/renormal`:=`help/text/renormal`:
`help/text/repfrac`:=TEXT(
    ``,
    `FUNCTION: repfrac - represent a fractional invariant in terms of the `,
    `                                                Cohen-Macaulay basis`,
    `   `,
    `CALLING SEQUENCE:`,
    `   repfrac(expr,known);`,
    `   `,
    `PARAMETERS:`,
    `   expr  - a homogeneous rational function in known[vars]`,
    `                                     (y1,y2,... or x1,x2,...)`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- Let f/g be an invariant, with f and g polynomials. Then there exists a `,
    `  polynomial h such that h*f and h*g are invariants. repfrac constructs such an `,
    `  h of minimal degree and then represents h*f and h*g in terms of the `,
    `  Cohen-Macaulay basis which is given by the second argument known. The `,
    `  quotient is then returned.`,
    ``,
    `- The second argument known must be a table containing numerous entries which `,
    `  define the group and the Cohen-Macaulay basis.`,
    ``,
    `- repfrac is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> protocol_level:=-1:`,
    `> invring(Z4);`,
    `> protocol_level:=1:`,
    `> expr:=(y2*y3+y2**2+y1*y3+y1*y2)/(-y1*y2-3*y1*y3+y1**2-5*y2*y3-3*y2**2-y3**2);`,
    ``,
    `                                        2`,
    `                              y2 y3 + y2  + y1 y3 + y1 y2`,
    `            expr := -----------------------------------------------`,
    `                                          2                 2     2`,
    `                    - y1 y2 - 3 y1 y3 + y1  - 5 y2 y3 - 3 y2  - y3`,
    ``,
    `> repfrac(expr);`,
    `fraction extended by a factor of degree 1`,
    `Solving a system of 8 linear equations in 2 unknowns ...`,
    `Solving a system of 8 linear equations in 2 unknowns ...`,
    `                                        t2`,
    `                                 - -----------`,
    `                                   - t1 + 3 t2`,
    ``,
    `SEE ALSO:  invar, known, protocol_level`
    ):
`help/invar/text/repfrac`:=`help/text/repfrac`:
`help/text/represent`:=TEXT(
    ``,
    `FUNCTION: represent - represent an invariant in terms of the Cohen-Macaulay `,
    `                                                                      basis`,
    `   `,
    `CALLING SEQUENCE:`,
    `   represent(expr,known);`,
    `   `,
    `PARAMETERS:`,
    `   expr  - a list containing a homogeneous expression and its degree`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- Given a Cohen-Macaulay basis (s1,s2,... ,f1,f2,...) for a K-algebra I, each `,
    `  element g of I can be uniquely expressed as`,
    `              g = g1(s1,s2,...)*f1 + g2(s1,s2,...)*f2+ ...`,
    `  with polynomials g1,g2,...`,
    `  This is what represent does, where I is contained in a ring of polynomials.`,
    ``,
    `- The second argument known is a table with numerous entries which defines the `,
    `  Cohen-Macaulay basis. For all primary and secondary generators there are `,
    `  symbolic names s1,s2,... and t1,t2,... The first argument expr is a list `,
    `  containing the expression to be represented and its degree. The expression `,
    `  can either be a homogeneous polynomial in the indeterminates (y1,y2... or `,
    `  x1,x2,...) or a product of symbols si and ti. An expression in the symbols is `,
    `  returned as result.`,
    `  If the expression turns out not to lie in I, the string FAIL is returned. If `,
    `  the solution is not unique (so something must be wrong with the `,
    `  Cohen-Macaulay basis), the string ``MULTIPLE SOLUTIONS!`` is returned.`,
    ``,
    `- represent is at the very core of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> protocol_level:=-1:`,
    `> invring(Z3);`,
    `> protocol_level:=1:`,
    `> expr:=5*y1*y2**4+6*y1**2*y2**3-y1**5+4*y1**3*y2**2+y2**5;`,
    ``,
    `                             4       2   3     5       3   2     5`,
    `              expr := 5 y1 y2  + 6 y1  y2  - y1  + 4 y1  y2  + y2`,
    ``,
    `> represent([expr,5]);`,
    `Solving a system of 6 linear equations in 2 unknowns ...`,
    `                                 s2 t1 + s2 s3`,
    ``,
    `> represent([t1^3,9]);`,
    `Solving a system of 10 linear equations in 4 unknowns ...`,
    `                                 3       3        3`,
    `                          - 27 s3  - 3 s2  s3 - s2  t1`,
    ``,
    `SEE ALSO:  invar, known, protocol_level`
    ):
`help/invar/text/represent`:=`help/text/represent`:
`help/text/showseconds`:=`help/text/showbasis`:
`help/invar/text/showseconds`:=`help/text/showseconds`:
`help/text/secondaries`:=TEXT(
    ``,
    `FUNCTION: secondaries - calculate secondary generators`,
    `   `,
    `CALLING SEQUENCE:`,
    `   secondaries(degrees,t_name,known);`,
    `   `,
    `PARAMETERS:`,
    `   degrees - list of the (expected) degrees of basis elements`,
    `   t_name  - (optional) string (default is 't')`,
    `   known   - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- secondaries is one of the subroutines called by invring during the `,
    `  calculation of the ring of invariants. After the primary generators have been `,
    `  calculated, it is known that the ring of invariants is a free module over `,
    `  K[{primaries}] with a basis of homogeneous invariants b1,b2,... . By the `,
    `  poincare-series, the number of bi's and their degrees are also known. `,
    `  secondaries finds such bi's.`,
    ``,
    `- When searching for basis elements bi, secondaries first tries products of `,
    `  bi's that have already been found. This way the bi's are products of some `,
    `  invariants t1,t2,..., called secondary generators. secondaries will keep the `,
    `  number of these minimal.`,
    ``,
    `- The second argument gives the user the freedom to let the secondaries be `,
    `  called something different from t1,t2,... . secondaries receives information `,
    `  about the primary generators and much more by the fourth argument, the `,
    `  known-table. secondaries writes its result into that table, too. On how to `,
    `  retrieve this  result, see ?showseconds and ?showbasis`,
    ``,
    `- secondaries is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `   (See the examples for invring)`,
    ``,
    `SEE ALSO:  invar, invring, known, showseconds, showbasis`
    ):
`help/invar/text/secondaries`:=`help/text/secondaries`:
`help/text/throw`:=TEXT(
    ``,
    `FUNCTION: throw - apply a matrix to a polynomial`,
    `   `,
    `CALLING SEQUENCE:`,
    `   throw(expr,mat,o_and_n_vars,known);`,
    `   `,
    `PARAMETERS:`,
    `   expr         - a multivariate polynomial`,
    `   mat          - a matrix given by a list of lists`,
    `   o_and_n_vars - (optional) a list of two lists of names`,
    `                                 (default is [known[vars],[known[vars]])`,
    `   known        - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- If f is a polynomial in n indeterminates viewed as a function from K^n to K `,
    `  and A an (m x n)-matrix over K, throw calculates the polynomial A(f) in m `,
    `  indeterminates defined by (A(f))(x)=f(A^t*x), where A^t is the transpose of `,
    `  A. Note that in literature the application of A is often defined by taking `,
    `  A^(-1) instead of  A^t.`,
    ``,
    `- The polynomial f is passed by the first argument. It should be a polynomial `,
    `  in the variables listed in the first entry of the third argument, which is `,
    `  known[vars] by default. But f may contain any parameters, too. A is given by `,
    `  the second argument as a listlist. By the third argument the user may specify `,
    `  the variables of f and those of the result.`,
    ``,
    `- throw is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> array([[1,1],[1,-1],[0,1]]);`,
    ``,
    `                                   [ 1   1 ]`,
    `                                   [       ]`,
    `                                   [ 1  -1 ]`,
    `                                   [       ]`,
    `                                   [ 0   1 ]`,
    ``,
    `> throw(x**2+y,convert(",listlist),[[x,y],[u,v,w]]);`,
    ``,
    `                           2            2`,
    `                          u  + 2 u v + v  + u - v + w`,
    ``,
    `SEE ALSO:  invar, known`
    ):
`help/invar/text/throw`:=`help/text/throw`:
`help/text/valueof`:=TEXT(
    ``,
    `FUNCTION: valueof - the actual value of an expression of symbols s_i and t_i`,
    `   `,
    `CALLING SEQUENCE:`,
    `   valueof(expr,known);`,
    `   `,
    `PARAMETERS:`,
    `   expr  - an expression`,
    `   known - (optional) table with information (default is  _known)`,
    `   `,
    `SYNOPSIS:   `,
    `- When calculating the ring of invariants, invring introduces symbolic `,
    `  expressions s1,s2,... and t1,t2,... for the primary and secondary generators. `,
    `  Now valueof returns the actual value of some expression in these symbols.`,
    ``,
    `- The second argument is a table that must have (at least) the entry `,
    `  known[values] which relates the symbols to their actual values.`,
    ``,
    `- valueof is part of the invar package.`,
    ``,
    `EXAMPLES:`,
    `> protocol_level:=-1:`,
    `> invring(Z3);`,
    `> valueof(s2*t1);`,
    ``,
    `                              2     2         2        3     3`,
    `                 (- y1 y2 - y1  - y2 ) (- 3 y1  y2 - y1  + y2 )`,
    ``,
    `SEE ALSO:  invar, invring, known, protocol_level`
    ):
`help/invar/text/valueof`:=`help/text/valueof`:





# ******************************************************************* #
#                                                                     #
#                               SAVE IT                               #
#                                                                     #
# ******************************************************************* #




#    save invar,
#         `help/text/invar`,
#         `help/text/grouptable`, `help/invar/text/grouptable`,
#         `help/text/known`, `help/invar/text/known`,
#         `help/text/protocol_level`, `help/invar/text/protocol_level`,
#         `help/text/alglinsolve`, `help/invar/text/alglinsolve`,
#         `help/text/aver_orb`, `help/invar/text/aver_orb`,
#         `help/text/showbasis`, `help/invar/text/showbasis`,
#         `help/text/check_nullspace`, `help/invar/text/check_nullspace`,
#         `help/text/classes`, `help/invar/text/classes`,
#         `help/text/derive`, `help/invar/text/derive`,
#         `help/text/elements`, `help/invar/text/elements`,
#         `help/text/elesym`, `help/invar/text/elesym`,
#         `help/text/generators`, `help/invar/text/generators`,
#         `help/text/inv`, `help/invar/text/inv`,
#         `help/text/invarspace`, `help/invar/text/invarspace`,
#         `help/text/invring`, `help/invar/text/invring`,
#         `help/text/lingroup`, `help/invar/text/lingroup`,
#         `help/text/mklingroup`, `help/invar/text/mklingroup`,
#         `help/text/monoms`, `help/invar/text/monoms`,
#         `help/text/monoms_base`, `help/invar/text/monoms_base`,
#         `help/text/mul`, `help/invar/text/mul`,
#         `help/text/onesoff`, `help/invar/text/onesoff`,
#         `help/text/out`, `help/invar/text/out`,
#         `help/text/poincare`, `help/invar/text/poincare`,
#         `help/text/poinrep`, `help/invar/text/poinrep`,
#         `help/text/showprims`, `help/invar/text/showprims`,
#         `help/text/primaries`, `help/invar/text/primaries`,
#         `help/text/proj`, `help/invar/text/proj`,
#         `help/text/reci`, `help/invar/text/reci`,
#         `help/text/reduce`, `help/invar/text/reduce`,
#         `help/text/showrels`, `help/invar/text/showrels`,
#         `help/text/relations`, `help/invar/text/relations`,
#         `help/text/renormal`, `help/invar/text/renormal`,
#         `help/text/repfrac`, `help/invar/text/repfrac`,
#         `help/text/represent`, `help/invar/text/represent`,
#         `help/text/showseconds`, `help/invar/text/showseconds`,
#         `help/text/secondaries`, `help/invar/text/secondaries`,
#         `help/text/throw`, `help/invar/text/throw`,
#         `help/text/valueof`, `help/invar/text/valueof`,
#         `primaries/nextmat`,`primaries/nextvec`,`represent/evalprod`,
#         Z2,Z3,S3,V4,Z4,D4,A4,S4,Z5,D5,F20,A5,S5,
#         `invar.m`:
#
#
#
#
#
# ******************************************************************* #
#                                                                     #
#                               THE END                               #
#                                                                     #
# ******************************************************************* #
#quit
