# Copyright (c) 2018-2021, Carnegie Mellon University
# See LICENSE for details

TempArrayType := (child, y, x, index) ->
    When( IsBound(child.a.t_in), child.a.t_in[index], let(
          X := Flat([x])[1], # without type unification everything has same type
          When(X.t.t=TComplex or Flat([y])[1].t.t=TComplex, TComplex, X.t.t)
));

TempArray := (y, x, child) -> let(
    cols := Flat([Cols(child)]),
    StripList( List( [1..Length(cols)], i ->
        TempVec( TArray( TempArrayType(child, y, x, i), cols[i]))
)));

# HACKed in. This should just be rolled into TempArray or otherwise be handled cleanly.
TempArraySeq := function(y, x, child)
    local dims, P;
    if ObjId(child)=BB then
      dims := child.child(1).dims();
      P    := child.child(1).P;
    else
      dims := child.dims();
      P    := child.P;
    fi;

    return(
        StripList(List(
          Flat([ dims[2]/P ]), z ->
            TempVec(
                #TArray(TempArrayType(child, Flat([y])[1], Flat([x])[1]), z))
                TArray(TempArrayType(child, y, x, 1), z))
            )));
end;

#F _CodeSums(<sums-spl>, <y-output-var>, <x-input-var>)
#F
#F Generates unoptimize loop code for <sums-spl>
#F
_CodeSums := function(sums, y, x)
    local code;
    code := sums.code(y,x);
    code.dimensions := sums.dimensions;
    code.root := sums;
    return code;
end;

#CodeSums := _CodeSums;

_CodeSumsAcc := function(sums, y, x)
    local code;
    code := _CodeSums(sums, y, x);
    code := SubstTopDownNR(code, [assign, [@(0,nth), @(1, var, e->Same(e,y)), ...], @(3)],
                     e -> assign_acc(@(0).val, @(3).val));
    return code;
end;

# =======================================================================================
# Code generation templates
# [ Data, Blk, Blk1, Prm, R, W, SUM, ISum, Compose ]
# =======================================================================================
Formula.code := (self,y,x) >> self.child(1).code(y, x);

BB.code := (self,y,x) >> MarkForUnrolling(_CodeSums(self.child(1), y, x));

Buf.code := (self,y,x) >> _CodeSums(self.child(1), y, x);

COND.code := (self,y,x) >>
    IF(self.cond,
    _CodeSums(self.child(1), y, x),
    _CodeSums(self.child(2), y, x));

Data.code := meth(self, y, x)
    local val, t;
    if self.inline then
    # we set live_out to prevent copy propagation
    # self.var.setAttr("live_out")
    return decl(self.var, chain(
        assign(self.var, self.value.tolist()),
        _CodeSums(self.child(1), y, x)));
    else
    val := self.value;
    if IsBound(val.tolist) then
        if IsBound(val.range) and val.range() <> false then
        t := TArray(val.range(), val.domain());
        val := t.value(val.tolist());
        else
        val := V(val.tolist());
        fi;
    else val := Cond(IsSPL(val), V(MatSPL(val)), val);
    fi;
    return data(self.var, val, _CodeSums(self.child(1), y, x));
    fi;
end;

Diag.code := meth(self, y, x)
    local i, elt;
    i := Ind();
    elt := self.element.lambda().at(i);
    return loop(i, self.element.domain(), assign(nth(y,i), elt * nth(x,i)));
end;

RCDiag.code := meth(self, y, x)
    local i, elt, re, im;
    i := Ind();
    elt := self.element.lambda();
    re := elt.at(2*i);
    im := elt.at(2*i+1);
    return loop(i, self.element.domain()/2, chain(
	    assign(nth(y,2*i),   re * nth(x,2*i) - im * nth(x,2*i+1)),
	    assign(nth(y,2*i+1), im * nth(x,2*i) + re * nth(x,2*i+1))));
end;

ColVec.code := meth(self, y, x)
    local i, datavar, diag, func;
    i := Ind();
    func := self.element;
    if IsBound(self.inline) and self.inline then
    return loop(i, Rows(self), assign(nth(y,i), mul(func.lambda().at(i), nth(x,0))));
    else
    diag := V(func.lambda().tolist());
    datavar := Dat(diag.t);
    return
    data(datavar, diag,
        loop(i, Rows(self), assign(nth(y,i), mul(nth(datavar,i), nth(x,0)))));
    fi;
end;

RowVec.code := meth(self, y, x)
    local i, datavar, diag, func, t;
    i := Ind();
    t := TempVar(x.t.t);
    func := self.element;
    if IsBound(self.inline) and self.inline then
    return chain(
        assign(t,0),
        loop(i, Cols(self), assign(t, add(t, mul(func.lambda().at(i), nth(x,i))))),
        assign(nth(y,0), t));
    else
    diag := V(func.lambda().tolist());
    datavar := Dat(diag.t);
    return
    data(datavar, diag,
        chain(
        assign(t,0),
        loop(i, Cols(self), assign(t, add(t, mul(nth(datavar,i), nth(x,i))))),
        assign(nth(y,0), t)));
    fi;
end;

Scale.code := (self, y, x) >> let(i := Ind(),
    chain(
	_CodeSums(self.child(1), y, x),
	loop(i, Rows(self),
	    assign(nth(y,i), mul(self.scalar, nth(y,i))))));

I.code := (self, y, x) >> let(i := Ind(),
    loop(i, Rows(self), assign(nth(y,i), nth(x,i))));

Blk2code := (blk, y, x) -> let(b := V(blk.element).v,
    t0 := TempVar(x.t.t), t1 := TempVar(x.t.t),
    decl([t0, t1], chain(
	    assign(t0, mul(b[1].v[1], nth(x,0)) + mul(b[1].v[2], nth(x,1))),
	    assign(t1, mul(b[2].v[1], nth(x,0)) + mul(b[2].v[2], nth(x,1))),
	    assign(nth(y,0), t0),
	    assign(nth(y,1), t1)
	)));

Blk4code := (blk, y, x) -> let(b := V(blk.element).v,
    t0 := TempVar(x.t.t), t1 := TempVar(x.t.t),
    t2 := TempVar(x.t.t), t3 := TempVar(x.t.t),

    x0 := TempVar(x.t.t), x1 := TempVar(x.t.t),
    x2 := TempVar(x.t.t), x3 := TempVar(x.t.t),
    decl([x0,x1,x2,x3, t0, t1, t2, t3], chain(
	    assign(x0, nth(x,0)),
	    assign(x1, nth(x,1)),
	    assign(x2, nth(x,2)),
	    assign(x3, nth(x,3)),

	    assign(t0, mul(b[1].v[1], x0) + mul(b[1].v[2], x1) +
		       mul(b[1].v[3], x2) + mul(b[1].v[4], x3)),
	    assign(t1, mul(b[2].v[1], x0) + mul(b[2].v[2], x1) +
		       mul(b[2].v[3], x2) + mul(b[2].v[4], x3)),
	    assign(t2, mul(b[3].v[1], x0) + mul(b[3].v[2], x1) +
		       mul(b[3].v[3], x2) + mul(b[3].v[4], x3)),
	    assign(t3, mul(b[4].v[1], x0) + mul(b[4].v[2], x1) +
		       mul(b[4].v[3], x2) + mul(b[4].v[4], x3)),
	    assign(nth(y,0), t0),
	    assign(nth(y,1), t1),
	    assign(nth(y,2), t2),
	    assign(nth(y,3), t3)
    )));

Blk.code := meth(self, y, x)
    local i, j, t, datavar, matval;
    if Rows(self)=2 and Cols(self)=2 then return Blk2code(self, y, x); fi;
    if Rows(self)=4 and Cols(self)=4 then return Blk4code(self, y, x); fi;
    j := Ind();
    i := Ind();
    t := TempVar(x.t.t);
    matval := V(self.element);
    datavar := Dat(matval.t);
    return
    data(datavar, matval,
    loop(j, Rows(self),
        decl(t,
        chain(
            assign(t, 0),
            loop(i, Cols(self),
            assign(t, add(t, mul(nth(nth(datavar, j), i), nth(x, i))))),
            assign(nth(y,j), t)))));
end;

toeplitz.code := meth(self, y, x)
    local i, j, t, datavar, matval, dataload;
    j := Ind();
    i := Ind();
    t := TempVar(x.t.t);
    matval := V(self.obj.element);
    datavar := Dat(matval.t);
    dataload := TempVec(TArray(TDouble, Rows(self)*Cols(self)));
    return
    data(datavar, matval, decl(dataload, chain(
    loop(j, Rows(self),
        loop(i, Cols(self),
        assign(nth(dataload, j*Cols(self)+i), nth(nth(datavar,j),i)))),

    loop(j, Rows(self),
        decl(t,
        chain(
            assign(t, 0),
            loop(i, Cols(self),
            assign(t, add(t, mul(nth(dataload, j*Cols(self)+i), nth(x, i))))),
            assign(nth(y,j), t)))))));
end;

Blk1.code := (self, y, x) >>
   assign(nth(y,0), mul(toExpArg(self.element), nth(x,0)));

BlkConj.code := (self, y, x) >>
   assign(nth(y,0), conj(nth(x,0)));

Prm.code := (self, y, x) >> let(i := Ind(), func := self.func.lambda(),
    loop(i, Rows(self),
    assign(nth(y, i),
           nth(x, func.at(i)))));

Z.code := (self, y, x) >> let(i := Ind(), ii := Ind(), z := self.params[2],
    chain(
    loop(i, Rows(self)-z,
        assign(nth(y, i), nth(x, i+z))),
    loop(i, z,
        assign(nth(y, i+(Rows(self)-z)), nth(x, i)))));

O.code := (self, y, x) >> let(i := Ind(),
    loop(i, self.params[1],
        assign(nth(y, i), V(0))));

Gath.code := meth(self, y, x)
    local i, func, rfunc, ix;
    i := Ind(); func := self.func.lambda();

    if IsBound(self.func.rlambda) then
	rfunc := self.func.rlambda();
	ix := var.fresh_t("ix", TInt);
	return chain(
	    assign(ix, func.at(0)),
	    assign(nth(y, 0), nth(x, ix)),
	    loop(i, self.func.domain()-1,
		chain(assign(ix, rfunc.at(ix)),
		      assign(nth(y, i+1), nth(x, ix)))));
    else
	return loop(i, self.func.domain(), assign(nth(y,i), nth(x, func.at(i))));
    fi;
end;

Scat.code := meth(self, y, x)
    local i, func, rfunc, ix;
    i := Ind(); func := self.func.lambda();
    if IsBound(self.func.rlambda) then
	rfunc := self.func.rlambda();
	ix := var.fresh_t("ix", TInt);
	return chain(
	    assign(ix, func.at(0)),
	    assign(nth(y, ix), nth(x, 0)),
	    loop(i, self.func.domain()-1,
		chain(assign(ix, rfunc.at(ix)),
		      assign(nth(y, ix), nth(x, i+1)))));
    else
	return loop(i, self.func.domain(), assign(nth(y,func.at(i)), nth(x, i)));
    fi;
end;

SUM.code := (self, y, x) >>
       chain(List(self.children(), c -> _CodeSums(c, y, x)));

SUMAcc.code := (self, y, x) >>
   let(ii := Ind(),
       When(not Same(ObjId(self.child(1)), Gath),  # NOTE: come up with a general condition
       chain(
           loop(ii, Rows(self), assign(nth(y, ii), V(0))),
           List(self.children(), c -> _CodeSumsAcc(c, y, x))),
       chain(
           _CodeSums(self.child(1), y, x),
           List(Drop(self.children(), 1), c -> _CodeSumsAcc(c, y, x)))));

ISum.code := (self, y, x) >> let(myloop := When(IsSymbolic(self.domain), loopn, loop),
   myloop(self.var, self.domain,
        _CodeSums(self.child(1), y, x)));

ISumAcc.code := (self, y, x) >>
   let(ii := Ind(),
       chain(
       loop(ii, Rows(self),
           assign(nth(y, ii), V(0))),
       loop(self.var,
            self.domain,
        _CodeSumsAcc(self.child(1), y, x))));

Compose.code := meth(self,y,x)
    local ch, numch, vecs, allow, i;
#VIENNA filtering DMPGath, DMPScat out here because they do not generate code
#    ch := self.children();
    ch := Filtered(self.children(), i-> not i.name in ["DMPGath","DMPScat"]);
    numch := Length(ch);
    vecs := [y];
    allow := (x<>y);
    for i in [1..numch-1] do
        if allow and ObjId(ch[i])=Inplace then vecs[i+1] := vecs[i];
	else vecs[i+1] := TempVec(TArray(TempArrayType(y, x), Cols(ch[i])));
	fi;
    od;
    vecs[numch+1] := x;
    for i in Reversed([1..numch]) do
        if allow and ObjId(ch[i])=Inplace then vecs[i] := vecs[i+1];
	fi;
    od;

#Print(vecs, "\n");
    if vecs[1] = vecs[numch+1] then # everything was inplace
	vecs[1] := y;
    fi;
    if vecs[1] = vecs[numch+1] then # everything was inplace
	vecs[numch+1] := x;
    fi;
#Print(vecs, "\n");
    vecs := Reversed(vecs);
    ch := Reversed(ch);
    return decl( Difference(vecs{[2..Length(vecs)-1]}, [x,y]),
	chain( List([1..numch], i -> When(vecs[i+1]=vecs[i],
		    compiler._IPCodeSums(ch[i], vecs[i]),
		    _CodeSums(ch[i], vecs[i+1], vecs[i])))));
end;

ICompose.code := (self, y, x) >>

#DoneSpiralHelp - How to make this work for any datatype (var t)
#Look into Compose.code for tempvec

  chain([let(
    t :=  Dat1d(x.t.t, Rows(self)),
    newind := Ind(self.domain),
    decl([t], chain(
       When(IsOddInt(self.domain),
       SubstVars(Copy(_CodeSums(self.child(1), y, x)), tab((self.var.id) := (V(0)))),
          skip()
       ),
       When(IsEvenInt(self.domain),
          chain(
          SubstVars(Copy(_CodeSums(self.child(1), t, x)), tab((self.var.id) := (V(0)))),
          SubstVars(Copy(_CodeSums(self.child(1), y, t)), tab((self.var.id) := (V(1))))
          ),
          skip()
       ),
      loop(newind, (self.domain/2),
        chain([
        SubstVars(Copy(_CodeSums(self.child(1), x, y)), tab((self.var.id) := (2*newind)+2)),
        SubstVars(Copy(_CodeSums(self.child(1), y, x)), tab((self.var.id) := (2*newind)+3))
        ])
      )
    ))
  )]);


# Copyright (c) 2018-2021, Carnegie Mellon University
# See LICENSE for details

VarFrontier := (e, depth) -> Cond(
    IsBound(e.isExpComposite) and e.isExpComposite,
        Union(List(VarArgsExp(e), x->VarFrontier(x, depth))),
    IsVar(e),
        When(depth=0,
            Set([e]),
            Union([e], Union(List(PredLoc(e), x->VarFrontier(x, depth-1))))),
    Set([])
);

IsXRef := exp -> Intersection([var.table.X,var.table.X1,var.table.XY1], VarFrontier(exp, -1)) <> [];

IsYRef := exp -> Intersection([var.table.Y,var.table.Y1,var.table.XY1], VarFrontier(exp, -1)) <> [];

CollectLoads := cmd -> Collect(cmd, [assign, @, @(1, [deref, nth], IsXRef)]);

CollectStores := cmd -> Collect(cmd, [assign, @(1, [deref, nth], IsYRef), @]);


local reps,tori,labels,I,II,ords,blocks;

# Permutation from standard to gap
# / 1 2 3 4 \
# \ 2 4 3 1 /
# Position(pr,Permuted([2,3,4,2],(2,4,1)))=24;

