#
# 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

# Cell Unparser
# Author: schellap

@TInt := @.cond(x->x.t=TInt);
@TRealOrInt := @.cond(x->x.t=TInt or x.t=TReal);
@TReal := @.cond(x->x.t=TReal);
@TVect := @.cond(x->ObjId(x.t)=TVect);

Class(altivecUnparser, CMacroUnparserProg, rec(
    # str = format string with $n for arguments ($1 = first argument)
    # list of arguments , arguments are printed using this unparser
    printf := (self, str, args) >> ApplyFunc(PrintEvalF, Concatenation([str],
        List(args, a->(When(IsFunc(a), a, ()->self(a,0,0)))))),

    # -----------------------------
    # ISA independent constructs
    # -----------------------------
    nth :=  (self, o, i, is) >> self.printf("$1[$2]", [o.loc, o.idx]),
    fdiv := (self, o, i, is) >> self.printf("((($3)$1) / $2)", Concatenation(o.args, [self.opts.vector.isa.ctype])),
    fdiv := (self, o, i, is) >> self.printf("($1 / $2)", o.args),
    idiv := (self, o, i, is) >> self.printf("($1 / $2)", o.args),
    imod := (self, o, i, is) >> self.printf("($1 % $2)", o.args),

    # This is the type used for declarations of vector variables
    ctype := (t, isa) -> Cond(
        #t in [TDouble, TVect(TDouble, 1)], "float",
        t = TReal,              isa.ctype, #evals to "float" or "double"
        t = TVect(TReal, 2),    isa.vtype, #evals to "vector double"
        t = TVect(TReal, 4),    isa.vtype,
        t = TVect(TReal, 8),    isa.vtype,
        Error("Unparser doesn't know how to declare type: ", t)
    ),

    Value := (self, o, i, is) >> Cond(
        o.t = TReal,
           let(v := When(IsCyc(o.v), ReComplex(Complex(o.v)), Double(o.v)), When(v<0, Print("(", v, ")"), Print(v))),
        o.t = TInt,
           When(o.v < 0, Print("(", o.v, ")"), Print(o.v)),
        ObjId(o.t)=TVect,
           Print("((", self.ctype(o.t, self.opts.vector.isa), "){", self.infix(o.v, ", "), "})"),
        #NOTE: Adding this for parallel cell, but this shouldn't be needed, right?
       IsArray(o.t),
           Print("{", self.infix(o.v, ", "), "}")
    ),

    vdup         := (self, o, i, is) >> self.printf("vec_splat( ((vector float){$1, $1, $1, $1}), 0)", [o.args[1]]),

    # Declarations
    TVect := (self, t, vars, i, is) >> let(ctype := self.ctype(t, self.opts.vector.isa),
              Print(ctype, " ", self.infix(vars, ", "))),

    TReal := ~.TVect,

    TInt := (self, t, vars, i, is) >> Print("int ", self.infix(vars, ", ")),

    # Arithmetic
    # --------------------------------
    # -- mul --
    mul := (self, o, i, is) >> CondPat(o,
    [mul, @TReal, @TVect],
        Cond(self.opts.vector.isa.v = 2,
            self.printf("vec_madd(((vector $1){$2,$2}), $3, (vector $1)(0))",       [self.ctype(o.args[1].t, self.opts.vector.isa), o.args[1], o.args[2]]),
        self.opts.vector.isa.v = 4,
            self.printf("vec_madd(((vector $1){$2,$2,$2,$2}), $3, (vector $1)(0))", [self.ctype(o.args[1].t, self.opts.vector.isa), o.args[1], o.args[2]]),
        Error("Don't know how to unparse vector arch of length: ", self.opts.vector.isa.v)
        ),
    [mul, @TVect,   @TVect],
        self.printf("vec_madd($1, $2, (vector float)(0) )",              [o.args[1], o.args[2]]),
    [mul, @TRealOrInt, @TRealOrInt],
        self.printf("($1 * $2)", o.args),
    Error("Don't know how to unparse <o>. Unrecognized type combination")),

    # -- add --
    add := (self, o, i, is) >> When(Length(o.args) > 2,
       self(_computeExpType(add(o.args[1], _computeExpType(ApplyFunc(add, Drop(o.args, 1))))), i, is),
       CondPat(o,
           [add, @TVect,   @TVect],
              self.printf("vec_add($1, $2)", [o.args[1], o.args[2]]),
           #[add, @TRealOrInt, @TRealOrInt],
           [add, @, @],
              self.printf("($1 + $2)", o.args),
           Error("Don't know how to unparse <o>. Unrecognized type combination")
       )
    ),

    # -- sub --
    sub := (self, o, i, is) >> When(Length(o.args) > 2,
       self(_computeExpType(sub(o.args[1], _computeExpType(ApplyFunc(sub, Drop(o.args, 1))))), i, is),
       CondPat(o,
           [sub, @TVect,   @TVect],
              self.printf("vec_sub($1, $2)", [o.args[1], o.args[2]]),
           [sub, @TRealOrInt, @TRealOrInt],
              self.printf("($1 + $2)", o.args),
           Error("Don't know how to unparse <o>. Unrecognized type combination")
       )
    ),

    # -- neg --
    neg := (self, o, i, is) >> CondPat(o,
       [neg, @TVect],
           self(o.t.value(Replicate(o.t.size,0)) - o.args[1], i, is),
       self.printf("(-$1)", o.args)
    ),

    vpack := (self, o, i, is) >> Print(self.opts.vector.isa.vconstv, "{", self.infix(o.args, ", "), "}"),

    vunpackhi_4x32f_av := (self, o, i, is) >> self.printf("vec_mergeh($1, $2)", [o.args[1], o.args[2]]),
    vunpacklo_4x32f_av := (self, o, i, is) >> self.printf("vec_mergel($1, $2)", [o.args[1], o.args[2]]),

   vparam_av := (self, o, i, is) >>
      Print("((vector unsigned char){", PrintCS(prep_perm_string(o.p)), "})"),

   vzero_4x32f := (self, o, i, is) >> Print("((", self.opts.vector.isa.vtype ,"){", PrintCS(List([1..self.opts.vector.isa.v], i->0)), "})"),

   # ----------------------------------------------------------------------------------
   # ISA specific : altivec_4x32f
   # ----------------------------------------------------------------------------------
   vperm_4x32f := (self, o, i, is) >>
     self.printf("vec_perm($1, $2, $3)", o.args),

   vuperm_4x32f := (self, o, i, is) >>
     self.printf("vec_perm($1, $1, $2)", o.args),

));


