#############################################################################
##
#A  genVarith            CHEVIE library          Meinolf Geck & Frank L"ubeck 
##
#Y  Copyright 1992--1993,  Lehrstuhl D f"ur Mathematik,    RWTH Aachen,   and
#Y                         IWR   der   Universit"at    Heidelberg,   Germany.
##
####################################################################
##
## Darstellung der Ausdruecke mit generischen Einheitswurzeln:
## a) `cew`-Ausdruecke: Listen der Form
##            [c,z,n].
##    Dabei ist  c: Vorfaktor (etwa Polynom in q, sqrt(q) oder
##                             Hilfsvariable qQ)
##               z: multivariates ganzzahliges Polynom (in q oder
##                  qQ und Parametern fuer Klassen und Charaktere)
##               n: ganzzahliges Polynom in q oder Hilfsvariable (qQ)                  
##    Ein solcher `cew`-Ausdruck steht fuer den Term:
##             c*e^(2*Pi*I*z/n)
##
## b) `scew`-Ausdruecke: Listen von `cew`-Ausdruecken.
##    Ein `scew`-Ausdruck steht fuer die Summe der zu den
##    `cew`-Ausdruecken gehoerenden Terme.
##
## c) `qscew`-Ausdruecke: Liste zweier `scew`-Ausdruecke,
##             [scew1,scew2],
##    die fuer den Quotienten der zugehoerigen Terme steht.
##
## In den Prozeduren, die solche Ausdruecke manipulieren,
## wird im Namen angedeutet, auf welchen
## Typ von Ausdruecken sie anzuwenden sind.


####################################################################
##
## Hilfsfunktion:
## ggT zweier Polynome a,b, so dass 
## auch die Inhalte von a/ggT und b/ggT
## teilerfremd sind. Wird nur mit univariaten 
## Polynomen in nfcew aufgerufen.
## Diese sind haeufig gleich, daher ist das `remember` sinnvoll.
##
a_ggtp:=proc(a,b)
  local ggt;
  options remember;
  ggt:=gcd(a,b);
  igcd(icontent(a),icontent(b))/icontent(ggt)*ggt;
end:

####################################################################
##
## Hilfsfunktion:
## liefert fuer zwei (auch konstante) ganzzahlige Polynome 
## in einer Variablen (die in der Menge nv uebergeben wird) 
## eine Normalform fuer `a mod b`.
## Auch hier lohnt sich `remember`.
##
a_rest:=proc(a,b,nv)
  local r,n,q;
  options remember;
  if nv={} then
    RETURN(a mod b);
  else
    r:=rem(a,b,op(nv),q);
    n:=denom(q);
    if n=1 then
      RETURN(r);
    else
      RETURN(r+(numer(q) mod n)/n*b);
    fi;
  fi;
end:

####################################################################
##
## Normalform fuer Quotient eines ganzzahligen 
## multivariaten Polynoms zz und eines ganzzahligen
## univariaten Polynoms nn.
##
nfew:=proc(zz,nn)
  local nv, zv, c, z, n, zl, cl, cv, ggt, i, m, zi;
  options remember;
  z:=zz;
  n:=nn;
  nv:=indets(n);
  zv:=indets(z);
  zv:=zv minus nv;
# `collect` nach allen Variablen in zz ohne die Variable von nn:  
  z:=collect(z,zv minus nv,'distributed');