#......Ross Jordan Block Sizes........Donna & Ross nilpotents........(in GAP notation)
reps:=[[[2,1],[4,1],[3,1],[1,1]],    # F_4
       [[2,1],[4,1],[7,1],[5,1]],    # F_4(a_1) [[2,1],[4,1],[10,1],[1,1]],
       #[[6,1],[10,1],[1,1],[5,1]],   # F_4(a_2) [[9,1],[1,1],[10,1],[4,1]],
       # F_4(a_2): s_11s_9*[4,3,1,24]=[4,5,9,10] also good .. has 2 semisimple elements?
       [[1,1],[4,1],[9,1],[10,1]],   # F_4(a_2) good for all p\neq 2 <C_3A_1
       [[4,1],[3,1],[1,1]],          # C_3      [[1,1],[3,1],[4,1]],
       [[2,1],[4,1],[3,1]],          # B_3      the same
       [[4,1],[6,1],[10,1],[18,1]],  # F_4(a_3) [[4,1],[13,1],[11,1],[12,1]],
       [[4,1],[1,1],[10,1]],         # C_3(a_1) [[1,1],[10,1],[4,1]],
       [[2,1],[3,1],[1,1]],          # sA_2A_1  [[3,1],[1,1],[2,1]],
       [[4,1],[3,1]],                # B_2      the same
       [[2,1],[4,1],[1,1]],          # A_2sA_1  the same
       [[3,1],[1,1]],                # sA_2     the same
       [[2,1],[4,1]],                # A_2      the same
#       [[2,1],[3,1]],                # A_1sA_1  [[2,1],[1,1]],
#       [[3,1]],                      # sA_1     [[1,1]],
#       [[2,1]]                       # A_1      [[2,1]]
       [[2,1],[1,1]],                # A_1sA_1  [[2,1],[1,1]],
       [[1,1]],                      # sA_1     [[1,1]],
       [[2,1]]                       # A_1      [[2,1]]
       ];

I:=[[],     # F_4
    [3],    # F_4(a_1)
    [3,4],  # F_4(a_2)
    [],     # C_3
    [],     # B_3
    [2,3,1],# F_4(a_3)
    [3],    # C_3(a_1)
    [],     # sA_2A_1
    [],     # B_2
    [],     # A_2sA_1
    [],     # sA_2
    [],     # A_2
    [],     # A_1sA_1
    [],     # sA_1
    []      # A_1
    ];

#
# The parabolic where the representative is distinguished
#
II:=[[1,2,3,4],   # F_4
     [1,2,3,4],   # F_4(a_1)
     [1,2,3,4],   # F_4(a_2)
     [4,3,1],     # C_3
     [2,4,3],     # B_3
     [1,2,3,4],   # F_4(a_3)
     [4,3,1],     # C_3(a_1)
     [3,1,2],     # sA_2A_1
     [4,3],       # B_2
     [2,4,1],     # A_2sA_1
     [3,1],       # sA_2
     [2,4],       # A_2
     [2,1],       # A_1sA_1
     [1],         # sA_1
     [2]          # A_1
     ];

labels:=["F_4",
         "F_4(a_1)",
         "F_4(a_2)",
         "C_3",
         "B_3",
         "F_4(a_3)",
         "C_3(a_1)",
         "\\tilde A_2A_1",
         "B_2",
         "A_2\\tilde A_1",
         "\\tilde A_2",
         "A_2",
         "A_1\\tilde A_1",
         "\\tilde A_1",
         "A_1"
         ];

tori:=[
       [2,2,2,2],  # F_4
       [2,2,0,2],  # F_4(a_1)
       [2,0,0,2],  # F_4(a_2)
       [2,-9,2,2], # C_3
       [-6,2,2,2], # B_3
       [0,0,0,2],  # F_4(a_3)
       [2,-5,0,2], # C_3(a_1)
       [2,2,2,-5], # sA_2A_1
       [-3,-4,2,2],# B_2
       [2,2,-3,2], # A_2sA_1
       [2,0,2,-4], # sA_2
       [0,2,-2,2], # A_2
       [2,2,-1,-1],# A_1sA_1
       [2,0,-1,0], # sA_1
       [0,2,0,-1]  # A_1
    ];

#blocks:=[];
blocks:=[[[19, 1], [15, 1], [9, 2]],                      # F_4
         [[9, 5], [7, 1]],                                # F_4(a_1)
         [[9, 3], [7, 1],[6, 2], [3, 2]],                 # F_4(a_2)
         [[9, 2], [8, 2], [6, 2], [3, 1], [1, 3]],        # C_3
         [[9, 2], [7, 4], [3, 1], [1, 3]],                # B_3
         [[7, 1], [6, 2], [5, 3], [3, 6]],                # F_4(a_3)
         [[7, 1], [6, 2], [5, 1], [4, 4], [3, 3], [1, 3]],# C_3(a_1)
         [[3, 16], [2, 2]],                               # sA_2A_1
         [[7, 1], [5, 4], [4, 4], [3, 1], [1, 6]],        # B_2
         [[3, 16], [2, 2]],                               # A_2sA_1
         [[3, 15], [1, 7]],                               # sA_2
         [[3, 15], [1, 7]],                               # A_2
         [[3, 10], [2, 8], [1, 6]],                       # A_1sA_1
         [[3, 7], [2, 8], [1, 15]],                       # sA_1
         [[3, 1], [2, 14], [1, 21]]                       # A_1
         ];

#ords:=[];
ords:=[ 27, 9, 9, 9, 9, 9, 9, 3, 9, 3, 3, 3, 3, 3, 3 ];

return [reps,tori,labels,I,II,ords,blocks];


# Copyright (c) 2018-2021, Carnegie Mellon University
# See LICENSE for details

domain := f -> f.domain(); pdomain := p -> p.val.domain();
range  := f -> f.range();  prange := p -> p.val.range();
is_perm := f -> f.isPermutation();

Class(fId, PermClass, rec(
    domain := self >> self.params[1],
    range  := self >> self.params[1],
    def    := size -> Checked(IsPosInt0Sym(size), rec(size := size)),
    lambda := self >> let(i := Ind(self.params[1]), Lambda(i,i)),
    transpose := self >> self,
    isIdentity := True
));

Class(fBase, FuncClass, rec(
    abbrevs := [ var -> Checked(IsVar(var) or ObjId(var)=ind, [var.range, var]) ],
    def    := (N, pos) -> rec(),
    domain := self >> 1,
    range  := self >> self.params[1],
    print  := (self, i, is) >> Print(self.name, "(",
        When(ObjId(self.params[2]) in [var, ind], Print(self.params[2]), PrintCS(self.params)), ")"),
    lambda := self >> let(i := Ind(1), Lambda(i, self.params[2]))
));

# used for I_n dirsum J_n
# below represent an alternator <I_n | J_n>_x o fBase(j)
# x = (pos+par) mod 2
Class(fXbase, FuncClass, rec(
    def := (n,j,par,pos) -> rec(),
    domain := self >> 1,
    range := self >> self.params[1],
    lambda := self >> let(
	n:=self.params[1], j:=self.params[2], par:=self.params[3],
	pos:=self.params[4], i := Ind(1),
	Lambda(i, j + imod(pos+par, 2)*(n - 1 - 2*j))
    )
));

# used for I_n dirsum (I_1 dirsum J_n-1)
# below represent an alternator <I_n | I_1 dirsum J_n_1>_x o fBase(j)
# x = (pos+par) mod 2
Class(fYbase, FuncClass, rec(
    def := (n,j,par,pos) -> rec(),
    lambda := self >> let(
	n := self.params[1], j := self.params[2], par := self.params[3],
	pos := self.params[4], i := Ind(1),
	Lambda(i, cond(eq(j, 0), 0, j + imod(pos+par, 2)*(n - 2*j)))
    )
));

Class(fDot, FuncClass, rec(
    def    := (v, func) -> Chain(v.setRange(func.domain()), rec()),
    range  := self >> self.params[2].range(),
    domain := self >> self.params[2].domain(),
    lambda := self >> let(
        v    := self.params[1],
        func := self.params[2],
        i    := Ind(func.domain()),
        Lambda(i, SubstVars(func.at(i), tab((v.id) := i))))
));

Class(fPlaceholder, FuncClass, rec(
    def := func -> rec(),
    range := self >> self.params[1].range(),
    domain := self >> self.params[1].domain(),
    lambda := self >> self.params[1].lambda(),
    at := (self, n) >> self.params[1].at(n)
));

#F fAdd is the "add constant" index-mapping function
#F fAdd(N, n, k): II_n -> II_N, i->i+k
#F References: "Formal Loop Merging for Signal Transforms"
Class(fAdd, FuncClass, rec(
    def := (N, n, val) -> rec(),
    range  := self >> self.params[1],
    domain := self >> self.params[2],
    lambda := self >> let(i := Ind(self.params[2]), Lambda(i, i + self.params[3]))
));

#F fSub is the "subtract constant" index-mapping function
#F fSub(N, n, k): II_n -> II_N, i->i-k
#F References: "fAdd" function
Class(fSub, FuncClass, rec(
    def := (N, n, val) -> rec(N := N, n := n),
    lambda := self >> let(i := Ind(self.params[2]), Lambda(i, i - self.params[3]))
));

Function.hash := false;

Class(FuncClassOper, Function, rec(
    _perm := true,
    _children := [],

    child := (self, n) >> self._children[n],
    children := self >> self._children,
    numChildren := self >> Length(self._children),

    from_rChildren := (self, rch) >> ApplyFunc(ObjId(self), rch),
    rChildren := self >> self._children,
    rSetChild := meth(self, n, what)
        self._children[n] := what;
    end,
    free := self >> Union(List(self.rChildren(), FreeVars)),

    skipOneChild := false,

    advdims   := self >> [ [[ self.range() ]], [[ self.domain() ]] ],
    advrange  := self >> [[ self.range() ]],
    advdomain := self >> [[ self.domain() ]],
    # dimensionality of range (1-d, 2-d, etc)
    advrangeDim := self >> Length(self.advrange()[1]),
    # dimensionality of domain (1-d, 2-d, etc)
    advdomainDim := self >> Length(self.advdomain()[1]),

    __call__ := meth(arg)
        local self, children, lkup, res, h;
        self := arg[1];
        children := Flat(Drop(arg, 1));
	Constraint(ForAll(children, c -> IsFunction(c) or IsFuncExp(c)));
        if self.skipOneChild and Length(children)=1 then return children[1]; fi;

        h := self.hash;
        if h<>false then
            lkup := h.objLookup(self, children);
            if lkup[1]<>false then return lkup[1]; fi;
        fi;
        res := WithBases(self, rec(operations := RewritableObjectOps, _children := children));
        if h<>false then return h.objAdd(res, lkup[2]);
        else return res;
        fi;
    end,

    print := self >> Print(self.name, "(", PrintCS(self._children), ")"),
    range := self >> Error("not implemented"),
    domain := self >> Error("not implemented"),

    rightBinary := self >> FoldR1(self._children, (p,x) -> let(base:=self.__bases__[1], base(x, p))),
    leftBinary  := self >> FoldL1(self._children, (p,x) -> let(base:=self.__bases__[1], base(p, x))),

    equals := (self, o) >> ObjId(o) = ObjId(self) and self.rChildren() = o.rChildren(),

    lessThan := (self, o) >> Cond(
        ObjId(self) <> ObjId(o), ObjId(self) < ObjId(o),
        [ ObjId(self), self.rChildren() ] < [ ObjId(o), o.rChildren() ]
    ),
));

FuncClass.tensor_op := (v,range,domain,fl,gl) ->
    Lambda(v, range * fl.at(idiv(v, domain)) + gl.at(imod(v, domain)));

FuncClassOper.tensor_op := FuncClass.tensor_op;

Class(fTensorBase, SumsBase, FuncClassOper, rec(
    domain := self >> Product(self._children, x->x.domain()),

    range := self >> let(cht := List(self._children, x->x.range()),
        Cond(ForAll(cht, x->not IsType(x)), Product(cht),
             ForAll(cht, x->not IsType(x) or x=TInt), TInt,
             Error("self.range() can't handle this combination of self._children ranges"))),

    skipOneChild := true,

    split_op := self >> let(
        c := self.children(), v := Ind(self.domain()),
        c1dom := c[1].domain(), c2dom := c[2].domain(),
        Checked(Length(c)=2,
            [ Cond(c1dom=1, 0, c2dom=1, v, idiv(v, c2dom)),
              Cond(c1dom=1, v, c2dom=1, 0, imod(v, c2dom)),
              v ])),

    lambda := self >> Cond(
	self.numChildren() > 2,
	    self.rightBinary().lambda(),
        let(split := self.split_op(),
            f := self.child(1).lambda(),
            g := self.child(2).lambda(),
            jv := DropLast(When(Length(f.vars) > Length(g.vars), f.vars, g.vars), 1),
            Lambda(Concatenation(jv, [split[3]]),
                   self.combine_op(jv, split, f, g)))),

    isIdentity := self >> ForAll(self._children, IsIdentity),
));

Class(fTensor, fTensorBase, rec(
    combine_op := (self, jv, split, f, g) >>
        self.child(2).range() * f.relaxed_at(jv, split[1]) + g.relaxed_at(jv, split[2]),

    transpose := self >> self.__bases__[1](List(self.children(), c->c.transpose()))
));

Class(diagTensor, fTensorBase, rec(
    print := FuncClassOper.print,
    range := self >> UnifyTypes(List(self.children(), x->x.range())),
    combine_op := (self, jv, split, f, g) >> f.relaxed_at(jv, split[1]) * g.relaxed_at(jv, split[2])
));

Class(gammaTensor, fTensorBase, rec(
    isCyclic := true,
    print := FuncClassOper.print,
    combine_op := (self, jv, split, f, g) >> let(
        r := self.child(1).range(), s := self.child(2).range(),
        imod(s * no_mod(f.relaxed_at(jv,split[1])) + r * no_mod(g.relaxed_at(jv,split[2])), self.range()))
));

_rankedLambdaCompose := function(l1, l2)
    local jv, v, vars;
    l1.expr := l1.expr.eval();
    l2.expr := l2.expr.eval();

    vars := When(Length(l1.vars) > Length(l2.vars), l1.vars, l2.vars);
    v := Last(vars);
    jv := vars{[1..Length(vars)-1]};

    return Lambda(vars, l1.relaxed_at(jv, l2.relaxed_at(jv, v)));
end;

Class(fCompose, FuncClassOper, rec(
    domain := self >> Last(self._children).domain(),
    range := self >> self._children[1].range(),
    advdomain := self >> Last(self._children).advdomain(),
    advrange := self >> self._children[1].advrange(),
    skipOneChild := true,

    lambda := self >>
        FoldL1(List(self.children(), z->z.lambda()), _rankedLambdaCompose),

    transpose := self >> self.__bases__[1](List(Reversed(self.children()), c->c.transpose())),

    isIdentity := self >> ForAll(self._children, IsIdentity),
));

Class(fDirsum, FuncClassOper, rec(
    domain := self >> Sum(self._children, x->x.domain()),
    range  := self >> Sum(self._children, x->x.range()),
    skipOneChild := true,

    lambda := meth(self)
        local dom_spans, row_spans, i, conds, v;
        # spans uses 1-based offsets
        dom_spans := BaseOverlap.spans(0, List(self.children(), x->x.domain())) - 1;
        row_spans := BaseOverlap.spans(0, List(self.children(), x->x.range())) - 1;
        conds := [];
        v := Ind(self.domain());
        for i in [1..self.numChildren()] do
            Add(conds, leq(dom_spans[i][1], v, dom_spans[i][2]));
            Add(conds, row_spans[i][1] + self.child(i).lambda().at(v - dom_spans[i][1]));
        od;
        return Lambda(v, ApplyFunc(cond, conds));
    end,

    transpose := self >> self.__bases__[1](List(self.children(), c->c.transpose()))
));