# 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;


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

Declare(ISA_Bridge);

IsISABridge := (obj) -> IsRec(obj) and IsBound(obj._ISABridge) and obj._ISABridge=true;

#F VCvt(<n>, <ISA_Bridge>)
#F data type conversion from one ISA into another.
#F
# OBSOLETE, will be removed
Class(VCvt, TCast, rec(
    abbrevs := [],
    def := (n, brige) -> Perm((), n),

    dmn := self >> [ TArray(self.params[2].isa_from.gt(), self._spl().dims()[2]) ],
    rng := self >> [ TArray(self.params[2].isa_to.gt(),   self._spl().dims()[1]) ],

    toAMat := self >> self._spl().toAMat(),

    _spl := self >> self.params[2].splVCvt(self.params[1]),
));

Class(Cvt, SumsBase, Sym, rec(
    abbrevs := [  (isa_bridge) -> Checked(IsISABridge(isa_bridge), [isa_bridge]) ],
    def := (isa_bridge) -> Perm((), isa_bridge.granularity()),

    n         := self >> self.params[1].granularity(),
    isa_to    := self >> self.params[1].isa_to,
    isa_from  := self >> self.params[1].isa_from,

    dmn := self >> [ TArray(self.isa_from().t.base_t(), self.n()) ],
    rng := self >> [ TArray(self.isa_to().t.base_t(), self.n()) ],

    isPermutation := False,

    toAMat := self >> self.params[1].toAMat(),

    #doNotMeasure := true
));

Class(TCvt, Tagged_tSPL, rec(
    abbrevs := [
        (n, isa_to, isa_from) -> Checked(IsPosIntSym(n), IsISA(isa_to), IsISA(isa_from), [n, isa_to, isa_from, []]),
        (n, isa_to, isa_from, props) -> Checked(IsPosIntSym(n), IsISA(isa_to), IsISA(isa_from), IsList(props), [n, isa_to, isa_from, props]),
    ],

    #def := (n, isa_to, isa_from) -> Perm((), n),

    n         := self >> self.params[1],
    isa_to    := self >> self.params[2],
    isa_from  := self >> self.params[3],
    props     := self >> self.params[4],

    dmn := self >> [ TArray(self.isa_from().t.base_t(), self.n()) ],
    rng := self >> [ TArray(self.isa_to().t.base_t(), self.n()) ],

    toAMat := self >> I(self.n()).toAMat(),

    isReal := self >> true,

    isAuxNonTerminal := true,

    hashAs := self >> ObjId(self)(8*Lcm(self.isa_to().v, self.isa_from().v), self.isa_to(), self.isa_from(), self.props()).takeTA(self),
));

#
# ISA_Bridge: represents conversion from one SIMD_ISA to another.
# Each descendant implements particular conversion case and registered in
# the ISA_Bridge object by [source ISA, destination ISA] pair.
#

# <props> field
# "wraparound" | "saturation"
# "trunc" | "round" | "ceil" | "floor"