# Umwandlung in Liste, dann wird zuerst alles durch den ggT
# dieser Terme mit n geteilt und danach jeder Summand
# auf eine Normalform modulo n gebracht.
  if type(z,`+`) then
    zl:=convert(z,list);
  else
    zl:=[z];
  fi;
  cl:=[];
  cv:=[];
  for zi in zl do
    c:=coeffs(zi,zv);
    cl:=[op(cl),c];
    cv:=[op(cv),zi/c];
  od;
  cv:=subs(nfcewhelpvar=1,cv);
  i:=1;
  ggt:=n;
  while ggt<>1 and i<=nops(zl) do
    ggt:=a_ggtp(ggt,cl[i]);
    i:=i+1;
  od;
  z:=0;
  if ggt<>1 then
    n:=normal(n/ggt,expanded);
    if n=1 then 
      RETURN([0,1]); 
    fi;
    m:=sign(n);
    n:=m*n;
    nv:=indets(n);
    for i to nops(zl) do
      z:=z+m*a_rest(normal(cl[i]/ggt,expanded),n,nv)*cv[i];
    od;
  else
    for i to nops(zl) do
      m:=sign(n);
      n:=m*n;
      z:=z+m*a_rest(cl[i],n,nv)*cv[i];
    od;
  fi;
  [z,n];
end:
 
####################################################################
##
## Normalform fuer Ausdruecke der Form `cew`.
## Ist nach der Anwendung von nfew auf z und n der Nenner
## n eine zweite oder dritte Einheitswurzel, so werden
## die Ausdruecke noch weiter umgeformt. Damit kann in einem
## `scew`-Ausdruck erkannt werden, ob sich die Terme mit
## zweiten bzw. dritten Einheitswurzeln wegheben.
## 
nfcew:=proc(cew)
  local z, n, zn, c;
# Die Hilfsvariable `nfcewhelpvar` wird hier benutzt, damit
# in `nfew` das `collect` richtig funktioniert.
  z:=expand(cew[2]*nfcewhelpvar);
  n:=expand(cew[3]);
  if z=0 or n=1 then 
    RETURN([expand(cew[1]),0,1]); 
  fi;
  zn:=nfew(z,n);
  z:=zn[1];
  n:=zn[2];
# Spezielle Behandlung der 2-ten Einheitswurzeln:
  if n=2 then
    z:=z mod 2;
    c:=subs(map(x->x=0,indets(z)),z);
    if c=1 then
      if z=1 then  
        RETURN([expand(-cew[1]),0,1]);
      else
        RETURN([expand(-cew[1]),z-c,2]);
      fi;
    fi;
  fi;
# Spezielle Behandlung der 3-ten Einheitswurzeln:
  if n=3 then
    z:=z mod 3;
    c:=subs(map(x->x=0,indets(z)),z);
    if c=2 then
      RETURN([expand(-cew[1]),z-1,3], [expand(-cew[1]),z-2,3]);
    fi;
  fi;
  if z=0 then
    n:=1;
  fi;
  [expand(cew[1]),expand(z),n];
end:
#Initialisieren der remember-Tafel:
nfcew([1,0,1]):

####################################################################
##
## Zusammenfassen von Liste der Form `scew`:
## Terme mit gleichem Einheitswurzelausdruck werden zusammengefasst,
## also etwa [[c1,z,n],[c2,z,n],...] zu [[c1+c2,z,n],...].
## Hier wird zum Vergleich der Terme die remember-Tafel der
## Hilfsfunktion a_lstf verwendet. Dies geht schneller als
## mit einer lokalen maple-Tafel.
##

# Hilfsfunktion zum Zusammenfassen:
a_lstf:=proc()
  options remember;
  false;
end:

simpscew:=proc(scew)
  local lst, erg, cew, pr, i;
  if scew=[] then
    RETURN([]);
  fi;
  for cew in scew  do
    pr:=a_lstf(cew[2],cew[3]);
    if pr=false then
      a_lstf(cew[2],cew[3]):=cew[1];
    else
      a_lstf(cew[2],cew[3]):=pr+cew[1];
    fi;
  od;
  erg:=[];
  lst:=op(4,op(a_lstf));
  for cew in {indices(lst)} do
    i:=lst[op(cew)];
    if i<>0 then
      erg:=[op(erg),[i,op(cew)]];
    fi;
    lst[op(cew)]:=evaln(lst[op(cew)]);
  od;
  erg;