Class(fStack, FuncClassOper, rec(
    domain := self >> Sum(self._children, x->x.domain()),
    # NB: use lowercase max that handles symbolics
    range := self >> ApplyFunc(max, List(self._children, x->x.range())),
    subdomainsDivisibleBy := (self, n) >> ForAll(self._children, x -> x.domain() mod n = 0),
    skipOneChild := true,

    lambda := meth(self)
        local dom_spans, i, conds, v;
        # spans uses 1-based offsets
        dom_spans := BaseOverlap.spans(0, List(self.children(), x->x.domain())) - 1;
        conds := [];
        v := Ind(self.domain());
        for i in [1..self.numChildren()] do
            Add(conds, leq(dom_spans[i][1], v, dom_spans[i][2]));
            Add(conds, self.child(i).lambda().at(v - dom_spans[i][1]));
        od;
	# Else branch needed below (=errExp), it ensures that cond(..).ev() does not crash
	# with nested conds. For example:
	#   cond(c1, cond(...., errExp), b)
	# if c1 is false, our inner cond can be invalid, because then b is taken instead
	Add(conds, errExp(conds[2].t));
        return Lambda(v, ApplyFunc(cond, conds));
    end
));

Class(diagDirsum, fStack, rec(
    range := self >> UnifyTypes(List(self.children(), x->x.range()))
));

# Function for Pease twiddle access. Maps n->n to index into
#   Diag(TC(n,2,0)) based upon iteration l.
#   fAccPease(n, r, l)
Class(fAccPease, FuncClass, rec(
   def := (n, r, l) -> rec(N:=n, n:=n),
   lambda := self >> let(
       N := self.params[1], k := Ind(N),
       R := self.params[2], l := self.params[3],

       Lambda(k, let(
           bits:=Log2Int(N)-Log2Int(R),
	       rCyclicShift(
               bin_and(
                   k,
                   concat(
                       bin_shl(
                           2^(bits)-1,
                           l*Log2Int(R),
                           bits
                       ),
		               R-1,
                       Log2Int(R)
                   )
               ),
               Log2Int(R),
               Log2Int(N)
           )
	   )))
));

#F fPad(<diag_func>)
#F
#F   Index function with 0 range and non zero domain.
#F   Returns result of <diag_func> diagonal function wrapped into funcExp().
#F   Relies on rewriting nth(<loc>, funcExp(A)) -> A.

Class(fPad, FuncClass, rec(
    def       := (f) -> rec(),

    domain    := self >> self.params[1].domain(),
    range     := self >> 0,

    lambda    := self >> let(
                    i := Ind(self.domain()),
                    l := self.params[1].lambda(),
                    Lambda(i, funcExp(l.at(i)))),

    transpose := self >> Error("fPad is not transposable, needs inert form"),
));

#F fInsert(<map>, <value>)
#F
#F Index function with padding.
#F
#F   Ex: Gath(fInsert([1, 1, 0, 1, 0], 3))*X ==
#F
#F   [ [ 1, 0, 0 ],
#F     [ 0, 1, 0 ],
#F     [ 0, 0, 0 ],  * X + [ 0, 0, 3, 0, 3 ]^T
#F     [ 0, 0, 1 ],
#F     [ 0, 0, 0 ] ]
#F
#F

Class(fInsert, FuncClass, rec(
    def := (map, val) -> rec(),
    domain := self >> Length(self.params[1]),
    range  := self >> Sum(self.params[1]),
    lambda := self >> let(
        i := Ind(self.domain()),
        map := self.params[1],
        v   := funcExp(self.params[2]),
        ctr := Counter(0),
        Lambda(i, ApplyFunc( cond,
            ConcatList([1..Length(map)], j ->[eq(i, j-1), Cond(map[j]<>0, ctr.next(), v)])
            :: [errExp(TInt)]))),
    transpose := self >> Error("fInsert is not transposable, needs inert form"),
));


# Copyright (c) 2018-2021, Carnegie Mellon University
# See LICENSE for details

DimLength := (l) -> When(IsList(l), Length(l), 1);

Class(CrossBase, BaseOperation, rec(
  abbrevs := [ arg -> [Flat(arg)] ],

   new := meth(self, L)
        if Length(L)=1 then return L[1]; fi;
        return SPL(WithBases(self, rec(_children:=L, dimensions := TransposedMat(List(L, e -> e.dims())))));
   end,

   area := self >> Sum(self.children(), x->x.area()),

   normalizedArithCost := self >> Sum(self.children(), x -> x.normalizedArithCost()),

));

Declare(Cross);

Class(Cross, CrossBase, rec(
   codeletName:="C",

   isPermutation := self >> false,

   dmn:=self>>let(li:=[],Flat(List(self._children, x-> x.dmn()))),

   rng:=self>>let(li:=[],Flat(List(self._children, x-> x.rng()))),

   sums:= self>>ObjId(self)(List(self.rChildren(),x->x.sums())),

   transpose := self >> self,

   isIdentity := self >> ForAll(self._children, IsIdentitySPL),

   isBlockTransitive := true,
));

Declare(fCross);
Class(fCross, FuncClassOper, rec(
   _perm := true,

   isFunction := true,
   skipOneChild := true,

   range := (self) >> StripList(List(self.rChildren(), (x) -> x.range())),

   advrange := (self) >> List(self.rChildren(), (x) -> x.advrange()[1]),

   domain := (self) >> StripList(List(self.rChildren(), (x) -> x.domain())),

   advdomain := (self) >> List(self.rChildren(), (x) -> x.advdomain()[1]),

   toSpl := (self, allinds, cols) >>
       fCross(List([1..Length(self.rChildren())],
               i -> self.rChildren()[i].toSpl(allinds,cols[i]))),

   downRank := (self, loopid, ind) >>
       fCross(List(self.rChildren(),
           c -> c.downRank(loopid, ind))),

   downRankFull := (self, allinds) >>
       fCross(List(self.rChildren(),
           c -> c.downRankFull(allinds))),

   isIdentity := self >> ForAll(self._children, IsIdentity),

   transpose  := self >> ObjId(self)(List(self._children, e -> e.transpose())),
));

Class(ExplicitGath, Gath);
Class(ExplicitCross, Cross);

Class(ExplicitOLRuleset, RuleSet);
RewriteRules(ExplicitOLRuleset, rec(
   Gath_f2DTrExpR := Rule([Gath, [@(2,fCompose), @(3,f2DTrExplicitR), ...]],
      e-> Gath(fCompose(Drop(@(2).val.rChildren(),1)))*ExplicitGath(ApplyFunc(f2DTr,@(3).val.rChildren()))),

   Gath_f2DTrExpLR := Rule([Gath, [@(2,fCompose), @(3,f2DTrExplicitL), @(4,f2DTrExplicitR), ...]],
      e-> Gath(fCompose([ApplyFunc(f2DTr,@(4).val.rChildren())]::Drop(@(2).val.rChildren(),2)))
      *ExplicitGath(ApplyFunc(f2DTr,@(3).val.rChildren()))),

   Cross_Compose_ExplicitGath_1 := Rule([Cross, [@(1,Compose), ..., @(2,ExplicitGath)], @(3) ],
       e ->
       Compose(Cross(Compose(DropLast(@(1).val.rChildren(),1)), @(3).val),
       ExplicitCross(ApplyFunc(Gath, @(2).val.rChildren()), ApplyFunc(2DI, @(3).val.advdims()[2][1])))),

   Cross_Compose_ExplicitGath_2 := Rule([Cross, @(3), [@(1,Compose), ..., @(2,ExplicitGath)] ],
       e ->
       Compose(Cross(@(3).val, Compose(DropLast(@(1).val.rChildren(),1))),
       ExplicitCross(ApplyFunc(2DI, @(3).val.advdims()[2][1]), ApplyFunc(Gath, @(2).val.rChildren())))),
));

Class(FinalOLRuleset, RuleSet);
RewriteRules(FinalOLRuleset, rec(
   ExplicitCross := Rule(@(1,ExplicitCross),
       e -> ApplyFunc(Cross, @(1).val.rChildren()))
));


#
# cls 70: A_2
#
handle70char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[70];
    info:=infos[70];

    rep:=Representative(o);
    #pr[19]=[0,0,1,1,1,0,0,0]
    #pr[17]=[0,1,1,1,0,0,0,0]
    #pr[37]=[1,1,1,2,1,0,0,0]
    tmp1:=ApplyRootsReflections(rep,[19,17,37]);
    uu:=Unipotent(chevalleyAdj(rep),[[3,1]]);
    tmp1:=Conj(tmp1,uu);
    #tmp1:=ConjugateByTori(tmp1,[3],[-1]);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[19,17,37]);
    uu:=Unipotent(chevalleyAdj(cent),[[3,1]]);
    tmp2:=Conj(tmp2,uu);
    #tmp2:=ConjugateByTori(tmp2,[3],[-1]);

    return [tmp1,tmp2];
end;

#
# cls 68: A_2A_1
#
handle68char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[68];
    info:=infos[68];

    rep:=Representative(o);
    #pr[53]=[1,1,1,2,1,1,1,0]
    #pr[52]=[1,1,1,2,2,1,0,0]
    #pr[93]=[1,2,3,4,3,2,1,0]
    tmp1:=ApplyRootsReflections(rep,[53,52,93]);
    uu:=Unipotent(chevalleyAdj(rep),[[1,-1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[1],[-1]);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[53,52,93]);
    uu:=Unipotent(chevalleyAdj(cent),[[1,-1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    tmp2:=ConjugateByTori(tmp2,[1],[-1]);

    return [tmp1,tmp2];
end;

#
# cls 64: A_2^2
#
handle64char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[64];
    info:=infos[64];

    rep:=Representative(o);
    #pr[43]=[0,0,1,1,1,1,1,1]
    #pr[41]=[0,1,1,1,1,1,1,0]
    #pr[79]=[1,1,1,2,2,2,2,1]
    tmp1:=ApplyRootsReflections(rep,[43,41,79]);
    uu:=Unipotent(chevalleyAdj(rep),[[1,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[3],[-1]);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[43,41,79]);
    uu:=Unipotent(chevalleyAdj(cent),[[1,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    tmp2:=ConjugateByTori(tmp2,[3],[-1]);

    return [tmp1,tmp2];
end;

#
# cls 61: D_4(a_1)
#
handle61char2:=function(orbs,infos)
    local tmp1,tmp2,tmp3,tmp4,o,info,rep,cent,uu;
    o:=Classes(orbs)[61];
    info:=infos[61];

    rep:=Representative(o);
    #pr[4] =[0,0,0,1,0,0,0,0]
    #pr[51]=[1,1,2,2,1,1,0,0]
    #pr[52]=[1,1,1,2,2,1,0,0]
    tmp1:=ApplyRootsReflections(rep,[4,51,52]);
    uu:=Unipotent(chevalleyAdj(rep),[[3,-1],[5,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[4,1,2,6],[-1,-1,-1,-1]);

    #pr[26]=[0,1,0,1,1,1,0,0]
    #pr[27]=[0,0,1,1,1,1,0,0]
    tmp3:=ApplyRootsReflections(rep,[26,27]);
    uu:=Unipotent(chevalleyAdj(rep),[[3,1],[4,1],[5,-1],[11,1],[12,-1]],Ordering(rep));
    tmp3:=Conj(tmp3,uu);
    tmp3:=ConjugateByTori(tmp3,[2,4],[-1,-1,-1,-1]);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[4,51,52]);
    uu:=Unipotent(chevalleyAdj(cent),[[3,-1],[5,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    tmp2:=ConjugateByTori(tmp2,[4,1,2,6],[-1,-1,-1,-1]);

    tmp4:=0;
    tmp4:=ApplyRootsReflections(cent,[26,27]);
    uu:=Unipotent(chevalleyAdj(cent),[[3,1],[4,1],[5,-1],[11,1],[12,-1]],Ordering(cent));
    tmp4:=Conj(tmp4,uu);
    tmp4:=ConjugateByTori(tmp4,[2,4],[-1,-1,-1,-1]);

    return [tmp1,tmp3,tmp2,tmp4];
end;

#
# NOT FINISHED .. Should be an S_3
#
# cls 57: D_4(a_1)A_1 ([2,5,9,10,7] in E_7) [2,5,10,11,7] in E_8  ?CHANGE THE REPRESENTATIVE IN CHARACTERISTIC 2?
#
handle57char2:=function(orbs,infos)
    local tmp1,tmp2,tmp3,tmp4,o,info,rep,cent,uu;
    o:=Classes(orbs)[57];
    info:=infos[57];

    rep:=Representative(o);

    #pr[23]=[1,1,1,1,0,0,0,0]
    #pr[24]=[1,0,1,1,1,0,0,0]
    tmp1:=ApplyRootsReflections(rep,[23,24]);
    tmp1:=ConjNegUa(tmp1,4,1);
    uu:=Unipotent(chevalleyAdj(rep),[[2,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    #pr[65]=[1,1,2,2,1,1,1,1]
    #pr[67]=[1,1,1,2,2,1,1,1]
    tmp3:=ApplyRootsReflections(rep,[65,67,4]);
    uu:=Unipotent(chevalleyAdj(rep),[[12,1],[11,1]],Ordering(rep));
    tmp3:=Conj(tmp3,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[23,24]);
    tmp2:=ConjNegUa(tmp2,4,1);
    uu:=Unipotent(chevalleyAdj(cent),[[2,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    tmp4:=0;
    tmp4:=ApplyRootsReflections(cent,[65,67,4]);
    uu:=Unipotent(chevalleyAdj(cent),[[12,1],[11,1]],Ordering(cent));
    tmp4:=Conj(tmp4,uu);

    return [tmp1,tmp3,tmp2,tmp4];
end;

#
# cls 54: A_4 [1,2,3,4]
#
handle54char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[54];
    info:=infos[54];

    rep:=Representative(o);

    #pr[33]=[0,1,1,1,1,1,0,0]
    #pr[31]=[1,0,1,1,1,1,0,0]
    #pr[64]=[1,1,2,2,2,1,1,0]
    #pr[89]=[1,2,2,4,3,2,1,0]
    tmp1:=ApplyRootsReflections(rep,[33,31,64,89]);
    uu:=Unipotent(chevalleyAdj(rep),[[2,1],[3,1],[9,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[33,31,64,89]);
    uu:=Unipotent(chevalleyAdj(cent),[[2,1],[3,1],[9,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 51: D_4(a_1)A_2 [2,5,10,11,7,8] in E_8
#
handle51char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[51];
    info:=infos[51];

    rep:=Representative(o);

    #pr[23]=[1,1,1,1,0,0,0,0]  asta merge si cu rep=[2,3,10,12,7,8] urmat de ConjNegUa(tmp1,4,1)
    #pr[24]=[1,0,1,1,1,0,0,0]
    tmp1:=ApplyRootsReflections(rep,[23,24]);
    tmp1:=ConjNegUa(tmp1,4,1);
    uu:=Unipotent(chevalleyAdj(rep),[[5,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[23,24]);
    tmp2:=ConjNegUa(tmp2,4,1);
    uu:=Unipotent(chevalleyAdj(cent),[[5,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 50: A_4A_1
#
handle50char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[50];
    info:=infos[50];

    rep:=Representative(o);

    #pr[41]=[0,1,1,1,1,1,1,0]
    #pr[39]=[1,0,1,1,1,1,1,0]
    #pr[57]=[1,1,2,2,2,1,0,0]
    #pr[89]=[1,2,2,4,3,2,1,0]
    tmp1:=ApplyRootsReflections(rep,[41,39,57,89]);
    uu:=Unipotent(chevalleyAdj(rep),[[2,1],[3,1],[9,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[41,39,57,89]);
    uu:=Unipotent(chevalleyAdj(cent),[[2,1],[3,1],[9,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 48: D_5(a_1)
#
handle48char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[48];
    info:=infos[48];

    rep:=Representative(o);

    #pr[82]=[1,1,2,3,3,2,1,0]
    #pr[80]=[1,2,2,3,2,2,1,0]
    tmp1:=ApplyRootsReflections(rep,[82,80]);
    uu:=Unipotent(chevalleyAdj(rep),[[4,1],[12,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[2,4],[-1,-1,-1,-1]);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[82,80]);
    uu:=Unipotent(chevalleyAdj(cent),[[4,1],[11,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    tmp2:=ConjugateByTori(tmp2,[2,4],[-1,-1,-1,-1]);

    return [tmp1,tmp2];
end;

#
# cls 47: A_4A_1^2
#
handle47char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[47];
    info:=infos[47];

    rep:=Representative(o);

    #pr[71]=[1,1,2,2,2,2,1,0]
    #pr[72]=[1,1,2,2,2,1,1,1]
    #pr[112]=[1,3,3,5,4,3,2,1]
    #pr[111]=[2,2,3,5,4,3,2,1]
    tmp1:=ApplyRootsReflections(rep,[71,72,112,111]);
    uu:=Unipotent(chevalleyAdj(rep),[[2,1],[3,1],[9,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[71,72,112,111]);
    uu:=Unipotent(chevalleyAdj(cent),[[2,1],[3,1],[9,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

# THE COMPONENT GROUP IS GENERATED BY u IN CHARACTERISTIC 2
#
# cls 40: D_4A_2
#
#handle40char3:=function(orbs,infos)
#    local tmp1,tmp2,o,info,rep,cent,uu;
#    o:=Classes(orbs)[40];
#    #info:=infos[40];
#
#    rep:=Representative(o);
#
#    #pr[34]=[0,1,0,1,1,1,1,0]
#    #pr[35]=[0,0,1,1,1,1,1,0]
#    #pr[68]=[0,1,1,2,2,2,1,1]
#    tmp1:=ApplyRootsReflections(rep,[34,35,68]);
#    uu:=Unipotent(chevalleyAdj(rep),[[8,-1]],Ordering(rep));
#    tmp1:=Conj(tmp1,uu);
#    tmp1:=ConjugateByTori(tmp1,[4,6,7,8],[-1,-1,-1,-1]);
#
#    tmp2:=0;
#    #cent:=FromPositiveBorel(o,info[2]);
#    #tmp2:=ApplyRootsReflections(cent,[36,34,46,61]);
#    #uu:=Unipotent(chevalleyAdj(cent),[[2,-1],[4,1]],Ordering(cent));
#    #tmp2:=Conj(tmp2,uu);
#    #tmp2:=ConjugateByTori(tmp2,[2,3,4],[-1,-1,-1,-1]);
#
#    return [tmp1,tmp2];
#end;

#
# cls 40: E_6(a_3)
#
handle40char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[40];
    info:=infos[40];

    rep:=Representative(o);
    tmp1:=ConjNegUa(rep,3,1);
    tmp1:=ConjNegUa(tmp1,2,1);
    uu:=Unipotent(chevalleyAdj(rep),[[5,1],[6,1],[9,1],[10,1],[17,1],[18,1],[25,1],[26,1],[33,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ConjNegUa(cent,3,1);
    tmp2:=ConjNegUa(tmp2,2,1);
    uu:=Unipotent(chevalleyAdj(cent),[[5,1],[6,1],[9,1],[10,1],[17,1],[18,1],[25,1],[26,1],[33,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 35: D_6(a_2)
#
handle35char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[35];
    info:=infos[35];

    rep:=Representative(o);

    #pr[42]=[0,1,0,1,1,1,1,0]
    #pr[43]=[0,0,1,1,1,1,1,0]
    tmp1:=ApplyRootsReflections(rep,[42,43]);
    uu:=Unipotent(chevalleyAdj(rep),[[4,1],[6,1],[10,1],[13,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[42,43]);
    uu:=Unipotent(chevalleyAdj(cent),[[4,1],[6,1],[10,1],[13,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 34: E_6(a_3)A_1
#
handle34char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[34];
    info:=infos[34];

    rep:=Representative(o);
    tmp1:=ConjNegUa(rep,3,1);
    tmp1:=ConjNegUa(tmp1,2,1);
    uu:=Unipotent(chevalleyAdj(rep),[[5,1],[6,1],[9,1],[10,1],[17,1],[18,1],[25,1],[26,1],[33,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ConjNegUa(cent,3,1);
    tmp2:=ConjNegUa(tmp2,2,1);
    uu:=Unipotent(chevalleyAdj(cent),[[5,1],[6,1],[9,1],[10,1],[17,1],[18,1],[25,1],[26,1],[33,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 33: E_7(a_5)
#
handle33char2:=function(orbs,infos)
    local tmp1,tmp2,tmp3,tmp4,o,info,rep,cent,uu;
    o:=Classes(orbs)[33];
    info:=infos[33];

    rep:=Representative(o);
    tmp1:=ApplyRootsReflections(rep,[2,3,5]);
    uu:=Unipotent(chevalleyAdj(rep),[[4,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp3:=ConjugateByTori(rep,[2,3,5],[aaa,aaa,aaa]);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[2,3,5]);
    uu:=Unipotent(chevalleyAdj(cent),[[4,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    tmp4:=0;
    tmp4:=ConjugateByTori(cent,[2,3,5],[aaa,aaa,aaa]);

    return [tmp1,tmp3,tmp2,tmp4];
end;

#
# cls 31: E_8(a_7)       NOT FINISHED IN CHARACTERISTIC 2 !!!!
#
handle31char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent;
    o:=Classes(orbs)[31];
    #info:=infos[31];

    rep:=Representative(o);

    tmp2:=0;
    #cent:=FromPositiveBorel(o,info[2]);

    return [tmp1,tmp2];
end;

#
# cls 30: A_6       NOT FINISHED IN CHARACTERISTIC 2 !!!!
#
handle30char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[30];
    info:=infos[30];

    rep:=Representative(o);
    tmp1:=rep^-1;              # longest words in E7 and E6: n_0*w_0=(17)(36)(45)
    #uu:=Unipotent(chevalleyAdj(rep),[[3,1],[5,1],[7,1],[10,1],[11,1],[24,1],[25,1]],Ordering(rep)); in E_7
    uu:=Unipotent(chevalleyAdj(rep),[[3,1],[5,1],[7,1],[11,1],[12,1],[27,1],[28,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);# =[27,28,24,39]
    tmp2:=coefficients(cent);# =[27,28,24,39]
    tmp2:=Unipotent(chevalleyAdj(cent),[tmp2[1],tmp2[3],tmp2[2],tmp2[4]],Ordering(cent));# =[27,24,28,39]
    uu:=Unipotent(chevalleyAdj(cent),[[3,1],[5,1],[7,1],[11,1],[12,1],[27,1],[28,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 29: D_6(a_1)
#
handle29char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[29];
    info:=infos[29];

    rep:=Representative(o);

    #pr[42]=[0,1,0,1,1,1,1,0]
    #pr[43]=[0,0,1,1,1,1,1,0]
    tmp1:=ApplyRootsReflections(rep,[42,43]);
    uu:=Unipotent(chevalleyAdj(rep),[[4,1],[11,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[3,4],[-1,-1,-1,-1]);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[42,43]);
    uu:=Unipotent(chevalleyAdj(cent),[[4,1],[11,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    #tmp2:=ConjugateByTori(tmp2,[2,3,4],[-1,-1,-1,-1]);

    return [tmp1,tmp2];
end;

# NO COMPONENT GROUP IN CHARACTERISTIC 2 FOR THIS CLASS
#
# cls 27: E_7(a_4)
#
#handle27char2:=function(orbs,infos)
#    local tmp1,tmp2,o,info,rep,cent;
#    o:=Classes(orbs)[27];
#    #info:=infos[27];
#
#    rep:=Representative(o);
#    tmp1:=ConjugateByTorus(rep,4,-1);# h_1 --> h_4
#
#    tmp2:=0;
#    #cent:=FromPositiveBorel(o,info[2]);
#    #tmp2:=ConjugateByTorus(cent,4,-1);# h_1 --> h_4
#
#    return [tmp1,tmp2];
#end;

#
# cls 26: E_6(a_1)
#
handle26char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[26];
    info:=infos[26];

    #pr[61]=[0,1,1,2,2,2,1]
    #pr[59]=[1,1,1,2,2,1,1]
    #pr[58]=[1,1,2,2,1,1,1]

    rep:=Representative(o);
    tmp1:=ApplyRootsReflections(rep,[58,59,61]);
    tmp1:=ConjNegUa(tmp1,4,1);
    uu:=Unipotent(chevalleyAdj(rep),[[2,1],[6,1],[10,1],[11,1],[16,1],[17,1],[23,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[58,59,61]);
    tmp2:=ConjNegUa(tmp2,4,1);
    uu:=Unipotent(chevalleyAdj(cent),[[2,1],[6,1],[10,1],[11,1],[16,1],[17,1],[23,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2,uu];
end;

# THE COMPONENT GROUP IS GENERATED BY u IN CHARACTERISTIC 2
#
# cls 24: D_5A_2
#
#handle24char3:=function(orbs,infos)
#    local tmp1,tmp2,o,info,rep,cent,uu;
#    o:=Classes(orbs)[24];
#    #info:=infos[24];
#
#    rep:=Representative(o);
#
#    #pr[87]=[1,1,2,3,3,2,1,1]
#    #pr[86]=[1,2,2,3,2,2,1,1]
#    #pr[119]=[2,3,4,6,5,4,3,1]
#    tmp1:=ApplyRootsReflections(rep,[87,86,119]);
#    uu:=Unipotent(chevalleyAdj(rep),[[8,-1]],Ordering(rep));
#    tmp1:=Conj(tmp1,uu);
#    tmp1:=ConjugateByTori(tmp1,[1,4,7],[-1,-1,-1,-1,-1]);
#
#    tmp2:=0;
#    #cent:=FromPositiveBorel(o,info[2]);
#    #tmp2:=ApplyRootsReflections(cent,[36,34,46,61]);
#    #uu:=Unipotent(chevalleyAdj(cent),[[2,-1],[4,1]],Ordering(cent));
#    #tmp2:=Conj(tmp2,uu);
#    #tmp2:=ConjugateByTori(tmp2,[2,3,4],[-1,-1,-1,-1]);
#
#    return [tmp1,tmp2];
#end;

#
# cls 21: D_7(a_2)
#
handle21char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[21];
    info:=infos[21];

    rep:=Representative(o);

    #pr[113]=[2,3,3,5,4,3,2,1]
    #pr[114]=[0,0,1,1,1,1,1,0]
    tmp1:=ApplyRootsReflections(rep,[113,114]);
    uu:=Unipotent(chevalleyAdj(rep),[[4,1],[6,1],[11,1],[13,1],[20,1],[27,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[113,114]);
    uu:=Unipotent(chevalleyAdj(cent),[[4,1],[6,1],[11,1],[13,1],[20,1],[27,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;
#
# THIS IS FOR THE D_7(a_2) class in E_8 characteristic 2
#
#set:=Filtered(subs,i->i<=63);
#Sort(set);
#uu:=Unipotent(alg,List(set,i->[i,avars[i]]),Ordering(rep));
#vals:=ShallowCopy(avars);
#vals[3]:=-vals[2];
#vals[4]:=-One(APR);
#vals[5]:=0;
#vals[6]:=One(APR);
#vals[7]:=0;
#vals[8]:=-vals[3];
#vals[11]:=-(vals[2]+vals[10]+One(APR));
#vals[12]:=-vals[3];
#vals[13]:=-(vals[2]+One(APR));
#vals[14]:=-vals[8];
#vals[15]:=-(vals[2]^2+vals[2]);
#vals[18]:=-(vals[2]^2+vals[2]+vals[17]);
#vals[19]:=-(vals[2]^2+vals[2]+vals[18]);
#vals[20]:=-(vals[2]+vals[10]+vals[13]);
#vals[21]:=-vals[15];
#vals[22]:=-(vals[2]^2+vals[19]);
#vals[26]:=-(vals[2]^2+vals[2]*vals[10]+vals[10]^2+vals[2]+vals[10]+vals[17]);
#vals[27]:=-(vals[2]^2+vals[2]*vals[13]+vals[10]*vals[13]+vals[11]*vals[13]+vals[2]+vals[10]+vals[11]+vals[13]+vals[17]+vals[20]+vals[26]+One(APR));
#vals[28]:=-vals[19];
#vals[29]:=-(vals[2]^3+vals[2]^2+vals[17]+vals[25]);

#vals[33]:=-(vals[2]^3+vals[2]^2*vals[10]+vals[2]*vals[10]+vals[2]+vals[17]+vals[32]);
#vals[34]:=-(vals[2]^3+vals[2]^2+vals[25]+vals[32]);
#vals[35]:=-(vals[2]^3+vals[2]^2+vals[17]+vals[25]+vals[34]);
#vals[36]:=-(vals[2]^2*vals[10]+vals[2]^2+vals[2]*vals[10]+vals[2]+vals[29]+vals[34]);
#vals[41]:=-(vals[2]^2*vals[17]+vals[2]*vals[17]+vals[17]^2+vals[25]);
#vals[43]:=-(vals[2]^4+vals[2]^2*vals[17]+vals[2]^2+vals[2]*vals[17]+vals[17]+vals[25]+vals[34]+vals[35]+vals[36]+vals[41]+vals[42]);
#vals[48]:=-(vals[2]^3*vals[10]+vals[2]^3+vals[2]*vals[10]+vals[10]*vals[17]+vals[2]+vals[17]+vals[25]+vals[32]+vals[42]);
#vals[49]:=-(vals[2]^4+vals[2]^3*vals[10]+vals[2]^2*vals[10]^2+vals[2]^3+vals[2]*vals[10]^2+vals[2]*vals[10]+vals[2]*vals[17]+vals[25]+vals[32]+vals[34]+vals[41]+vals[42]);

#vals[55]:=-(vals[2]^5+vals[2]^4*vals[10]+vals[2]^4+vals[2]^3+vals[2]^2*vals[10]+vals[2]^2+vals[17]^2+vals[17]*vals[25]+vals[25]+vals[50]);
#vals[61]:=-(vals[2]^5+vals[2]^4*vals[10]+vals[2]^4+vals[2]^3*vals[10]+vals[2]^3*vals[17]+vals[2]^2*vals[10]^2+vals[2]^2*vals[10]*vals[17]+vals[2]^2*vals[17]+vals[2]*vals[10]^2+vals[2]*vals[10]*vals[17]+vals[2]^2+vals[2]*vals[17]+vals[10]*vals[17]+vals[2]+vals[17]+vals[32]+vals[48]+vals[56]);
#vals[62]:=-(vals[2]^6+vals[2]^4*vals[10]^2+vals[2]^5+vals[2]^4*vals[10]+vals[2]^4*vals[17]+vals[2]^4+vals[2]^2*vals[10]^2+vals[2]^2*vals[10]*vals[17]+vals[2]^2*vals[10]*vals[25]+vals[2]^2*vals[17]^2+vals[2]^3+vals[2]^2*vals[10]+vals[2]^2*vals[25]+vals[2]*vals[10]*vals[17]+vals[2]*vals[10]*vals[25]+vals[17]^3+vals[2]*vals[17]+vals[2]*vals[25]+vals[17]^2+vals[17]*vals[25]+vals[32]^2+vals[25]+vals[50]);

#vv:=Value(uu,avars,vals);
#vvv:=Value(vv,avars,List(avars,i->0));

#
# cls 19: E_6(a_1)A_1
#
handle19char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[19];
    info:=infos[19];

    #pr[106]=[1,2,2,4,4,3,2,1]
    #pr[105]=[1,2,3,4,3,3,2,1]
    #pr[104]=[2,2,3,4,3,2,2,1]

    rep:=Representative(o);
    tmp1:=ApplyRootsReflections(rep,[106,105,104]);
    tmp1:=ConjNegUa(tmp1,4,1);
    uu:=Unipotent(chevalleyAdj(rep),[[2,1],[11,1],[10,1],[6,1],[16,1],[17,1],[23,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[106,105,104]);
    tmp2:=ConjNegUa(tmp2,4,1);
    uu:=Unipotent(chevalleyAdj(cent),[[2,1],[11,1],[10,1],[6,1],[16,1],[17,1],[23,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2,uu];
end;

#
# cls 18: E_7(a_3)
#
handle18char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[18];
    info:=infos[18];

    rep:=Representative(o);
    tmp1:=ConjNegUa(rep,3,1);
    tmp1:=ConjNegUa(tmp1,2,1);
    uu:=Unipotent(chevalleyAdj(rep),[[5,1],[18,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ConjNegUa(cent,3,1);
    tmp2:=ConjNegUa(tmp2,2,1);
    uu:=Unipotent(chevalleyAdj(cent),[[5,1],[18,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 17: E_8(b_6)
#
handle17char2:=function(orbs,infos)
    local tmp1,tmp2,tmp3,tmp4,o,info,rep,cent,uu;
    o:=Classes(orbs)[17];
    info:=infos[17];

    rep:=Representative(o);
    tmp1:=ApplyRootsReflections(rep,[1,2,5]);
    uu:=Unipotent(chevalleyAdj(rep),[[7,1],[8,1],[18,1],[23,1],[31,1],[39,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp3:=ConjugateByTori(rep,[1,2,5],[aaa,aaa,aaa^2]);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[1,2,5]);
    uu:=Unipotent(chevalleyAdj(cent),[[7,1],[8,1],[18,1],[23,1],[31,1],[39,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    tmp4:=0;
    tmp4:=ConjugateByTori(cent,[1,2,5],[aaa,aaa,aaa^2]);

    return [tmp1,tmp3,tmp2,tmp4];
end;

# THIS WORKS BUT HERE THE COMPONENT GROUP IS GENERATED BY U
#
# cls 16: D_7(a_1)
#
handle16char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[16];
    info:=infos[16];

    rep:=Representative(o);

    #pr[113]=[2,3,3,5,4,3,2,1]
    #pr[114]=[0,0,1,1,1,1,1,0]
    tmp1:=ApplyRootsReflections(rep,[113,114]);
    #uu:=Unipotent(chevalleyAdj(rep),[[4,1],[10,-1]],Ordering(rep)); si asta merge
    uu:=Unipotent(chevalleyAdj(rep),[[4,1],[11,-1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    #tmp1:=ConjugateByTori(tmp1,[2,3,4],[aaa,aaa,-1]); IN CHAR 2 I DON'T NEED THIS

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[113,114]);
    uu:=Unipotent(chevalleyAdj(cent),[[4,1],[11,-1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    #tmp2:=ConjugateByTori(tmp2,[2,3,4],[-1,-1,-1,-1]);

    return [tmp1,tmp2];
end;

#
# cls 12: E_8(a_6)
#
handle12char2:=function(orbs,infos)
    local tmp1,tmp2,tmp3,tmp4,o,info,rep,cent,uu;
    o:=Classes(orbs)[12];
    info:=infos[12];

    rep:=Representative(o);
    tmp1:=ApplyRootsReflections(rep,[2,3,8,13]);
    uu:=Unipotent(chevalleyAdj(rep),[[18,1],[29,1],[31,1],[33,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp3:=ConjugateByTori(rep,[2,3,5,6,8],[aaa,aaa,aaa,aaa,aaa^2]);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[2,3,8,13]);
    uu:=Unipotent(chevalleyAdj(cent),[[18,1],[29,1],[31,1],[33,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    tmp4:=0;
    tmp4:=ConjugateByTori(cent,[2,3,5,6,8],[aaa,aaa,aaa,aaa,aaa^2]);

    return [tmp1,tmp3,tmp2,tmp4];
end;

#
# cls 10: E_8(b_5)
#
handle10char2:=function(orbs,infos)
    local tmp1,tmp2,tmp3,tmp4,o,info,rep,cent,uu;
    o:=Classes(orbs)[10];
    info:=infos[10];

    rep:=Representative(o);
    tmp1:=ApplyRootsReflections(rep,[1,2,5]);
    uu:=Unipotent(chevalleyAdj(rep),[[19,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp3:=ConjugateByTori(rep,[1,2,5],[aaa,aaa,aaa^2]);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[1,2,5]);
    uu:=Unipotent(chevalleyAdj(cent),[[19,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    tmp4:=0;
    tmp4:=ConjugateByTori(cent,[1,2,5],[aaa,aaa,aaa^2]);

    return [tmp1,tmp3,tmp2,tmp4];
end;

#
# cls 8: E_8(a_5)       NOT FINISHED IN CHARACTERISTIC 2 !!!!
#
handle8char2:=function(orbs,infos)
    local tmp1,tmp2,tmp3,tmp4,o,info,rep,cent,uu;
    o:=Classes(orbs)[8];
    info:=infos[8];

    rep:=Representative(o);
    uu:=Unipotent(chevalleyAdj(rep),[ [ 2, 1 ], [ 3, 1 ], [ 6, 1 ], [ 8, 1 ], [ 21, 1 ], [ 25, 1 ] ],Ordering(rep));
    tmp1:=Conj(rep,uu);

    uu:=Unipotent(chevalleyAdj(rep),[ [ 1, 1 ], [ 7, 1 ], [ 10, 1 ], [ 12, 1 ], [ 14, 1 ], [ 16, 1 ], [ 19, 1 ], [ 21, 1 ], [ 23, 1 ], [ 25, 1 ], [ 28, 1 ], [ 37, 1 ], [ 44, 1 ], [ 46, 1 ], [ 53, 1 ], [ 55, 1 ], [ 59, 1 ], [ 66, 1 ], [ 67, 1 ], [ 69, 1 ], [ 71, 1 ], [ 74, 1 ], [ 76, 1 ], [ 77, 1 ], [ 81, 1 ], [ 107, 1 ], [ 110, 1 ], [ 114, 1 ], [ 115, 1 ] ],Ordering(rep));
    tmp3:=Conj(rep,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    uu:=Unipotent(chevalleyAdj(cent),[ [ 2, 1 ], [ 3, 1 ], [ 6, 1 ], [ 8, 1 ], [ 21, 1 ], [ 25, 1 ] ],Ordering(cent));
    tmp2:=Conj(cent,uu);

    tmp4:=0;
    uu:=Unipotent(chevalleyAdj(cent),[ [ 1, 1 ], [ 7, 1 ], [ 10, 1 ], [ 12, 1 ], [ 14, 1 ], [ 16, 1 ], [ 19, 1 ], [ 21, 1 ], [ 23, 1 ], [ 25, 1 ], [ 28, 1 ], [ 37, 1 ], [ 44, 1 ], [ 46, 1 ], [ 53, 1 ], [ 55, 1 ], [ 59, 1 ], [ 66, 1 ], [ 67, 1 ], [ 69, 1 ], [ 71, 1 ], [ 74, 1 ], [ 76, 1 ], [ 77, 1 ], [ 81, 1 ], [ 107, 1 ], [ 110, 1 ], [ 114, 1 ], [ 115, 1 ] ],Ordering(cent));
    tmp4:=Conj(cent,uu);

    return [tmp1,tmp3,tmp2,tmp4,uu];
end;

# THE COMPONENT GROUP IS GENERATED BY u IN CHARACTERISTIC 2
#
# cls 7: E_8(b_4) [1,4,7,8,14,17,19,26]  this doesn't work in char2
#
#handle7char3:=function(orbs,infos)
#    local tmp1,tmp2,o,info,rep,cent;
#    o:=Classes(orbs)[7];
#    #info:=infos[7];
#
#    rep:=Representative(o);
#    tmp1:=ConjugateByTorus(rep,4,-1);# h_1 --> h_4
#
#    tmp2:=0;
#    #cent:=FromPositiveBorel(o,info[2]);
#    #tmp2:=ConjugateByTorus(cent,4,-1);# h_1 --> h_4
#
#    return [tmp1,tmp2];
#end;

#
# cls 5: E_8(a_4)       NOT FINISHED IN CHARACTERISTIC 2 !!!!
#
handle5char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[5];
    info:=infos[5];

    rep:=Representative(o);
    uu:=Unipotent(chevalleyAdj(rep),[[2,1],[3,1],[5,1],[7,1]],Ordering(rep));
    tmp1:=Conj(rep,uu);
    uu:=Unipotent(chevalleyAdj(rep),[ [ 1, 1 ], [ 4, 1 ], [ 6, 1 ], [ 8, 1 ], [ 11, 1 ], [ 12, 1 ], [ 14, 1 ], [ 16, 1 ], [ 19, 1 ], [ 25, 1 ], [ 27, 1 ], [ 28, 1 ], [ 30, 1 ], [ 31, 1 ], [ 32, 1 ], [ 33, 1 ], [ 35, 1 ], [ 37, 1 ], [ 38, 1 ], [ 39, 1 ], [ 40, 1 ], [ 41, 1 ], [ 45, 1 ], [ 46, 1 ], [ 48, 1 ], [ 49, 1 ], [ 52, 1 ], [ 53, 1 ], [ 59, 1 ], [ 64, 1 ], [ 66, 1 ], [ 67, 1 ], [ 72, 1 ], [ 77, 1 ], [ 80, 1 ], [ 81, 1 ], [ 85, 1 ], [ 98, 1 ], [ 99, 1 ], [ 100, 1 ], [ 102, 1 ], [ 106, 1 ], [ 107, 1 ], [ 110, 1 ], [ 112, 1 ], [ 114, 1 ], [ 115, 1 ], [ 116, 1 ] ],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    uu:=Unipotent(chevalleyAdj(cent),[[2,1],[3,1],[5,1],[7,1]],Ordering(cent));
    tmp2:=Conj(cent,uu);
    uu:=Unipotent(chevalleyAdj(cent),[ [ 1, 1 ], [ 4, 1 ], [ 6, 1 ], [ 8, 1 ], [ 11, 1 ], [ 12, 1 ], [ 14, 1 ], [ 16, 1 ], [ 19, 1 ], [ 25, 1 ], [ 27, 1 ], [ 28, 1 ], [ 30, 1 ], [ 31, 1 ], [ 32, 1 ], [ 33, 1 ], [ 35, 1 ], [ 37, 1 ], [ 38, 1 ], [ 39, 1 ], [ 40, 1 ], [ 41, 1 ], [ 45, 1 ], [ 46, 1 ], [ 48, 1 ], [ 49, 1 ], [ 52, 1 ], [ 53, 1 ], [ 59, 1 ], [ 64, 1 ], [ 66, 1 ], [ 67, 1 ], [ 72, 1 ], [ 77, 1 ], [ 80, 1 ], [ 81, 1 ], [ 85, 1 ], [ 98, 1 ], [ 99, 1 ], [ 100, 1 ], [ 102, 1 ], [ 106, 1 ], [ 107, 1 ], [ 110, 1 ], [ 112, 1 ], [ 114, 1 ], [ 115, 1 ], [ 116, 1 ] ],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2,uu];
end;

#
# cls 4: E_8(a_3)       NOT FINISHED IN CHARACTERISTIC 2 !!!!
#
handle4char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[4];
    info:=infos[4];

    rep:=Representative(o);
    uu:=Unipotent(chevalleyAdj(rep),[[2,1],[3,1],[5,1],[19,1]],Ordering(rep));
    tmp1:=Conj(rep,uu);

    tmp2:=0;
    cent:=FromPositiveBorel(o,info[2]);
    uu:=Unipotent(chevalleyAdj(cent),[[2,1],[3,1],[5,1],[19,1]],Ordering(cent));
    tmp2:=Conj(cent,uu);

    return [tmp1,tmp2];
end;


# Copyright (c) 2018-2021, Carnegie Mellon University
# See LICENSE for details

####################################################
##
## NOTE: Chained unparser!
##

_bitsInt := (bits) -> Sum(List([1..Length(bits)], i -> bits[i]*2^(i-1)));

_sklr_unaligned_store := (bits) -> meth(self, o, i, is )
    local loc0, loc1, offs, p, loffs, mask, l;

    if  IsUnalignedPtrT(o.loc.t) then
        Error("cannot do unaligned store when base pointer is not aligned");
    fi;

    loc0 := vtref(o.exp.t, o.loc, idiv(o.offs, bits));
    loc1 := vtref(o.exp.t, o.loc, idiv(o.offs, bits)+1);
    offs := _unwrap(o.offs) mod bits;
    p    := _unwrap(o.p);
    loffs:= p+offs-bits;
    mask := (n) -> o.exp.t.value(Replicate(n, 1) :: Replicate(bits-n, 0));
    l    := Cond(
                (IsSymbolic(offs) and (p>1)) or (not IsSymbolic(offs) and (offs + p > bits)),
                          # a0 = a0 xor (( a0 xor (val << offs)) & (mask << offs));
                          # a1 = (offs + p <= bits) ? a1 :
                          #          ((a1 >> loffs) << loffs) | ((val & mask) >> bits-offs)
                        [ assign(loc0, bin_xor(loc0, bin_and(bin_shl(mask(p), offs), bin_xor(loc0, bin_shl(o.exp, offs))))),
                          assign(loc1, cond(leq(offs + p, bits), loc1, bin_or(bin_shl(bin_shr(loc1, loffs), loffs), bin_shr(bin_and(o.exp, mask(p)), bits-offs))))],
                p = bits and offs = 0,
                        [ assign(loc0, o.exp) ],
                #else
                        [ assign(loc0, bin_xor(loc0, bin_and(bin_shl(mask(p), offs), bin_xor(loc0, bin_shl(o.exp, offs))))) ]);
    return Print(Blanks(i), self.infix(ESReduce(l, self.opts), Blanks(i)));
end;

Class(SKLRBUnparser, SSEUnparser, rec(
   # none exp must be gone before it reaches unparser
   noneExp := (self, o, i, is) >> self(o.args[1].zero(), i, is),

   utype := (self, t) >> (ObjId(t) = BitVector or (ObjId(t) = TVect and t.t in  [T_Int(1), T_UInt(1), TInt, TUInt]))
                         and t.size in [16, 32, 64],

   BitVector := (self, t, vars, i, is) >> self.T_UInt(T_UInt(t.size), vars, i, is),

   Value := (self, o, i, is) >> When( self.utype(o.t),
        #self(T_UInt(o.t.size).value(_bitsInt(o.v)), i, is),
        Print("0x", HexStringInt(_bitsInt(o.v)), When(o.t.size=64, "LL", "")),
        Inherited(o, i, is)),

   mul := (self, o, i, is) >> Cond( self.utype(o.t), Print("(", self.infix(o.args, ")&("), ")"),
                                    # NOTE: HACK HACK 16x8i mul -> binary and
                                    o.t = TVect(T_Int(8), 16), self( ApplyFunc(bin_and, o.args), i, is ),
                                    Inherited(o, i, is)),
   add := (self, o, i, is) >> When( self.utype(o.t), Print("(", self.infix(o.args, ")^("), ")"), Inherited(o, i, is)),
   sub := ~.add,

   rCyclicShift := (self, o, i, is) >> When( not self.utype(o.t), Inherited(o, i, is),
                      Cond( o.t.size = 16, self.printf("__rotl16($1, $2)", [o.args[1], o.args[2]]),
                            o.t.size = 32, self.printf("__rotl32($1, $2)", [o.args[1], o.args[2]]),
                            o.t.size = 64, self.printf("__rotl64($1, $2)", [o.args[1], o.args[2]]),
                            Error("unsupported size")
                      )),

   sklr_bcast_32x1i := (self, o, i, is) >> self.printf("$1", [tcast(o.t, arith_shr(tcast(T_Int(32), bin_shl(o.args[1], 31-o.args[2])), 31))]),
   sklr_bcast_64x1i := (self, o, i, is) >> self.printf("$1", [tcast(o.t, arith_shr(tcast(T_Int(64), bin_shl(o.args[1], 63-o.args[2])), 63))]),

   sklr_storeu_32x1i := _sklr_unaligned_store(32),
   sklr_storeu_64x1i := _sklr_unaligned_store(64),
));

SKLRBUnparser.bin_shl := (self, o, i, is) >> When( self.utype(o.t) or IsOrdT(o.t),
         self.printf("(($1)<<($2))", [o.args[1], o.args[2]]),
     # else
         Inherited(o, i, is));
SKLRBUnparser.bin_shr := (self, o, i, is) >> Cond(
       self.utype(o.t) or IsUIntT(o.t), self.printf("(($1)>>($2))", [o.args[1], o.args[2]]),
       self.utype(o.t) or IsIntT(o.t),
           Print("((", self.declare(o.t, [], 0, 0), ")(((", self.declare(When(ObjId(o.t)=T_Int, T_UInt(o.t.params[1]), TUInt), [], 0, 0), ")(",
               self(o.args[1], i, is), "))>>(", self(o.args[2], i, is), ")))"),
       Inherited(o, i, is));
SKLRBUnparser.arith_shr := (self, o, i, is) >> Cond(
       IsIntT(o.t), self.printf("(($1)>>($2))", [o.args[1], o.args[2]]),
       self.utype(o.t) or ObjId(o.t)=T_UInt,
           Print("((", self.declare(o.t, [], 0, 0), ")(((", self.declare(T_Int(When(ObjId(o.t)=T_Int,o.t.params[1], o.t.size)), [], 0, 0), ")(",
               self(o.args[1], i, is), "))>>(", self(o.args[2], i, is), ")))"),
       Inherited(o, i, is));
SKLRBUnparser.bin_and := (self, o, i, is) >> When( self.utype(o.t) or o.t in [T_Int(32), T_UInt(32), T_Int(64), T_UInt(64)],
       Print("((", self.infix(o.args, ")&("), "))"),
       Inherited(o, i, is));
SKLRBUnparser.bin_xor := (self, o, i, is) >> When( self.utype(o.t) or o.t in [T_Int(32), T_UInt(32), T_Int(64), T_UInt(64)],
       Print("(", self.infix(o.args, ")^("), ")"),
       Inherited(o, i, is));
SKLRBUnparser.bin_or := (self, o, i, is) >> When( self.utype(o.t) or o.t in [T_Int(32), T_UInt(32), T_Int(64), T_UInt(64)],
       Print("(", self.infix(o.args, ")|("), ")"),
       Inherited(o, i, is));
SKLRBUnparser.tcast := (self, o, i, is) >> When( not self.utype(o.t), Inherited(o, i, is),
       Print("((", self.declare(o.args[1], [], i, is), ")(", self(o.args[2], i, is), "))"));


#
# cls 40: A_2
#
handle40char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[40];
    info:=infos[40];

    rep:=Representative(o);
    #pr[17]=[0,0,1,1,1,0,0]
    #pr[15]=[0,1,1,1,0,0,0]
    #pr[32]=[1,1,1,2,1,0,0]
    tmp1:=ApplyRootsReflections(rep,[17,15,32]);
    uu:=Unipotent(chevalleyAdj(rep),[[3,1]]);
    tmp1:=Conj(tmp1,uu);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[17,15,32]);
    uu:=Unipotent(chevalleyAdj(cent),[[3,1]]);
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 38: A_2A_1
#
handle38char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[38];
    info:=infos[38];

    rep:=Representative(o);
    #pr[44]=[1,1,1,2,1,1,1]
    #pr[43]=[1,1,1,2,2,1,0]
    #pr[62]=[1,2,3,4,3,2,1]
    tmp1:=ApplyRootsReflections(rep,[44,43,62]);
    uu:=Unipotent(chevalleyAdj(rep),[[1,-1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[44,43,62]);
    uu:=Unipotent(chevalleyAdj(cent),[[1,-1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 30: D_4(a_1)
#
handle30char2:=function(orbs,infos)
    local tmp1,tmp2,tmp3,tmp4,o,info,rep,cent,uu;
    o:=Classes(orbs)[30];
    info:=infos[30];

    rep:=Representative(o);
    #pr[4] =[0,0,0,1,0,0,0]
    #pr[42]=[1,1,2,2,1,1,0]
    #pr[43]=[1,1,1,2,2,1,0]
    tmp1:=ApplyRootsReflections(rep,[4,42,43]);
    uu:=Unipotent(chevalleyAdj(rep),[[3,-1],[5,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[4,1,2,6],[-1,-1,-1,-1]);

    #pr[23]=[0,1,0,1,1,1,0]
    #pr[24]=[0,0,1,1,1,1,0]
    tmp3:=ApplyRootsReflections(rep,[23,24]);
    uu:=Unipotent(chevalleyAdj(rep),[[3,1],[4,1],[5,-1],[10,1],[11,-1]],Ordering(rep));
    tmp3:=Conj(tmp3,uu);
    tmp3:=ConjugateByTori(tmp3,[2,4],[-1,-1,-1,-1]);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[4,42,43]);
    uu:=Unipotent(chevalleyAdj(cent),[[3,-1],[5,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    tmp2:=ConjugateByTori(tmp2,[4,1,2,6],[-1,-1,-1,-1]);

    tmp4:=ApplyRootsReflections(cent,[23,24]);
    uu:=Unipotent(chevalleyAdj(cent),[[3,1],[4,1],[5,-1],[10,1],[11,-1]],Ordering(cent));
    tmp4:=Conj(tmp4,uu);
    tmp4:=ConjugateByTori(tmp4,[2,4],[-1,-1,-1,-1]);

    return [tmp1,tmp3,tmp2,tmp4];
end;

#
# cls 27: D_4(a_1)A_1 [2,5,9,10,7]
#
handle27char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[27];
    #o:=orbs;
    info:=infos[27];
    #info:=infos;

    rep:=Representative(o);

    #pr[20]=[1,1,1,1,0,0,0]
    #pr[21]=[1,0,1,1,1,0,0]
    tmp1:=ApplyRootsReflections(rep,[20,21]);
    uu:=Unipotent(chevalleyAdj(rep),[[3,-1],[4,-1],[5,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[1,4],[-1,-1,-1,-1]);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[20,21]);
    uu:=Unipotent(chevalleyAdj(cent),[[3,-1],[4,-1],[5,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    tmp2:=ConjugateByTori(tmp2,[1,4],[-1,-1,-1,-1]);

    return [tmp1,tmp2];
end;

#
# cls 26: A_3A_2^{(2)} from [1,3,4,6,7,37] we get [31,20,40,30,27,37] has no component group
#
handle26char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[27];
    #o:=orbs;
    info:=infos[27];
    #info:=infos;

    rep:=Representative(o);

    #pr[20]=[1,1,1,1,0,0,0]
    #pr[21]=[1,0,1,1,1,0,0]
    tmp1:=ApplyRootsReflections(rep,[20,21]);
    uu:=Unipotent(chevalleyAdj(rep),[[3,-1],[4,-1],[5,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[1,4],[-1,-1,-1,-1]);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[20,21]);
    uu:=Unipotent(chevalleyAdj(cent),[[3,-1],[4,-1],[5,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    tmp2:=ConjugateByTori(tmp2,[1,4],[-1,-1,-1,-1]);

    return [tmp1,tmp2];
end;

##
## cls 26: A_3A_2 [3,4,2,6,7] (not finished - check it)
##
#handle26char3:=function(orbs,infos)
#    local tmp1,tmp2,o,info,rep,cent,uu;
#    o:=Classes(orbs)[26];
#    info:=infos[26];
#
#    rep:=Representative(o);
#
#    #pr[23]=[0,1,0,1,1,1,0]
#    #pr[24]=[0,0,1,1,1,1,0]
#    #pr[45]=[0,1,1,2,2,1,1]
#    tmp1:=ApplyRootsReflections(rep,[23,24,45]);
#    uu:=Unipotent(chevalleyAdj(rep),[[2,-1],[3,1],[6,-1]],Ordering(rep));
#    tmp1:=Conj(tmp1,uu);
#    tmp1:=ConjugateByTori(tmp1,[4],[-1]);
#
#    cent:=FromPositiveBorel(o,info[2]);
#    tmp2:=ApplyRootsReflections(cent,[23,24,45]);
#    uu:=Unipotent(chevalleyAdj(cent),[[6,-1]],Ordering(cent));
#    tmp2:=Conj(tmp2,uu);
#    tmp2:=ConjugateByTori(tmp2,[4],[-1]);
#
#    return [tmp1,tmp2];
#end;

#
# cls 25: A_4 [1,2,3,4]
#
handle25char3:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[25];
    info:=infos[25];

    rep:=Representative(o);

    #pr[29]=[0,1,1,1,1,1,0]
    #pr[27]=[1,0,1,1,1,1,0]
    #pr[51]=[1,1,2,2,2,1,1]
    #pr[61]=[1,2,2,4,3,2,1]
    tmp1:=ApplyRootsReflections(rep,[29,27,51,61]);
    uu:=Unipotent(chevalleyAdj(rep),[[2,-1],[4,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[1,2,3,4],[-1,-1,-1,-1]);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[29,27,51,61]);
    uu:=Unipotent(chevalleyAdj(cent),[[2,-1],[4,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    tmp2:=ConjugateByTori(tmp2,[1,2,3,4],[-1,-1,-1,-1]);

    return [tmp1,tmp2];
end;

#
# cls 20: D_5(a_1) (not finished)? cred ca e gata
#
handle20char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[20];
    info:=infos[20];

    rep:=Representative(o);

    #pr[59]=[1,1,2,3,3,2,1]
    #pr[58]=[1,2,2,3,2,2,1]
    tmp1:=ApplyRootsReflections(rep,[59,58]);
    uu:=Unipotent(chevalleyAdj(rep),[[4,-1],[11,-1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[1,2,4,5],[-1,-1,-1,-1]);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[59,58]);
    uu:=Unipotent(chevalleyAdj(cent),[[4,-1],[11,-1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    tmp2:=ConjugateByTori(tmp2,[1,2,4,5],[-1,-1,-1,-1]);

    return [tmp1,tmp2];
end;

#
# cls 21: A_4A_1
#
handle21char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[21];
    info:=infos[21];

    rep:=Representative(o);

    #pr[36]=[0,1,1,1,1,1,1]
    #pr[34]=[1,0,1,1,1,1,1]
    #pr[46]=[1,1,2,2,2,1,0]
    #pr[61]=[1,2,2,4,3,2,1]
    tmp1:=ApplyRootsReflections(rep,[36,34,46,61]);
    uu:=Unipotent(chevalleyAdj(rep),[[2,-1],[4,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[1,2,3,4],[-1,-1,-1,-1]);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[36,34,46,61]);
    uu:=Unipotent(chevalleyAdj(cent),[[2,-1],[4,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);
    tmp2:=ConjugateByTori(tmp2,[1,2,3,4],[-1,-1,-1,-1]);

    return [tmp1,tmp2];
end;

#
# cls 6: E_6(a_1) (not finished)
#
handle6char3:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[6];
    info:=infos[6];

    #pr[47]=[1,1,2,2,1,1,1]
    #pr[48]=[1,1,1,2,2,1,1]
    #pr[49]=[0,1,1,2,2,2,1]

    rep:=Representative(o);
    rep:=Unipotent(alg,coefficients(rep),[1..Length(pr)]);#ordered rep
    tmp1:=ApplyRootsReflections(rep,[47,48,49]);
    tmp1:=ConjNegUa(tmp1,4,1);
    uu:=Unipotent(chevalleyAdj(rep),[[1,1],[8,1],[6,-1]],Ordering(rep));# this works for orderd rep
    tmp1:=Conj(tmp1,uu);
    tmp1:=ConjugateByTori(tmp1,[3,7],[-1,-1]);
    tmp1:=Unipotent(chevalleyAdj(rep),coefficients(tmp1),Ordering(Representative(o)));

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[47,48,49]);
    tmp2:=ConjNegUa(tmp2,4,1);
    #tmp2:=ConjugateByTorus(cent,4,-1);# h_1 --> h_4

    return [tmp1,tmp2];
end;

#
# cls 14: E_6(a_3) for ([1,8,9,11,14,19] in E6) [1,9,10,12,16,22] in E7
#
handle14char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[14];
    info:=infos[14];

    rep:=Representative(o);
    tmp1:=ConjNegUa(rep,3,1);
    tmp1:=ConjNegUa(tmp1,2,1);
    uu:=Unipotent(chevalleyAdj(rep),[[5,1],[6,1],[8,1],[9,1],[15,1],[16,1],[22,1],[23,1],[29,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ConjNegUa(cent,3,1);
    tmp2:=ConjNegUa(tmp2,2,1);
    uu:=Unipotent(chevalleyAdj(cent),[[5,1],[6,1],[8,1],[9,1],[15,1],[16,1],[22,1],[23,1],[29,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;

#
# cls 4: E_7(a_3) [1,7,9,10,12,16,22] in E7
#
handle4char2:=function(orbs,infos)
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=Classes(orbs)[4];
    info:=infos[4];

    rep:=Representative(o);
    tmp1:=ConjNegUa(rep,3,1);
    tmp1:=ConjNegUa(tmp1,2,1);
    uu:=Unipotent(chevalleyAdj(rep),[[5,1],[6,1],[8,1],[9,1],[15,1],[16,1],[22,1],[23,1],[29,1]],Ordering(rep));
    tmp1:=Conj(tmp1,uu);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ConjNegUa(cent,3,1);
    tmp2:=ConjNegUa(tmp2,2,1);
    uu:=Unipotent(chevalleyAdj(cent),[[5,1],[6,1],[8,1],[9,1],[15,1],[16,1],[22,1],[23,1],[29,1]],Ordering(cent));
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;


local reps,tori,labels,I,II,ords;

reps:=[[[1,1],[2,1],[3,1],[4,1],[5,1],[6,1],[7,1],[8,1]],              # E_8
#       [[1,1],[2,1],[10,1],[11,1],[5,1],[6,1],[7,1],[8,1]],            # E_8(a_1)
       [[1,1],[2,1],[10,1],[11,1],[5,1],[6,1],[7,1],[8,1]],            # E_8(a_1)
#       [[1,1],[2,1],[3,1],[10,1],[12,1],[13,1],[14,1],[8,1]],          # E_8(a_2)
       [[1,1],[2,1],[3,1],[10,1],[12,1],[13,1],[14,1],[8,1]],          # E_8(a_2)
#       [[8,1],[18,1],[7,1],[6,1],[19,1],[1,1],[17,1],[4,1]],           # E_8(a_3)
       [[7,1],[8,1],[9,1],[10,1],[11,1],[12,1],[13,1],[19,1]],         # E_8(a_3)
#       [[18,1],[6,1],[19,1],[1,1],[17,1],[21,1],[4,1],[8,1]],          # E_8(a_4)
       [[9,1],[10,1],[11,1],[12,1],[13,1],[14,1],[15,1],[19,1]],       # E_8(a_4)
       [[1,1],[2,1],[3,1],[4,1],[5,1],[6,1],[7,1]],                    # E_7
#       [[8,1],[7,1],[14,1],[26,1],[19,1],[1,1],[17,1],[4,1]],          # E_8(b_4)
       [[7,1],[8,1],[9,1],[17,1],[18,1],[19,1],[20,1],[21,1]],         # E_8(b_4)
#       [[18,1],[22,1],[19,1],[1,1],[17,1],[20,1],[21,1],[4,1],[6,1]],  # E_8(a_5)
       [[9,1],[14,1],[15,1],[17,1],[18,1],[19,1],[20,1],[26,1]],       # E_8(a_5)
#       [[1,1],[2,1],[11,1],[10,1],[5,1],[6,1],[7,1]],                  # E_7(a_1)
       [[1,1],[3,1],[10,1],[11,1],[5,1],[6,1],[7,1]],                  # E_7(a_1)
#       [[18,1],[8,1],[31,1],[7,1],[33,1],[4,1],[19,1],[23,1]],         # E_8(b_5)
       [[8,1],[16,1],[17,1],[18,1],[20,1],[21,1],[24,1],[33,1]],       # E_8(b_5)
       [[8,1],[7,1],[6,1],[5,1],[4,1],[2,1],[3,1]],                    # D_7
#       [[18,1],[31,1],[7,1],[33,1],[4,1],[29,1],[23,1],[19,1]],        # E_8(a_6)
       [[4,1],[10,1],[12,1],[14,1],[15,1],[26,1],[27,1],[30,1]],       # E_8(a_6)
#       [[1,1],[2,1],[3,1],[10,1],[12,1],[13,1],[14,1]],                # E_7(a_2)
       [[1,1],[2,1],[3,1],[10,1],[12,1],[13,1],[14,1]],                # E_7(a_2)
       [[1,1],[2,1],[3,1],[4,1],[5,1],[6,1],[8,1]],                    # E_6A_1

#      [[2,1],[3,1],[10,1],[12,1],[6,1],[7,1],[8,1],[83,1]],            # (D_7(a_1))^{(2)}    char 2
#       [[8,1],[7,1],[6,1],[5,1],[10,1],[11,-1],[2,1],[3,1]],            # D_7(a_1)
       [[2,1],[3,1],[10,1],[12,1],[6,1],[7,1],[8,1]],                   # D_7(a_1)
#       [[31,1],[33,1],[18,1],[4,1],[39,-1],[41,1],[8,1],[19,1],[23,1]], # E_8(b_6)
       [[15,1],[22,1],[23,1],[24,1],[25,1],[26,1],[27,1],[28,1]],       # E_8(b_6)
#       [[17,1],[1,1],[19,1],[6,1],[7,1],[18,1],[4,1]],                  # E_7(a_3)
       [[1,1],[4,1],[7,1],[12,1],[13,1],[17,1],[19,1]],                 # E_7(a_3)
#       [[3,1],[5,1],[1,1],[6,1],[11,1],[12,1],[2,1],[8,1]],             # E_6(a_1)A_1
       [[1,1],[2,1],[11,1],[12,1],[5,1],[6,1],[8,1]],                   # E_6(a_1)+A_1
#       [[1,1],[3,1],[4,1],[5,1],[6,1],[7,1],[8,1],[67,1]],             # (A_7)^{(3)}     char 3
       [[1,1],[3,1],[4,1],[5,1],[6,1],[7,1],[8,1]],                     # A_7
#       [[8,1],[7,1],[13,1],[10,1],[11,-1],[12,1],[2,1],[3,1]],          # D_7(a_2)
       [[2,1],[3,1],[10,1],[12,1],[13,1],[14,1],[8,1]],                 # D_7(a_2)
       [[1,1],[2,1],[3,1],[4,1],[5,1],[6,1]],                           # E_6
       [[7,1],[6,1],[5,1],[4,1],[2,1],[3,1]],                           # D_6
#       [[1,1],[2,1],[3,1],[4,1],[5,1],[7,1],[8,1],[51,1]],             # (D_5+A_2)^{(2)}     char 2
       [[1,1],[3,1],[4,1],[2,1],[5,1],[7,1],[8,1]],                     # D_5A_2
#       [[3,1],[5,1],[1,1],[6,1],[11,1],[12,1],[2,1]],                   # E_6(a_1)
       [[1,1],[2,1],[11,1],[12,1],[5,1],[6,1]],                         # E_6(a_1)
#       [[17,1],[1,1],[19,1],[14,1],[26,1],[7,1],[18,1],[4,1]],          # E_7(a_4)
       [[1,1],[4,1],[7,1],[11,1],[25,1],[26,1],[27,1]],                 # E_7(a_4)
       [[2,1],[4,1],[5,1],[6,1],[7,1],[8,1],[1,1]],                     # A_6A_1
#       [[7,1],[6,1],[5,1],[10,1],[11,-1],[2,1],[3,1]],                  # D_6(a_1)
       [[2,1],[3,1],[10,1],[12,1],[6,1],[7,1]],                         # D_6(a_1)
       [[2,1],[4,1],[5,1],[6,1],[7,1],[8,1]],                           # A_6
#       [[47,1],[49,1],[5,1],[45,1],[44,1],[42,1],[27,1],[46,1]],        # E_8(a_7)
       [[30,1],[33,1],[35,1],[36,1],[40,1],[45,1],[53,1],[58,1]],       # E_8(a_7)

       [[1,1],[3,1],[4,1],[2,1],[5,1],[7,1]],                # D_5A_1
#       [[24,1],[26,1],[7,1],[27,1],[23,1],[4,1],[25,1]],     # E_7(a_5)
       [[4,1],[11,1],[14,1],[16,1],[24,1],[25,1],[26,1]],    # E_7(a_5)
#       [[17,1],[1,1],[19,1],[6,1],[18,1],[4,1],[8,1]],       # E_6(a_3)A_1
       [[4,1],[6,1],[9,1],[11,1],[18,1],[19,1],[8,1]],       # E_6(a_3)+A_1
#       [[7,1],[13,1],[10,1],[11,-1],[12,1],[2,1],[3,1]],     # D_6(a_2)
       [[2,1],[3,1],[10,1],[12,1],[13,1],[14,1]],            # D_6(a_2)
#       [[1,1],[3,1],[10,1],[12,1],[2,1],[5,1],[7,1],[8,1]],  # D_5(a_1)A_2
       [[1,1],[10,1],[11,1],[2,1],[5,1],[7,1],[8,1]],        # D_5(a_1)+A_2
       [[2,1],[4,1],[5,1],[6,1],[7,1],[1,1]],                # A_5A_1
       [[1,1],[3,1],[4,1],[2,1],[6,1],[7,1],[8,1]],          # A_4A_3
       [[1,1],[3,1],[4,1],[2,1],[5,1]],                      # D_5
#       [[17,1],[1,1],[19,1],[6,1],[18,1],[4,1]],             # E_6(a_3)
       [[4,1],[6,1],[9,1],[11,1],[18,1],[19,1]],             # E_6(a_3)
#       [[2,1],[3,1],[4,1],[5,1],[7,1],[8,1],[63,1]],        # (D_4+A_2)^{(2)}     char 2
       [[3,1],[4,1],[2,1],[5,1],[7,1],[8,1]],                # D_4A_2
       [[5,1],[6,1],[7,1],[8,1],[1,1],[3,1],[2,1]],          # A_4A_2A_1
#       [[1,1],[3,1],[10,1],[12,1],[2,1],[5,1],[7,1]],        # D_5(a_1)A_1
       [[1,1],[10,1],[11,1],[2,1],[5,1],[7,1]],              # D_5(a_1)+A_1
       [[1,1],[3,1],[4,1],[5,1],[6,1]],                      # A_5
       [[1,1],[3,1],[4,1],[2,1],[6,1],[7,1]],                # A_4A_2
       [[1,1],[3,1],[4,1],[2,1],[6,1],[8,1]],                # A_4A_1^2
#       [[1,1],[3,1],[10,1],[12,1],[2,1],[5,1]],              # D_5(a_1)
       [[1,1],[10,1],[11,1],[2,1],[5,1]],                    # D_5(a_1)

       [[1,1],[3,1],[4,1],[6,1],[7,1],[8,1]],          # A_3^2
       [[1,1],[3,1],[4,1],[2,1],[6,1]],                # A_4A_1
#       [[3,1],[10,1],[12,1],[2,1],[5,1],[7,1],[8,1]],  # D_4(a_1)A_2
       [[2,1],[3,1],[10,1],[12,1],[7,1],[8,1]],        # D_4(a_1)+A_2
       [[3,1],[4,1],[2,1],[5,1],[7,1]],                # D_4A_1
       [[5,1],[6,1],[7,1],[1,1],[3,1],[2,1]],          # A_3A_2A_1
       [[1,1],[3,1],[4,1],[2,1]],                      # A_4
#       [[1,1],[3,1],[4,1],[6,1],[7,1],[44,1]],        # (A_3+A_2)^{(2)}     char 2
       [[3,1],[4,1],[2,1],[6,1],[7,1]],                # A_3A_2
#       [[3,1],[10,1],[12,1],[2,1],[5,1],[7,1]],        # D_4(a_1)A_1
       [[2,1],[3,1],[10,1],[12,1],[7,1]],              # D_4(a_1)+A_1
       [[5,1],[6,1],[7,1],[2,1],[3,1]],                # A_3A_1^2
       [[1,1],[3,1],[5,1],[6,1],[2,1],[8,1]],          # A_2^2A_1^2
       [[3,1],[4,1],[2,1],[5,1]],                      # D_4
#       [[3,1],[10,1],[12,1],[2,1],[5,1]],              # D_4(a_1)
       [[2,1],[3,1],[10,1],[12,1]],                    # D_4(a_1)
       [[1,1],[3,1],[4,1],[6,1]],                      # A_3A_1
       [[1,1],[3,1],[5,1],[6,1],[2,1]],                # A_2^2A_1
       [[1,1],[3,1],[5,1],[6,1]],                      # A_2^2
       [[1,1],[3,1],[2,1],[5,1],[7,1]],                # A_2A_1^3
       [[1,1],[3,1],[4,1]],                            # A_3
       [[2,1],[4,1],[1,1],[6,1]],                      # A_2A_1^2
       [[1,1],[3,1],[2,1]],                            # A_2A_1
       [[2,1],[3,1],[5,1],[7,1]],                      # A_1^4
       [[1,1],[3,1]],                                  # A_2
       [[1,1],[4,1],[6,1]],                            # A_1^3
       [[1,1],[6,1]],                                  # A_1^2
       [[1,1]]                                         # A_1
       ];

I:=[[],
    [4],           #??? E_8(a_1)
    [4,6],         #??? E_8(a_2)
    [2,3,5],       # E_8(a_3)
    [2,3,5,7],     # E_8(a_4)
    [],            # E_7
    [2,3,5,6],     # E_8(b_4)
    [2,3,5,6,8],   # E_8(a_5)
    [4],           # E_7(a_1)
    [1,2,3,5,6],   # E_8(b_5)
    [],            # D_7
    [1,2,3,5,6,8], # E_8(a_6)
    [4,6],         #??? E_7(a_2)
    [],            # E_6A_1

#    [4],             # (D_7(a_1))^{(2)}     char 2
    [4],             # D_7(a_1)
    [1,2,3,5,6,7],   # E_8(b_6)
    [2,3,5],         # E_7(a_3)
    [4],             # E_6(a_1)A_1
#    [],              # (A_7)^{(3)}     char 3
    [],              # A_7
    [4,5],           #??? D_7(a_2)
    [],              # E_6
    [],              # D_6
#    [],              # (D_5+A_2)^{(2)}     char 2
    [],              # D_5A_2
    [4],             # E_6(a_1)
    [2,3,5,6],       # E_7(a_4)
    [],              # A_6A_1
    [4],             # D_6(a_1)
    [],              # A_6
    [1,2,3,4,6,7,8], # E_8(a_7)

    [],          # D_5A_1
    [1,2,3,5,6], # E_7(a_5)
    [2,3,5],     # E_6(a_3)A_1
    [4,5],       #??? D_6(a_2)
    [4],         # D_5(a_1)A_2
    [],          # A_5A_1
    [],          # A_4A_3
    [],          # D_5
    [2,3,5],     # E_6(a_3)
#    [],          # (D_4+A_2)^{(2)}     char 2
    [],          # D_4A_2
    [],          # A_4A_2A_1
    [4],         # D_5(a_1)A_1
    [],          # A_5
    [],          # A_4A_2
    [],          # A_4A_1
    [4],         # D_5(a_1) check this also

    [],  # A_3^2
    [],  # A_4A_1
    [4], # D_4(a_1)A_2
    [],  # D_4A_1
    [],  # A_3A_2A_1
    [],  # A_4
#    [],  # (A_3+A_2)^{(2)}     char 2
    [],  # A_3A_2
    [4], # D_4(a_1)A_1
    [],  # A_3A_1^2
    [],  # A_2^2A_1^2
    [],  # D_4
    [4], # D_4(a_1)
    [],  # A_3A_1
    [],  # A_2^2A_1
    [],  # A_2^2
    [],  # A_2A_1^3
    [],  # A_3
    [],  # A_2A_1^2
    [],  # A_2A_1
    [],  # A_1^4
    [],  # A_2
    [],  # A_1^3
    [],  # A_1^2
    []   # A_1
    ];

#
# The parabolic where the representative is distinguished
#
II:=[[1,2,3,4,5,6,7,8], # E_8
     [1,2,3,4,5,6,7,8], # E_8(a_1)
     [1,2,3,4,5,6,7,8], # E_8(a_2)
     [1,2,3,4,5,6,7,8], # E_8(a_3)
     [1,2,3,4,5,6,7,8], # E_8(a_4)
     [1,2,3,4,5,6,7],   # E_7
     [1,2,3,4,5,6,7,8], # E_8(b_4)
     [1,2,3,4,5,6,7,8], # E_8(a_5)
     [1,2,3,4,5,6,7],   # E_7(a_1)
     [1,2,3,4,5,6,7,8], # E_8(b_5)
     [2,3,4,5,6,7,8],   # D_7
     [1,2,3,4,5,6,7,8], # E_8(a_6)
     [1,2,3,4,5,6,7],   # E_7(a_2)
     [1,2,3,4,5,6,8],   # E_6A_1

#     [2,3,4,5,6,7,8],   # D_7(a_1)^{(2)}     char 2
     [2,3,4,5,6,7,8],   # D_7(a_1)
     [1,2,3,4,5,6,7,8], # E_8(b_6)
     [1,2,3,4,5,6,7],   # E_7(a_3)
     [1,2,3,4,5,6,8],   # E_6(a_1)A_1
#     [1,3,4,5,6,7,8],   # A_7^{(3)}     char 3
     [1,3,4,5,6,7,8],   # A_7
     [2,3,4,5,6,7,8],   # D_7(a_2)
     [1,2,3,4,5,6],     # E_6
     [2,3,4,5,6,7],     # D_6
#     [1,2,3,4,5,7,8],   # D_5A_2^{(2)}     char 2
     [1,2,3,4,5,7,8],   # D_5A_2
     [1,2,3,4,5,6],     # E_6(a_1)
     [1,2,3,4,5,6,7],   # E_7(a_4)
     [1,2,4,5,6,7,8],   # A_6A_1
     [2,3,4,5,6,7],     # D_6(a_1)
     [2,4,5,6,7,8],     # A_6
     [1,2,3,4,5,6,7,8], # E_8(a_7)

     [1,2,3,4,5,7],   # D_5A_1
     [1,2,3,4,5,6,7], # E_7(a_5)
     [1,2,3,4,5,6,8], # E_6(a_3)A_1
     [2,3,4,5,6,7],   # D_6(a_2)
     [1,2,3,4,5,7,8], # D_5(a_1)A_2
     [1,2,4,5,6,7],   # A_5A_1
     [1,2,3,4,6,7,8], # A_4A_3
     [1,2,3,4,5],     # D_5
     [1,2,3,4,5,6],   # E_6(a_3)
#     [2,3,4,5,7,8],   # D_4A_2^{(2)}     char 2
     [2,3,4,5,7,8],   # D_4A_2
     [1,2,3,5,6,7,8], # A_4A_2A_1
     [1,2,3,4,5,7],   # D_5(a_1)A_1
     [1,3,4,5,6],     # A_5
     [1,2,3,4,6,7],   # A_4A_2
     [1,2,3,4,6,8],   # A_4A_1^2
     [1,2,3,4,5],     # D_5(a_1)

     [1,3,4,6,7,8], # A_3^2
     [1,2,3,4,6],   # A_4A_1
     [2,3,4,5,7,8], # D_4(a_1)A_2
     [2,3,4,5,7],   # D_4A_1
     [1,2,3,5,6,7], # A_3A_2A_1
     [1,2,3,4],     # A_4
#     [2,3,4,6,7],   # A_3A_2^{(2)}     char 2
     [2,3,4,6,7],   # A_3A_2
     [2,3,4,5,7],   # D_4(a_1)A_1
     [2,3,5,6,7],   # A_3A_1^2
     [1,2,3,5,6,8], # A_2^2A_1^2
     [2,3,4,5],     # D_4
     [2,3,4,5],     # D_4(a_1)
     [1,3,4,6],     # A_3A_1
     [1,2,3,5,6],   # A_2^2A_1
     [1,3,5,6],     # A_2^2
     [1,2,3,5,7],   # A_2A_1^3
     [1,3,4],       # A_3
     [1,2,4,6],     # A_2A_1^2
     [1,2,3],       # A_2A_1
     [2,3,5,7],     # A_1^2
     [1,3],         # A_2
     [1,4,6],       # A_1^3
     [1,6],         # A_1^2
     [1]            # A_1
     ];

labels:=["E_8",
         "E_8(a_1)",
         "E_8(a_2)",
         "E_8(a_3)",
         "E_8(a_4)",
         "E_7",
         "E_8(b_4)",
         "E_8(a_5)",
         "E_7(a_1)",
         "E_8(b_5)",
         "D_7",
         "E_8(a_6)",
         "E_7(a_2)",
         "E_6A_1",

#         "D_7(a_1)^{(2)}",
         "D_7(a_1)",
         "E_8(b_6)",
         "E_7(a_3)",
         "E_6(a_1)A_1",
#         "A_7^{(3)}",
         "A_7",
         "D_7(a_2)",
         "E_6",
         "D_6",
#         "D_5A_2^{(2)}",
         "D_5A_2",
         "E_6(a_1)",
         "E_7(a_4)",
         "A_6A_1",
         "D_6(a_1)",
         "A_6",
         "E_8(a_7)",

         "D_5A_1",
         "E_7(a_5)",
         "E_6(a_3)A_1",
         "D_6(a_2)",
         "D_5(a_1)A_2",
         "A_5A_2",
         "A_4A_3",
         "D_5",
         "E_6(a_3)",
#         "D_4A_2^{(2)}",
         "D_4A_2",
         "A_4A_2A_1",
         "D_5(a_1)A_1",
         "A_5",
         "A_4A_2",
         "A_4A_1^2",
         "D_5(a_1)",

         "A_3^2",
         "A_4A_1",
         "D_4(a_1)A_2",
         "D_4A_1",
         "A_3A_2A_1",
         "A_4",
#         "A_3A_2^{(2)}",
         "A_3A_2",
         "D_4(a_1)A_1",
         "A_3A_1^2",
         "A_2^2A_1^2",
         "D_4",
         "D_4(a_1)",
         "A_3A_1",
         "A_2^2A_1",
         "A_2^2",
         "A_2A_1^3",
         "A_3",
         "A_2A_1^2",
         "A_2A_1",
         "A_1^4",
         "A_2",
         "A_1^3",
         "A_1^2",
         "A_1"
         ];

tori:=[
    [2,2,2,2,2,2,2,2],   # E_8
    [2,2,2,0,2,2,2,2],   # E_8(a_1)
    [2,2,2,0,2,0,2,2],   # E_8(a_2)
    [2,0,0,2,0,2,2,2],   # E_8(a_3)
    [2,0,0,2,0,2,0,2],   # E_8(a_4)
    [2,2,2,2,2,2,2,-27], # E_7
    [2,0,0,2,0,0,2,2],   # E_8(b_4)
    [2,0,0,2,0,0,2,0],   # E_8(a_5)
    [2,2,2,0,2,2,2,-21], # E_7(a_1)
    [0,0,0,2,0,0,2,2],   # E_8(b_5)
    [-21,2,2,2,2,2,2,2], # D_7
    [0,0,0,2,0,0,2,0],   # E_8(a_6)
    [2,2,2,0,2,0,2,-17], # E_7(a_2)
    [2,2,2,2,2,2,-17,2], # E_6A_1

#    [-16,2,0,2,2,2,2,2],   # D_7(a_1)^{(2)}     char 2
    [-16,2,0,2,2,2,2,2],   # D_7(a_1)
    [0,0,0,2,0,0,0,2],     # E_8(b_6)
    [2,0,0,2,0,2,2,-15],   # E_7(a_3)
    [2,2,2,0,2,2,-13,2],   # E_6(a_1)A_1
#    [2,-15,2,2,2,2,2,2],   # A_7^{(3)}     char 4
    [2,-15,2,2,2,2,2,2],   # A_7
    [-13,2,2,0,2,0,2,2],   # D_7(a_2)
    [2,2,2,2,2,2,-16,0],   # E_6
    [-15,2,2,2,2,2,2,-10], # D_6
#    [2,2,2,2,2,-12,2,2],   # D_5A_2^{(2)}     char 2
    [2,2,2,2,2,-12,2,2],   # D_5A_2
    [2,2,2,0,2,2,-12,0],   # E_6(a_1)
    [2,0,0,2,0,0,2,-11],   # E_7(a_4)
    [2,2,-11,2,2,2,2,2],   # A_6(a_1)
    [-11,2,2,0,2,2,2,-8],  # D_6(a_1)
    [0,2,-10,2,2,2,2,2],   # A_6
    [0,0,0,0,2,0,0,0],     # E_8(a_7)

    [2,2,2,2,2,-11,2,-1], # D_5A_1
    [0,0,0,2,0,0,2,-9],   # E_7(a_5)
    [2,0,0,2,0,2,-9,2],   # E_6(a_3)A_1
    [-9,2,2,0,2,0,2,-6],  # D_6(a_2)
    [2,2,2,0,2,-9,2,2],   # D_5(a_1)A_2
    [2,2,-9,2,2,2,2,-5],  # A_5A_1
    [2,2,2,2,-9,2,2,2],   # A_4A_3
    [2,2,2,2,2,-10,0,0],  # D_5
    [2,0,0,2,0,2,-8,0],   # E_6(a_3)
#    [-6,2,2,2,2,-8,2,2],  # D_4A_2^{(2)}     char 2
    [-6,2,2,2,2,-8,2,2],  # D_4A_2
    [2,2,2,-7,2,2,2,2],   # A_4A_2A_1
    [2,2,2,0,2,-8,2,-1],  # D_5(a_1)A_1
    [2,-9,2,2,2,2,-5,0],  # A_5
    [2,2,2,2,-8,2,2,-2],  # A_4A_2
    [2,2,2,2,-7,2,-2,2],  # A_4A_1^2
    [2,2,2,0,2,7,0,0],    # D_5(a_1)

    [2,-3,2,2,-6,2,2,2],   # A_3^2
    [2,2,2,2,-7,2,-1,0],   # A_4A_1
    [-4,2,2,0,2,-6,2,2],   # D_4(a_1)A_2
    [-6,2,2,2,2,-7,2,-1],  # D_4A_1
    [2,2,2,-6,2,2,2,-3],   # A_3A_2A_1
    [2,2,2,2,-6,0,0,0],    # A_4
#    [-3,2,2,2,-6,2,2,-2],  # A_3A_2^{(2)}     char 2
    [-3,2,2,2,-6,2,2,-2],  # A_3A_2
    [-4,2,2,0,2,-5,2,-1],  # D_4(a_1)A_1
    [-1,2,2,-5,2,2,2,-3],  # A_3A_1^2
    [2,2,2,-5,2,2,-3,2],   # A_2^2A_1^2
    [-6,2,2,2,2,-6,0,0],   # D_4
    [-4,2,2,0,2,-4,0,0],   # D_4(a_1)
    [2,-3,2,2,-4,2,-1,0],  # A_3A_1
    [2,2,2,-5,2,2,-2,0],   # A_2^2A_1
    [2,0,2,-4,2,2,-2,0],   # A_2^2
    [2,2,2,-4,2,-2,2,-1],  # A_2A_1^3
    [2,-3,2,2,-3,0,0,0],   # A_3
    [2,2,-3,2,-3,2,-1,0],  # A_2A_1^2
    [2,2,2,-3,0,0,0,0],    # A_2A_1
    [-1,2,2,-3,2,-2,2,-1], # A_1^4
    [2,2,0,-2,0,0,0,0],    # A_2
    [2,-1,-2,2,-2,2,-1,0], # A_1^3
    [2,0,-1,0,-1,2,-1,0],  # A_1^2
    [2,0,-1,0,0,0,0,0]     # A_1
    ];

ords:=[];

return [reps,tori,labels,I,II,ords];


# Copyright (c) 2018-2021, Carnegie Mellon University
# See LICENSE for details

# PR(<dom>, <m>, <n>, <j>) - scatter function for our older PRDFT agorithm,
#                            still used in PRDFT rule for Circulant
Class(PR, FuncClass, rec(
    def := (dom,m,n,j) -> rec(N := approx.FloorRat(m*n/2)+1, n := dom),
    lambda := self >> let(
        m := self.params[2], n := self.params[3], j := self.params[4],
        i := Ind(self.params[1]),
        Lambda(i, cond(leq(i, approx.CeilingRat(m/2)-1), i*n + j, m*n - i*n - j)))
));

#####################
# Matrices
#####################

Class(BotHalf, Sym, rec(
    def := (m, conj) -> let(mc := idiv(m+1, 2), mf := idiv(m, 2),
    DirectSum(I(2*mc), Tensor(I(mf), conj)))
));
Class(TopHalf, Sym, rec(
    def := (m, conj) -> let(mc := idiv(m+1, 2), mf := idiv(m, 2),
    DirectSum(Tensor(I(mc), conj), I(2*mf)))
));

Class(BHD, DiagFunc, rec(
    def := (m, e0, e1) -> Checked(IsPosIntSym(m), rec(size := 2*m)),
    domain := self >> 2*self.params[1],
    range := self >> TReal,
    lambda := self >> let(
	m := self.params[1], mc := idiv(m+1, 2), mf := idiv(m, 2),
	e0:=self.params[2], e1:=self.params[3], i := Ind(2*m),
	Lambda(i, cond(leq(i, 2*mc-1), 1.0, cond(neq(imod(i,2),0), e1, e0))))
));

Class(BHN, DiagFunc, rec(
    def := (m) -> Checked(IsPosIntSym(m), rec(size := m)),
    domain := self >> self.params[1],
    range := self >> TReal,
    lambda := self >> let(
    m := self.params[1], mc := idiv(m+1, 2), i := Ind(m),
    Lambda(i, cond(leq(i, mc-1), 1, -1)))
));

Class(HTwid, Sym, rec(
    def := (N, n, k, a, b, i, hconj_mat) -> let(
    na := Numerator(a),   nb := Numerator(b),
    da := Denominator(a), db := Denominator(b),
    w := Global.Conjugate(E(N*da*db)^k),
    DirectSum(List([0..n-1],
        j -> hconj_mat * Mat(RealMatComplexMat([[w ^ ((i*da+na)*(j*db+nb))]])) )))
));

X13 := (n,blk) -> DirectSum(I(1), Tensor(I(n-1), blk)) ^ (Z(2*n-1,-1)*LIJ(2*n-1));
SymSplit1 := n -> DirectSum(I(n+1), J(n-1)) * DirectSum(I(1), X13(n,F(2)));
SymSplit3 := n -> IJ(2*n,n)   * DirectSum(I(1), X13(n,J(2)*F(2)));

X24 := (n,blk) -> Tensor(blk, I(n)) * IJ(2*n,n);
SymSplit2 := n -> X24(n,F(2));
SymSplit4 := n -> X24(n,J(2)*F(2));

rpdiag := p -> Tensor(pdiag(Int(p/2)+1), I(2));
rperm_ev := p -> Tensor(J((p+1)/2), Diag(1,-1));
rperm_od := p -> Tensor(J((p+1)/2), Diag(-1,1));

# YSV: Many rules below turned out to be invalid for odd stride in H, so I added guards
#
RewriteRules(RulesFuncSimp, rec(
 BHD_Stride_fId := ARule(fCompose,
     [ [BHD, @(1),@(2),@(3)], [fTensor, [H,@,@(4),@,@.cond(e->IsEvenInt(e) and e>1)], [fId, 2]] ],
     e -> [BHD(@(4).val, @(2).val, @(3).val)] ),

 BHD_HH_fId := ARule(fCompose,
     [ [BHD, @(1),@(2),@(3)], [fTensor, [HH,@,@(4),_0,[ListClass, @.cond(e->IsEvenInt(e) and e>1), _1]], [fId, 2]] ],
     e -> [BHD(@(4).val, @(2).val, @(3).val)] ),

 BHD_Stride_H := ARule(fCompose,
     [ [BHD, @(1),@(2),@(3)], [fTensor, [H,@,@(4),@,@.cond(e->IsEvenInt(e) and e>1)], [H,2,2,0,1]] ],
     e -> [BHD(@(4).val, @(2).val, @(3).val)] ),

 BHN_H := ARule(fCompose,
     [ BHN, [@(1,H),@,@,@, @.cond(e->IsEvenInt(e) and e>1)] ],
     e -> [BHN(@(1).val.domain())] ),

# ******* TOGGLE THIS RULE
 # NOTE: IsSymbolic in the condition is dangerous! this rule is not valid if stride is odd.
 #        which is precisely what happens with ASPF_CT1Odd_RFT rule, but this rule is still needed for DCTs
# BHN_HH := ARule(fCompose,
#     [ BHN, [@(1,HH),@,@,_0,[ListClass, @.cond(e->IsSymbolic(e) or (IsEvenInt(e) and e>1)), _1]] ], # vector stride = 1 means stride <> 1
#     e -> [BHN(@(1).val.domain())] ),
##############

# NOTE: breaks for odd sizes -> guard
 BHN_fTensor := ARule(fCompose,
     [ BHN, [@(1, fTensor), @(2,fId,e->IsEvenInt(e.range())), @(3,fBase,e->IsEvenInt(e.range()))] ],
     e -> [BHN(@(1).val.domain())] ),

 BHD_fTensor := ARule(fCompose,
     [ @(0,BHD), [@(1, fTensor), @(2,fId,e->IsEvenInt(e.range())), @(3,fBase,e->IsEvenInt(e.range())), [fId, 2]] ],
     e -> [BHD(@(2).val.params[1], @(0).val.params[2], @(0).val.params[3])] ),

 ## BH o H -> BH
 BH_H := ARule(fCompose, [ [@(1,BH), @N, @r, @n, @b, @s], [H, @n, @m, @bb, @(2).cond(e->e>1 and IsEvenInt(e))] ],
     (e, cx) -> [ BH(@N.val, @r.val, @m.val,
                 memo(cx, "g", @b.val + @s.val * @bb.val), # new base
             @s.val * @(2).val) ]),                     # new stride

 ## H o BH -> BH
 H_BH := ARule(fCompose, [ [H, @N, @n, @b, @(2).cond(e->e>1)], [@(1,BH), @n, @r, @m, @bb, @ss] ],
     (e, cx) -> [ When(@b.val<>0, H(@N.val,@N.val,@b.val, 1), fId(@N.val)),
              BH(@N.val, @r.val * @(2).val, @m.val,
                 memo(cx, "g", @(2).val * @bb.val), # new base
             @(2).val * @ss.val) ]),            # new stride

));