Class(ISA_Bridge, rec(
    _ISABridge := true,
    _table     := rec(),

    __call__ := meth(self)
        local inst;
        inst := WithBases(self);
        if not IsBound(self._table.(inst.isa_from.id())) then
            self._table.(inst.isa_from.id()) := rec();
        fi;
        if not IsBound(self._table.(inst.isa_from.id()).(inst.isa_to.id())) then
            self._table.(inst.isa_from.id()).(inst.isa_to.id()) := [];
        fi;
        Add(self._table.(inst.isa_from.id()).(inst.isa_to.id()), inst);
        return inst;
    end,

    add := (self, class) >> class(),

    printAvailableBridges := meth(self)
        local all;
        all := Flat(List(UserRecValues(self._table), r -> UserRecValues(r)));
        Sort(all, (a,b) -> let(a1 := StringPrint(a.isa_from), b1 := StringPrint(b.isa_from), When(a1=b1, StringPrint(a.isa_to)<StringPrint(b.isa_to), a1<b1)));
        Print(PrintPad("From", 27), PrintPad("To", 27), "Props\n", Replicate(60, '-'), "\n");
        DoForAll(all, e -> Print(PrintPad(StringPrint(e.isa_from), 26), " ", PrintPad(StringPrint(e.isa_to), 26), " ", e.props, "\n"));
    end,

    _applicable_cvt := (props, min_range, range_in, bridge) -> let(
        range_from  := bridge.isa_from.t.range(),
        range_to    := bridge.isa_to.t.range(),
        range_mid   := bridge.range(),

        range       := Cond( range_in=false, range_from, range_from * range_in),
        range_req   := Cond( range_in=false, min_range,  min_range  * range_in),
        range_mul   := range_to * range_mid * range_from,

        class_range := Cond( range.min >= range_to.min and range.max <= range_to.max and
                             range.min >= range_mid.min and range.max <= range_mid.max, [], ["saturation", "wraparound"]),
        class_prec  := Cond( range_from.eps = range_to.eps and range_from.eps = range_mid.eps,    [], ["round", "trunc", "ceil", "floor"]),
        bridge_class_range  := Intersection(class_range, props),
        bridge_class_prec   := Intersection(class_prec,  props),

        Intersection(bridge_class_range, bridge.props) = bridge_class_range
        and Intersection(bridge_class_prec, bridge.props) = bridge_class_prec
        and range_mul.min <= range_req.min and range_mul.max >= range_req.max
        and range_mul.eps <= range_req.eps
    ),

    # bridge: returns ISA_Bridge objects list which implement conversion from isa_from to isa_to

    find := (arg) >> let(
        self     := arg[1],
        isa_to   := arg[2],
        isa_from := arg[3],
        props    := arg[4],
        min_range:= Cond( Length(arg)>4, arg[5], isa_to.t.range() * isa_from.t.range() ),
        range_in := Cond( Length(arg)>5, arg[6], isa_from.t.range()),

        Cond( IsBound(self._table.(isa_from.id())) and IsBound(self._table.(isa_from.id()).(isa_to.id())),
            Filtered(self._table.(isa_from.id()).(isa_to.id()),
                e -> self._applicable_cvt(props, min_range, range_in, e)),
            [] )
    ),

    #######################
    # descendants should define the fields listed below.

    # isa_from: source SIMD_ISA
    isa_from := false,
    # isa_to: destination SIMD_ISA
    isa_to   := false,

    # minimal range
    range := (self) >> self.isa_to.t.range() * self.isa_to.t.range(),

    # NOTE: bridge conversion properties, explain
    # For example float-to-int conversion can be implemented with different rounding methods bla bla bla
    props       := [],

    # code(<y>, <x>, <opts>): called from codegen to get code that implements Cvt(...);
    code        := (self, y, x, opts) >> Error("not implemented"),

    # granularity: Cvt object height and width (which are the same).
    granularity := self >> Lcm(self.isa_to.v, self.isa_from.v),

    # toAMat: implements Cvt.toAMat()
    toAMat      := abstract(),

    # toSpl: conversion block formula NOTE: explain
    toSpl       := abstract(),

    ##############################################
    # helpers

    _x := (self, x, offs) >> vtref(self.isa_from.t, x, offs),
    _y := (self, y, offs) >> vtref(self.isa_to.t,   y, offs),

    _mkTL       := meth(self, isa, n, s)
        local t, hentry, sums;
        t := TL(n, s).withTags(isa.getTags());
        hentry := HashLookup(SIMD_ISA_DB.getHash(), t);
        if hentry=false then
            #return Error("SIMD_ISA_DB lookup failed, tried ", t);
            return Prm(L(n, s));
	else
            sums := _SPLRuleTree(hentry[1].ruletree).sums();
            sums := When(IsBound(sums.unroll), sums.unroll(), sums);
            return sums;
	fi;
    end,

));

# ISA_Bridge base class for bridges for which Cvt object is identity
Class(ISA_Bridge_I, ISA_Bridge, rec(
    toAMat      := (self) >> I(self.granularity()).toAMat(),
    toSpl       := (self) >> Cvt(self),
));

Class(ISA_Bridge_VvL, ISA_Bridge, rec(
    toAMat      := (self) >> L(self.granularity(), self.isa_from.v).toAMat(),
    toSpl       := (self) >> Cvt(self)*self._mkTL(self.isa_to, self.granularity(), div(self.granularity(), self.isa_from.v)),
));

Class(ISA_Bridge_VvR, ISA_Bridge, rec(
    toAMat      := (self) >> L(self.granularity(), self.isa_from.v).toAMat(),
    toSpl       := (self) >> self._mkTL(self.isa_to, self.granularity(), div(self.granularity(), self.isa_from.v)) * Cvt(self),
));

# plain conversion from one scalar data type to another,
# assuming isa_to type range includes isa_from type range
Class(ISA_Bridge_tcast, ISA_Bridge_I, rec(
    code  := (self, y, x, opts) >> assign( self._y(y,0), tcast(self.isa_to.t, self._x(x,0)) ),
));

# reinterpretation
Class(ISA_Bridge_wrap, ISA_Bridge_I, rec(
    range := (self) >> self.isa_to.t.range(),
    props := ["wraparound"],
    code  := (self, y, x, opts) >> assign( self._y(y,0), tcast(self.isa_to.t, self._x(x,0)) ),
));

_debug_print_cvt_chains := false;

DebugCVT := function(value)
    _debug_print_cvt_chains := value;
end;

# _cvt_build_chains( <TCvt non terminal> ) returns all data conversion chains available for given
#   TCvt object.
#

Declare(_cvt_build_chains_rec);
_cvt_build_chains_rec := function(chain, isa, min_range, range, props, available_isa)
    local ifrom, fr, r, b, dbg_print;
    ifrom := Last(chain).isa_to;
    if ifrom = isa then
        if _debug_print_cvt_chains then
            Print(GreenStr("CVT: "), ConcatSepList(chain, e -> e.__name__, " -> "), "\n");
        fi;
        return [chain];
    else
        r  := [];
        fr := ConcatList( available_isa, isa -> ISA_Bridge.find( isa, ifrom, props, min_range, range ));
        dbg_print := fr = [] and _debug_print_cvt_chains;
        for b in fr do
            Append(r, _cvt_build_chains_rec(chain :: [b], isa, min_range, b.range()*range, props, RemoveList(available_isa, b.isa_to)));
        od;
    fi;
    if dbg_print then
        Print(RedStr("Dead CVT: "), ConcatSepList(chain, e -> e.__name__, " -> "), "\n");
    fi;
    return r;
end;

_cvt_build_chains := function(t)
    local available_isa, r, range, fr, b, min_range;
    available_isa := Set(t.firstTag().params[1] :: [t.isa_from(), t.isa_to()]);#Set(RemoveList(t.firstTag().params[1], t.isa_from()) :: [t.isa_to()]);
    r         := [];
    range     := StripList(t.getA("r_in", [t.isa_from().t.range()]));
    min_range := t.isa_to().t.range() * t.isa_from().t.range();
    fr        := ConcatList( available_isa, isa -> ISA_Bridge.find( isa, t.isa_from(), t.props(), min_range, range ));
    for b in fr do
        Append(r, _cvt_build_chains_rec([b], t.isa_to(), min_range, b.range()*range, t.props(), RemoveList(available_isa, b.isa_to)));
    od;
    return Sort(r);
end;

NewRulesFor( TCvt, rec(
   TCvt_Term := rec(
       forTransposition := false,

       applicable := t -> t.firstTagIs(AMultiVec),
       freedoms   := t -> [ _cvt_build_chains(t) ],
       child      := (t, fr) -> [InfoNt(Reversed(fr[1]))],
       apply      := (self, t, C, nt) >> let(
           # merge adjasent loops when possible
           l := self._merge_loops(List(nt[1].params[1], b -> rec(
                   # NOTE: need ESReduce here to deal with symbolic expressions
                   #        this way can figure out divisibility at least some times
                   its := ESReduce(div(t.n(), b.granularity()), spiral.SpiralDefaults), bridge := b
                ))),

           Compose(List(l, e -> self._to_ISum(e)))
       ),

       _to_ISum := (self, r) >> let(
           ch   := Cond( IsBound(r.bridge), [r.bridge.toSpl()], List(r.children, e -> self._to_ISum(e))),
           cols := Cols(Last(ch)),
           rows := Rows(ch[1]),
           i    := Ind(r.its),
           ISum(i, Compose( [Scat(H(r.its*rows, rows, rows*i, 1))] :: ch :: [Gath(H(r.its*cols, cols, cols*i, 1))]))
       ),

       _merge_loops := meth(self, l)
           local c, e, new_ch, r;
           r := [];
           c := l[1];
           for e in Drop(l, 1) do
               if c.its <> 1 and _divides(c.its, e.its) then
                   new_ch := rec(its := ESReduce(div(e.its, c.its), spiral.SpiralDefaults), bridge := e.bridge);
                   if IsBound(c.bridge) then
                       # it's a single bridge, creating new node and making this one child
                       c := rec(
                           its      := c.its,
                           children := [rec(its := 1, bridge := c.bridge), new_ch]
                       );
                   else
                       Add(c.children, new_ch);
                   fi;
               elif e.its<>1 and _divides(e.its, c.its) then
                   new_ch := rec(its := ESReduce(div(c.its, e.its), spiral.SpiralDefaults));
                   if IsBound(c.bridge) then
                       new_ch.bridge := c.bridge;
                   else
                       new_ch.children := c.children;
                   fi;
                   c := rec(
                       its      := e.its,
                       children := [new_ch, rec(its := 1, bridge := e.bridge)]
                   );
               else
                   Add(r, c);
                   c := e;
               fi;
           od;

           Add(r, c);

           # recurse on children
           return List(r, e -> Cond(IsBound(e.children), CopyFields(e, rec(children := self._merge_loops(e.children))), e));
       end,

   ),
));


# Copyright 2018-2019, Carnegie Mellon University
# See LICENSE for details

Class(TState, VStack, rec(
 dims := self >> [0, FoldL(self._children,(a,b)->When(not IsSymbolic(b.dims()[2]), Max2(a,b.dims()[2]), a), 0) ],
 isCommand := true
));

HCOLSumsGen.TState := (self, o, opts) >> let(
      ch := o.children(),
      ApplyFunc(TState, List([1..Length(ch)], i->OLCompose(
        ScatHUnion(Rows(o), Rows(ch[i]),  Sum(List(ch{[1..i-1]}, child->child.dims()[1])), 1),
	self(ch[i], opts)
      )))
    );

HCOLCodegen.TState := (self, o, y, x, opts) >> List(o.children(), c -> self(c, opts.state, x, opts));


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

#the multiple number of buffers is equal as size
multiple_buffers := function(elements)
                local number, s;

                number := Length(elements);

                s := elements[1];

                return [ var("S", TArray(TArray(TReal, s.t.size), 2)) ];
end;

construct_loop := function(i, low, high, ld, st, val, br, s1, s2, s)
               local new_chain, new_loop, new_high;

               new_chain := chain(
                         SubstVars(Copy(st), rec((s2.id) := nth(s, imod(i,2)))),
                         SubstVars(Copy(ld), rec((i.id) := add(i,val), (s1.id) := nth(s, imod(i,2)))),
                         br);
               new_high := high - val;
               new_loop := swp_loop(i, [low..new_high.v], new_chain);

               return new_loop;
end;

swp_peel := function(fun, opts)
         #peeling instruction from loops
         local l, loops, ld, st, br, i, low, high, prologue, epilogue, new_chain, new_fun, s1, s2, svars, s;

         svars := [];

         loops := Collect(fun, swp_loop);

         new_fun := Copy(fun);

         for l in loops do
             i := l.var;
             low := Minimum(l.range);
             high := Maximum(l.range);

             ld := Collect(l, [loop, @1, @2, dma_load]);
             if Length(ld) = 0 then ld := Collect(l, dma_load)[1]; else ld := ld[1]; fi;
             st := Collect(l, [loop, @1, @2, dma_store]);
             if Length(st) = 0 then st := Collect(l, dma_store)[1]; else st := st[1]; fi;
             br := Collect(l, barrier_cmd)[1];

             s1 := Filtered(ld.free(), i->IsBound(i.t.qualifiers) and i.t.qualifiers=[opts.scratchModifier])[1];
             s2 := Filtered(st.free(), i->IsBound(i.t.qualifiers) and i.t.qualifiers=[opts.scratchModifier])[1];
             Add(svars, s1);
             Add(svars, s2);

             s := multiple_buffers(svars)[1];

             prologue := chain(
                      SubstVars(Copy(ld), rec((i.id) := low, (s1.id) := nth(s, low))),
                      br,
                      SubstVars(Copy(ld), rec((i.id) := low + 1, (s1.id) := nth(s, low + 1))),
                      br);

             epilogue := chain(
                      SubstVars(Copy(st), rec((i.id) := high - 1, (s2.id) := nth(s, bin_and(high - 1, 1)))),
                      br,
                      SubstVars(Copy(st), rec((i.id) := high, (s2.id) := nth(s, bin_and(high, 1)))));

             new_chain := When((low = 0 and high = 1), chain(prologue, epilogue), chain(prologue, construct_loop(i, low, high, ld, st, V(2), br, s1, s2, s), epilogue));
             new_chain := RulesStrengthReduce(new_chain);
             new_fun := SubstBottomUp(new_fun, @(1, swp_loop, e->e.var = i), e->new_chain);
         od;

         return new_fun;
end;

swp_merge_variables := function(fun, opts)
                   local l, loops, i, new_loop, new_fun, s1, s2, svars, s;

                   svars := [];

                   loops := Collect(fun, swp_loop);

                   new_fun := Copy(fun);

                   for l in loops do
                       i := l.var;

                       s1 := Filtered(l.free(), i->IsBound(i.t.qualifiers) and i.t.qualifiers=[opts.scratchModifier])[1];
                       s2 := Filtered(l.free(), i->IsBound(i.t.qualifiers) and i.t.qualifiers=[opts.scratchModifier])[2];
                       Add(svars, s1);
                       Add(svars, s2);

                       s := multiple_buffers(svars)[1];

                       new_loop := SubstVars(Copy(l), rec((s1.id) := nth(s, imod(i, 2)), (s2.id) := nth(s, imod(i, 2))));

                       new_loop := RulesStrengthReduce(new_loop);
                       new_fun := SubstBottomUp(new_fun, @(1, swp_loop, e->e.var = i), e->new_loop);
                   od;

                   return new_fun;
end;

Class(SWPDMACodegen, DefaultCodegen, rec(
    Formula := meth(self, o, y, x, opts)
        local icode, datas, prog, params, sub, initsub, io, t, initcode;
        [x, y] := self.initXY(x, y, opts);

        Add(x.t.qualifiers, opts.memModifier);
        Add(y.t.qualifiers, opts.memModifier);

        o := o.child(1);
        params := Set(Concatenation(Collect(o, param), Filtered(Collect(o, var), IsParallelLoopIndex)));

        datas := Collect(o, FDataOfs);
        [o,t] := UTimedAction(BlockSumsOpts(o, opts)); #PrintLine("BlockSums ", t);
        [icode,t] := UTimedAction(self(o, y, x, opts)); #PrintLine("codegen ", t);
        [icode,t] := UTimedAction(ESReduce(icode, opts)); #PrintLine("ESReduce ", t);
        icode := RemoveAssignAcc(icode);
        Unbind(Compile.times);
        [icode,t] := UTimedAction(BlockUnroll(icode, opts)); #PrintLine("BlockUnroll ", t);
        #PrintLine("---compile--");

        icode := DeclareHidden(icode);
        if IsBound(opts.isFixedPoint) and opts.isFixedPoint then
            icode := FixedPointCode(icode, opts.bits, opts.fracbits);
        fi;

        io := When(x=y, [x], [y, x]);
        sub := Cond(IsBound(opts.subNameDMA), opts.subNameDMA, "sub_dma");
        icode := func(TVoid, sub, Concatenation(io, params), icode);

        return icode;
    end,

    LSKernel := meth(self, o, y, x, opts)
        return barrier_cmd(self.swp_var);
    end,

    DMAGath := meth(self, o, y, x, opts)
        local i, func, rfunc, ix, size;
        Add(self.loadbuffers, y);
        Add(self.membuffers, x);
        i := Ind();

        func := o.func;
        size := 1;
        if ObjId(func) = fTensor and ObjId(Last(func.children())) = fId then
            size := Last(func.children()).domain();
            func := fTensor(Concat(DropLast(func.children(), 1), [fBase(size, 0)]));
        fi;

        func := func.lambda();
        return loop(i, func.domain(), dma_load(y+(i*size), x+func.at(i), size));
    end,

    DMAScat := meth(self, o, y, x, opts)
        local i, func, rfunc, ix, size;

        Add(self.storebuffers, x);
        Add(self.membuffers, y);
        i := Ind();

        func := o.func;
        size := 1;
        if ObjId(func) = fTensor and ObjId(Last(func.children())) = fId then
            size := Last(func.children()).domain();
            func := fTensor(Concat(DropLast(func.children(), 1), [fBase(size, 0)]));
        fi;

        func := func.lambda();
        return loop(i, func.domain(), dma_store(y+func.at(i), x+(i*size), size));
    end,

    DMAFence := (self,o,y,x,opts) >> chain(self(o.child(1), y, x, opts), dma_fence()),

    swp_var := false,
    SWPSum := meth(self, o, y, x, opts)
        return swp_loop(o.var, o.domain, self(o.child(1), y, x, opts));
    end

));

Class(SWPCPUCodegen, DefaultCodegen, rec(
    Formula := meth(self, o, y, x, opts)
        local icode, datas, prog, params, sub, initsub, io, t, initcode;
        [x, y] := self.initXY(x, y, opts);

        o := o.child(1);
        params := Set(Concatenation(Collect(o, param), Filtered(Collect(o, var), IsParallelLoopIndex)));

        datas := Collect(o, FDataOfs);
        [o,t] := UTimedAction(BlockSumsOpts(o, opts)); #PrintLine("BlockSums ", t);
        [icode,t] := UTimedAction(self(o, y, x, opts)); #PrintLine("codegen ", t);
        [icode,t] := UTimedAction(ESReduce(icode, opts)); #PrintLine("ESReduce ", t);
        icode := RemoveAssignAcc(icode);
        Unbind(Compile.times);
        [icode,t] := UTimedAction(BlockUnroll(icode, opts)); #PrintLine("BlockUnroll ", t);
        #PrintLine("---compile--");

        icode := DeclareHidden(icode);
        if IsBound(opts.isFixedPoint) and opts.isFixedPoint then
            icode := FixedPointCode(icode, opts.bits, opts.fracbits);
        fi;

        io := When(x=y, [x], [y, x]);
        Add(params, Filtered(icode.free(), i->not IsBound(i.init)));
        sub := Cond(IsBound(opts.subNameCompute), opts.subNameCompute, "sub_cpu");

        icode := func(TVoid, sub, params, icode);

        return icode;

    end,

    DMAGath := meth(self, o, y, x, opts)
        local dma;
        if not IsBound(x.t.qualifiers) then x.t.qualifiers := []; fi;
        if not IsBound(y.t.qualifiers) then y.t.qualifiers := []; fi;
        Add(x.t.qualifiers, opts.memModifier);
        y.t.qualifiers := [ opts.scratchModifier ];
        Add(self.loadbuffers, y);
        return nop_cmd(self.swp_var);
    end,

    DMAScat := meth(self, o, y, x, opts)
        local dma;
        if not IsBound(x.t.qualifiers) then x.t.qualifiers := []; fi;
        if not IsBound(y.t.qualifiers) then y.t.qualifiers := []; fi;
        x.t.qualifiers := [ opts.scratchModifier ];
        Add(y.t.qualifiers, opts.memModifier);
        Add(self.storebuffers, x);
        return barrier_cmd(self.swp_var);
    end,

    LSKernel := (self, o, y, x, opts) >> self(o.child(1), y, x, opts),

    DMAFence := (self,o,y,x,opts) >> chain(barrier_cmd(self.swp_var), self(o.child(1), y, x, opts)),

    swp_var := false,
    SWPSum := meth(self, o, y, x, opts)
        return swp_loop(o.var, o.domain, self(o.child(1), y, x, opts));
    end
));

Class(SWPBarrierScratchCodegen, DefaultCodegen, rec(
    CPUCodegen := SWPCPUCodegen,
    DMACodegen := SWPDMACodegen,
    MainCodegen := ScratchMainCodegen,

    Formula := meth(self, o, y, x, opts)
        local compute_time, tag, main, initfunc, cpufunc, dmafunc, prog, params, sub, initsub, memvars, scratchvars, v, io, loadvar, storevar, v, substrec, svars, initcode, datas, dvars, dv, counter;

        datas := Collect(o, FDataOfs);
        params := Set(Concatenation(Collect(o, param), Filtered(Collect(o, var), IsParallelLoopIndex)));
        sub := Cond(IsBound(opts.subName), opts.subName, "transform");

        initsub := Cond(IsBound(opts.subName), Concat("init_", opts.subName), "init");
        if IsBound(opts.generateInitFunc) and opts.generateInitFunc then
            initcode := chain(List(datas, x -> SReduce(x.var.init, opts)));
            initfunc := func(TVoid, initsub, params :: Set(Collect(initcode, param)), initcode);
        else
            initfunc := func(TVoid, initsub, params, chain());
        fi;

        dvars := List(datas, x->x.var);
        for dv in dvars do
            dv.t.qualifiers := [opts.romModifier];
        od;

        self.DMACodegen.loadbuffers := Set([]);
        self.DMACodegen.storebuffers := Set([]);
        self.DMACodegen.membuffers := Set([]);
        dmafunc := self.DMACodegen.Formula(o, y, x, opts);
        self.DMACodegen.membuffers := Set(self.DMACodegen.membuffers);
        SubtractSet(self.DMACodegen.membuffers, Set([x, y]));
        for v in self.DMACodegen.membuffers do v.t.qualifiers := [opts.memModifier]; od;

        self.CPUCodegen.loadbuffers := Set([]);
        self.CPUCodegen.storebuffers := Set([]);
        cpufunc := self.CPUCodegen.Formula(o, y, x, opts);

        memvars := Set(Concat(
            Filtered(Flat(List(Collect(cpufunc, decl), i->i.vars)), j->IsBound(j.t.qualifiers) and opts.memModifier in j.t.qualifiers),
            self.DMACodegen.membuffers));
        scratchvars := Set(Filtered(Flat(List(Collect(cpufunc, decl), i->i.vars)), j->IsBound(j.t.qualifiers) and opts.scratchModifier in j.t.qualifiers));

        tag := opts.tags[1];

        loadvar := var.fresh_t("S", TArray(opts.XType.t, Cond(tag.isRegCx, 2 * tag.size, tag.size)));
        loadvar.t.qualifiers := [opts.scratchModifier];
        storevar := var.fresh_t("S", TArray(opts.XType.t, Cond(tag.isRegCx, 2 * tag.size, tag.size)));
        storevar.t.qualifiers := [opts.scratchModifier];

        substrec := rec();
        for v in self.CPUCodegen.loadbuffers do substrec.(v.id) := loadvar; od;
        for v in self.CPUCodegen.storebuffers do substrec.(v.id) := storevar; od;
        for v in self.DMACodegen.loadbuffers do substrec.(v.id) := loadvar; od;
        for v in self.DMACodegen.storebuffers do substrec.(v.id) := storevar; od;
        cpufunc :=  SubstVars(cpufunc, substrec);
        dmafunc :=  SubstVars(dmafunc, substrec);

        cpufunc := SubstTopDown(cpufunc, @(1, decl), e->decl(Filtered(@(1).val.vars, i->not i in Concat(memvars, scratchvars, [loadvar, storevar])), @(1).val.cmd));
        dmafunc := SubstTopDown(dmafunc, @(1, decl), e->decl(Filtered(@(1).val.vars, i->not i in Concat(memvars, scratchvars, [loadvar, storevar])), @(1).val.cmd));

        [cpufunc, dmafunc, svars] := When(opts.swp,[swp_merge_variables(cpufunc, opts), swp_peel(dmafunc, opts), multiple_buffers([loadvar, storevar])],[cpufunc,dmafunc,[loadvar,storevar]]);

        x.t.qualifiers := Set(x.t.qualifiers);
        y.t.qualifiers := Set(y.t.qualifiers);

        compute_time := true;

        counter := var("count",TInt);

        cpufunc := func(cpufunc.ret, cpufunc.id, When(compute_time, Concat(cpufunc.params, [counter]), cpufunc.params), chain(register(), cpufunc.cmd));
	    dmafunc := func(dmafunc.ret, dmafunc.id, When(compute_time, Concat(dmafunc.params, [counter]), dmafunc.params), chain(register(), dmafunc.cmd));
        initfunc := func(initfunc.ret, initfunc.id, initfunc.params, chain(initialization(), initfunc.cmd));
        #PrintLine(svars);
        #main := self.MainCodegen.genMain(opts, sub, dmafunc, cpufunc, [y,x], params);
        prog := program(
            decl(Concat(Set(memvars), svars, dvars), chain(
                initfunc,
                cpufunc,
		        dmafunc
        )));

        prog.dimensions := o.dims();
        return prog;
    end
));


#############################################################################
##
#W  utils.gi                   automgrp package                Yevgen Muntyan
##                                                             Dmytro Savchuk
##
#Y  Copyright (C) 2003 - 2018 Yevgen Muntyan, Dmytro Savchuk
##

#############################################################################
##
##  AG_PermFromTransformation( <tr> )
##
InstallGlobalFunction(AG_PermFromTransformation,
function(tr)
  if IsPerm(tr) then
    return tr;
  elif tr^-1=fail then
    Error(tr, " is not invertible");
  else
    return AsPermutation(tr);
  fi;
end);

#############################################################################
##
##  AG_PrintTransformation( <tr> )
##
InstallGlobalFunction(AG_PrintTransformation,
function(tr)
  local list, i;
  if IsPerm(tr) then
    Print(tr);
  else
    list := ImageListOfTransformation(tr);
    Print("[");
    for i in [1..Length(list)] do
      if i <> 1 then
        Print(",");
      fi;
      Print(list[i]);
    od;
    Print("]");
  fi;
end);

#############################################################################
##
##  AG_TransformationString( <tr> )
##
InstallGlobalFunction(AG_TransformationString,
function(tr)
  local list, i, str;
  if IsPerm(tr) then
    return String(tr);
  else
    list := ImageListOfTransformation(tr);
    str:="[";
    for i in [1..Length(list)] do
      if i <> 1 then
        Append(str, ",");
      fi;
      Append(str, String(list[i]));
    od;
    Append(str, "]");
  fi;
  return str;
end);

InstallGlobalFunction(AG_TrCmp,
function(p1, p2, d)
  local l1, l2, getlist;

  if IsIdenticalObj(p1, p2) then
    return 0;
  fi;

  getlist := function(p)
    local l;
    if IsTransformation(p) then
      return ImageListOfTransformation(p);
    else
      return Permuted([1..d],p);
    fi;
  end;

  l1 := getlist(p1);
  l2 := getlist(p2);

  if l1 = l2 then
    return 0;
  elif l1 < l2 then
    return -1;
  else
    return 1;
  fi;
end);

#############################################################################
##
##  AG_CalculateWord(<word>, <list>)
##
# XXX do not use this
InstallMethod(AG_CalculateWord, [IsAssocWord, IsList],
function(w, dom)
  local result, i;

  result := One(dom[1]);

  for i in [1..NumberSyllables(w)] do
    result := result * dom[GeneratorSyllable(w, i)]^ExponentSyllable(w, i);
  od;

  return result;
end);

#############################################################################
##
##  AG_CalculateWords(<words_list>, <list>)
##
InstallMethod(AG_CalculateWords, [IsList, IsList],
function(words, domain)
  local result, i;

  result := [];
  for i in [1..Length(words)] do
    result[i] := AG_CalculateWord(words[i], domain);
  od;

  return result;
end);

###############################################################################
##
##  AG_ReducedSphericalIndex(<ind>)
##
InstallGlobalFunction("AG_ReducedSphericalIndex",
function(M)
  local beg, per, beg_len, per_len, i;
  if IsEmpty(M.period) then return StructuralCopy(M); fi;
  beg := StructuralCopy(M.start);
  per := StructuralCopy(M.period);
  beg_len := Length(beg);
  per_len := Length(per);
  for i in Difference(DivisorsInt(per_len), [per_len]) do
    if per = Concatenation(List([1..per_len/i], j -> per{[1..i]})) then
      per := per{[1..i]};
      per_len := i;
      break;
    fi;
  od;
  while Length(beg) <> 0 and beg[beg_len] = per[per_len] do
    per := Concatenation([per[per_len]], per{[1..per_len-1]});
    beg := beg{[1..beg_len-1]};
    beg_len := beg_len - 1;
  od;
  return rec(start := beg, period := per);
end);

###############################################################################
##
##  AG_IsEqualSphericalIndex(<ind1>, <ind2>)
##
InstallGlobalFunction("AG_IsEqualSphericalIndex",
function(M1, M2)
  return AG_ReducedSphericalIndex(M1) = AG_ReducedSphericalIndex(M2);
end);

###############################################################################
##
##  AG_TopDegreeInSphericalIndex(<ind>)
##
InstallGlobalFunction("AG_TopDegreeInSphericalIndex",
function(M)
  if not IsEmpty(M.start) then return M.start[1];
  else return M.period[1]; fi;
end);

###############################################################################
##
##  AG_DegreeOfLevelInSphericalIndex(<ind>)
##
InstallGlobalFunction("AG_DegreeOfLevelInSphericalIndex",
function(M, k)
  local i;
  if Length(M.start) >= k then return M.start[k]; fi;
  i := RemInt(k-Length(M.start), Length(M.period));
  if i = 0 then i := Length(M.period); fi;
  return M.period[i];
end);

###############################################################################
##
##  AG_TreeLevelTuples(<ind>)
##  AG_TreeLevelTuples(<ind>, <n>)
##  AG_TreeLevelTuples(<start>, <period>, <n>)
##
InstallGlobalFunction("AG_TreeLevelTuples",
function(arg)
  local n, m, args, ind, start, period;

  if Length(arg) = 1 and IsList(arg[1]) then
    ind := arg[1];
    n := Length(ind);
    args := List([1..n], i -> [1..ind[i]]);
    Add(args, function(arg) return arg; end);
    return CallFuncList(ListX, args);
  elif Length(arg) = 2 and IsRecord(arg[1]) and IsPosInt(arg[2]) then
    return AG_TreeLevelTuples(arg[1].start, arg[1].period, arg[2]);
  elif Length(arg) = 3 and IsList(arg[1]) and IsList(arg[2]) and IsPosInt(arg[3]) then
    start := arg[1];
    period := arg[2];
    n := arg[3];
    m := n;
    ind := [];
    if Length(start) <> 0 then
      if n <= Length(start) then
        ind := start{[1..n]};
        m := 0;
      else
        ind := start;
        m := n - Length(start);
      fi;
    fi;
    if m > 0 then
      # TODO: why Append doesn't work???
#       Append(ind, Concatenation(List([1..Int(m/Length(period))], i -> period)));
#       Append(ind, period{[1..m-Int(m/Length(period))]});
      ind := Concatenation(ind,
        Concatenation(List([1..Int(m/Length(period))], i -> period)));
      ind := Concatenation(ind, period{[1..m-Int(m/Length(period))]});
    fi;
    args := List([1..n], i -> [1..ind[i]]);
    Add(args, function(arg) return arg; end);
    return CallFuncList(ListX, args);
  else
    Error("in AG_TreeLevelTuples\n",
          "  usage: AG_TreeLevelTuples([n_1, n_2, ..., n_k])\n",
          "         AG_TreeLevelTuples(<spher_ind>, k)\n",
          "         AG_TreeLevelTuples(start, period, k)\n");
  fi;
end);

#############################################################################
##
##  AG_AbelImageX
##  AG_AbelImageSpherTrans
##
InstallGlobalFunction(AG_AbelImageX,
function()
  if not IsReadOnlyGlobal("AG_AbelImageXvar") then
    AG_AbelImageXvar := Indeterminate(GF(2),"x");
    MakeReadOnlyGlobal("AG_AbelImageXvar");
  fi;
  return AG_AbelImageXvar;
end);

InstallGlobalFunction(AG_AbelImageSpherTrans,
function()
  return One(AG_AbelImageX())/ (One(AG_AbelImageX()) + AG_AbelImageX());
end);

#############################################################################
##
##  AG_AbelImageAutomatonInList(<list>)
##
InstallGlobalFunction(AG_AbelImageAutomatonInList,
function(list)
  local zero, one, x, m, mk, e, n, d, s, i, k, det, detk, result;

  n := Length(list);
  d := Length(list[1]) - 1;
  x := AG_AbelImageX();
  m := IdentityMat(n, x);
  e := [];
  zero := 0*x;
  one := x^0;

  for s in [1..n] do
    for i in [1..d] do
      #  if the family is defined by wreath recursion
      if IsList(list[s][i]) then
        for k in [1..Length(list[s][i])] do
          m[s][AbsInt(list[s][i][k])] := m[s][AbsInt(list[s][i][k])] + x;
        od;
      #  if the family is defined by automaton
      else
        m[s][list[s][i]] := m[s][list[s][i]] + x;
      fi;
    od;
    if SignPerm(list[s][d+1]) = 1 then e[s] := zero;
    else e[s] := one; fi;
  od;

  result := [];
  det := Determinant(m);
  for s in [1..n] do
    mk := StructuralCopy(m);
    for i in [1..n] do
      mk[i][s] := e[i];
    od;
    detk := Determinant(mk);
    result[s] := detk / det;
  od;

  return result;
end);

#E


# Copyright 2018-2019, Carnegie Mellon University
# See LICENSE for details

Class(HCOLUnparserMixin, rec(
    copyright := "",
    loopc := (self,o,i,is) >> Checked(IsRange(o.range),
        let(v := o.var, lo := o.range[1], hi := Last(o.range),
            Print(Blanks(i), "for(", v, " = ", lo, "; ", v, self._lt, hi,
            ", ", self(o.cnd, i, is), "; ", v, "++) {\n",
                self(o.cmd,i+is,is),
                Blanks(i), "}\n"))),
    abs := (self, o, i, is) >> self(cond(geq(o.args[1], V(0)), o.args[1], neg(o.args[1])), i, is),
    pow := (self, o, i, is) >> Cond(o.args[1]=2 and o.args[2]>=0, Print("(2 << ",self(o.args[2], i, is), ")"),
     	    	      	     		Print("pow(", self(o.args[1], i, is), ",", self(o.args[2], i, is), ")") ),
    creturn := (self, o, i, is) >> Print(Blanks(i), "return ", self(o.args[1], i, is), ";\n"),
	creturnCond := (self, o, i, is) >> Print(Blanks(i), "if (", self(o.args[1], i, is), ")\n",self(creturn(o.args[2]), i+is, is), "\n"),
    SIMDchain := (self, o, i, is) >> Print(Blanks(i), "#pragma omp simd\n", Blanks(i), "#pragma ivdep\n", self(chain(o.cmds),i,is))
));

Class(HCOLUnparser, HCOLUnparserMixin, CUnparser);
Class(HCOLSSEUnparser, HCOLUnparserMixin, SSEUnparser);


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

Class(TResample, Tagged_tSPL_Container, rec(
	     abbrevs :=  [ (odims, idims) -> [ odims, idims, [] ],
			   (odims, idims, shifts) -> [ odims, idims, shifts ],],
	     dims := self >> [Product(self.params[1]), Product(self.params[2])],
	     isReal := True,
	     terminate := self >> Error("Not yet implemented."),
	     transpose := self >> ObjId(self)(self.params[2], self.params[1], -1.0 * self.params[3]).withTags(self.getTags())
	     ));