end:

####################################################################
##
## Kuerzen von Quotienten qscew.
## Diese Version von `kuerzqscew` kuerzt nur, wenn z=n ist.
## Kompliziertere Versionen haben sich nicht als sinnvoll
## erwiesen.
##
kuerzqscew:=proc(qscew)
  local z, n;
  z:=qscew[1];
  if z=[] then
    RETURN([[],[[1,0,1]]]);
  else
    n:=qscew[2];
  fi;
  if n=[[1,0,1]] then
    RETURN(qscew);
  fi;
  if z=n then 
    RETURN([[[1,0,1]],[[1,0,1]]]);
  fi;
  [z,n];
end:

####################################################################
##
## Rechenoperationen mit Termen der Form cew, scew, qscew:
## Hier ist wichtig, dass in jedem Rechenschritt sofort die
## Vereinfachungsroutinen `nfcew`, `simpscew` und `kuerzqscew`
## angewendet werden.
##
malcew:=proc(cew1,cew2)
  local cew;
  if cew1=[1,0,1] then
    RETURN(cew2);
  elif cew2=[1,0,1] then
    RETURN(cew1);
  fi;  
  cew:=[expand(cew1[1]*cew2[1]),
        expand(cew1[2]*cew2[3]+cew2[2]*cew1[3]),
        expand(cew1[3]*cew2[3])];
  nfcew(cew);
end:

malscew:=proc(scew1,scew2)
  local cew1, cew2, scew;
  if scew1=[[1,0,1]] then
    RETURN(scew2);
  fi;
  if scew2=[[1,0,1]] then
    RETURN(scew1);
  fi;
  scew:=[];
  for cew1 in scew1 do
    for cew2 in scew2 do
      scew:=[op(scew),malcew(cew1,cew2)];
    od;
  od;
  simpscew(scew);
end:

malqscew:=proc(qscew1,qscew2)
  local z, n;
  z:=malscew(qscew1[1],qscew2[1]);
  n:=malscew(qscew1[2],qscew2[2]);
  kuerzqscew([z,n]);
end:

addqscew:=proc(qscew1,qscew2)
  local z1, z2, z, n, h;
  if qscew1[2]=qscew2[2] then
    if qscew1[2]=[[1,0,1]] then
      RETURN([simpscew([op(qscew1[1]),op(qscew2[1])]),[[1,0,1]]]);
    else
      RETURN(kuerzqscew([simpscew([op(qscew1[1]),op(qscew2[1])]),
                                                     qscew1[2]]));
    fi;
  fi;
  h:=kuerzqscew([qscew1[2],qscew2[2]]);
  if h[2]=[[1,0,1]] then
    z1:=qscew1[1];
    z2:=malscew(qscew2[1],h[1]);
    z:=simpscew([op(z1),op(z2)]);
    n:=qscew1[2];
  else  
    z1:=malscew(qscew1[1],qscew2[2]);
    z2:=malscew(qscew2[1],qscew1[2]);
    z:=simpscew([op(z1),op(z2)]);
    n:=malscew(qscew1[2],qscew2[2]);
  fi;
  kuerzqscew([z,n]);
end:


# Multiplikation mit Skalar:
smalqscew:=proc(s,qscew)
  local z, cew, erg;
  if s=1 then
    RETURN(qscew);
  fi;
  z:=qscew[1];
  erg:=[];
  for cew in z do
    erg:=[op(erg),[expand(s*cew[1]),cew[2],cew[3]]];
  od;
  [erg,qscew[2]];
end:

####################################################################
##
## komplexe Konjugation:
##
conjcew:=proc(cew)
  nfcew([cew[1],-cew[2],cew[3]]);
end:

conjqscew:=proc(qscew)
  local z, n;
  z:=map(conjcew,qscew[1]);
  n:=map(conjcew,qscew[2]);
  [z,n];
end:


