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

Class(HockneyOpts, FFTXConvOpts, rec(
    tags := [],
    operations := rec(Print := s -> Print("<FFTX Hockney options record>"))
));

hockneyOpts := function(arg)
    local opts, maxSize, argrec;
    opts := Copy(FFTXConvOpts);

    argrec := rec(
        globalUnrolling := opts.globalUnrolling,
        prunedBasemaxSize := opts.globalUnrolling / 2
    );

    if IsBound(arg[1].args) and Length(arg[1].args) >= 2 and IsRec(arg[1].args[2]) then
           argrec := CopyFields(argrec, arg[1].args[2]);
    fi;
    opts.globalUnrolling := argrec.globalUnrolling;

    opts.useDeref := false;
    opts.breakdownRules.PRDFT := List([ PRDFT1_Base2, PRDFT1_CT, PRDFT_PD], _noT);
    opts.breakdownRules.IPRDFT := List([ IPRDFT1_Base1, IPRDFT1_Base2, IPRDFT1_CT, IPRDFT_PD], _noT);
    opts.breakdownRules.IPRDFT2 := List([ IPRDFT2_Base1, IPRDFT2_Base2, IPRDFT2_CT], _noT);
    opts.breakdownRules.PRDFT3 := List([ PRDFT3_Base1, PRDFT3_Base2, PRDFT3_CT, PRDFT3_OddToPRDFT1], _noT);
    opts.breakdownRules.URDFT := List([ URDFT1_Base1, URDFT1_Base2, URDFT1_Base4, URDFT1_CT ], _noT);
    opts.breakdownRules.DFT := [DFT_Base, DFT_PD, DFT_CT];
    opts.breakdownRules.IOPrunedMDRConv := [IOPrunedMDRConv_3D_2trip_zyx_freqdata];
    opts.breakdownRules.MDDFT := [ MDDFT_Base, MDDFT_RowCol ];
    opts.breakdownRules.GT := [ GT_NthLoop ];
    opts.breakdownRules.DFT := [ DFT_Base, DFT_CT, DFT_Rader, CopyFields(DFT_GoodThomas, rec(maxSize := 15)), DFT_PD ];
    opts.topTransforms := [DFT, MDDFT, PrunedDFT, PrunedIDFT, IOPrunedConv, PrunedPRDFT, PrunedIPRDFT, TTensorI, IOPrunedMDRConv,
        PRDFT, IPRDFT, PRDFT1, IPRDFT1, PRDFT2, IPRDFT2, PRDFT3, IPRDFT3, TTensorI ];
    opts.breakdownRules.MDRConv := [ MDRConv_3D_2trip_zyx_freqdata ]; # [MDRConv_Base]

    opts.breakdownRules.PrunedPRDFT := [ CopyFields(PrunedPRDFT_base, rec(maxSize := argrec.prunedBasemaxSize)), PrunedPRDFT_CT_rec_block ];
    opts.breakdownRules.PrunedIPRDFT := [ CopyFields(PrunedIPRDFT_base, rec(maxSize := argrec.prunedBasemaxSize)), PrunedIPRDFT_CT_rec_block ];
    opts.breakdownRules.PrunedDFT := [ CopyFields(PrunedDFT_base, rec(maxSize := argrec.prunedBasemaxSize)), PrunedDFT_CT_rec_block ];

    return opts;
end;

hockneyConf := rec(
    defaultName := "defaultHockneyConf",
    defaultOpts := (arg) >> rec(useHockney := true, args := arg),
    confHandler := hockneyOpts
);

fftx.FFTXGlobals.registerConf(hockneyConf);


#############################################################################
##
#W  extend.gi                                                   Karel Dekimpe
#W                                                               Bettina Eick
##

#############################################################################
##
#F FiniteSubgroupsAreCyclic( G )
##
FiniteSubgroupsAreCyclic := function( G )
    local fin, rep, flg;
    fin := FiniteSubgroupClasses( G );
    rep := List( fin, Representative );
    flg := ForAll( rep, IsCyclic );
    return flg;
end;

#############################################################################
##
#F AllActionsHolonomy( G )
##
## Computes all actions of G on Z which can lead to a torsion-free
## extension centralizing Fit(G). Thus all finite subgroups of G and Fit(G)
## centralizes Z and the kernel of the action has index at most 2.
##
AllActionsHolonomy := function( G )
    local mats, acts, fins, reps, gens, U, hom, F, low, H, i;

    # first we add the trivial action
    mats := List( Igs(G), x -> IdentityMat(1));
    acts := [mats];

    # determine the subgroup known to centralize Z
    U := FittingSubgroup(G);

    # determine all subgroups of index 2 in G/U
    hom := NaturalHomomorphismByNormalSubgroup( G, U );
    F   := Image( hom );
    low := LowIndexSubgroupClasses( F, 2 );
    low := List( low, Representative );
    low := List( low, x -> PreImage( hom, x ) );

    # for each subgroup we get one action
    for H in low do
        mats := List( Igs(G), x -> IdentityMat(1));
        for i in [1..Length(Igs(G))] do
            if not Igs(G)[i] in H then
                mats[i] := - mats[i];
            fi;
        od;
        Add( acts, mats );
    od;
    return acts;
end;

#############################################################################
##
#F AllActionsForTorsionFreeExtension( G )
##
## Computes all actions of G on Z which can lead to a torsion-free
## extension centralizing Fit(G). Thus all finite subgroups of G and Fit(G)
## centralizes Z and the kernel of the action has index at most 2.
##
AllActionsForTorsionFreeExtension := function( G )
    local mats, acts, fins, reps, gens, U, hom, F, low, H, i;

    # first we add the trivial action
    mats := List( Igs(G), x -> IdentityMat(1));
    acts := [mats];

    # determine the subgroup known to centralize Z
    fins := FiniteSubgroupClasses( G );
    reps := List( fins, Representative );
    gens := Flat( List( reps, Igs ) );
    Append( gens, GeneratorsOfGroup( FittingSubgroup(G) ) );
    U := NormalClosure( G, Subgroup( G, gens ) );
    if Index( G, U ) = 1 then return acts; fi;

    # determine all subgroups of index 2 in G/U
    hom := NaturalHomomorphismByNormalSubgroup( G, U );
    F   := Image( hom );
    low := LowIndexSubgroupClasses( F, 2 );
    low := List( low, Representative );
    low := List( low, x -> PreImage( hom, x ) );

    # for each subgroup we get one action
    for H in low do
        mats := List( Igs(G), x -> IdentityMat(1));
        for i in [1..Length(Igs(G))] do
            if not Igs(G)[i] in H then
                mats[i] := - mats[i];
            fi;
        od;
        Add( acts, mats );
    od;
    return acts;
end;

#############################################################################
##
#F ExpandedTail( tail, d )
##
ExpandedTail := function( t, d )
    local r, i;
    r := List( [1..d], x -> 0 );
    for i in [1..Length(t)] do
        if IsBound( t[i] ) then r[i] := t[i][1][1]; fi;
    od;
    return r;
end;

#############################################################################
##
#F HasTorsionFreeExtension( G, CR )
##
## Check if G has a torsion-free extension. We assume that all finite
## subgroups of G are cyclic (otherwise, no torsion-free extension exists).
## CR ist a record describing the second cohomology group of G by Z.
##
HasTorsionFreeExtension := function( G, CR )
    local fins, reps, repp, cc, d, subs, U, g, e, p, w, v, i, t, r, pr,
          f, V, spcs, sub;

    # compute the finite subgroups of prime order up to conjugacy
    fins := FiniteSubgroupClasses( G );
    reps := List( fins, Representative );
    repp := Filtered( reps, x -> IsPrime( Size(x) ) );
    if Length( repp ) = 0 then return true; fi;

    # extract second cohomology
    cc := CR.twocohom;

    # for each finite subgroup of prime order compute the corresponding
    # relation on cc.gcc
    subs := [];
    for U in repp do
        g := GeneratorsOfGroup( U )[1];
        e := Exponents( g );
        p := Order( g );
        w := rec( word := e, tail := [] );
        v := ShallowCopy( w );
        for i in [2..p] do
            v := CollectedTwoCR( CR, v, w );
        od;
        r := ExpandedTail( v.tail, Length( CR. enumrels ) );
        sub := NullspaceIntMod( cc.factor.prei, r, p );
        Add( subs, sub );
    od;

    e := Lcm( List( repp, Size ) );
    d := Length( cc.factor.prei );
    return SizeOfUnionMod( subs, e ) < e^d;
end;

#############################################################################
##
#F HasMinimalCentreExtension( G, CR )
##
## Check if there is an extension of G that has minimal centre; that is,
## Z(Fit(E)) = Z. This is only implemented for groups G with abelian
## Fitting subgroup.
##
HasMinimalCentreExtension := function( G, CR )
    local cc, d, t, F, f, g, l, c, i, j, v, w, a, b;

    # get cohomology and generic elements
    cc := CR.twocohom;
    d  := Length( CR.enumrels );
    t  := List( [1..Length(cc.gcc)], x -> Indeterminate( Integers, x ) );
    t  := t * cc.gcc;

    # get Fitting subgroup generators
    F := FittingSubgroup( G );

    # the abelian case is more effective - consider this first
    if IsAbelian( F ) then
        f := GeneratorsOfGroup( F );
        l := Length( f );

        # find tails of commutators
        c := NullMat( l, l );
        for i in [1..l] do
            for j in [i+1..l] do
                v := rec( word := Exponents( f[i]^-1 ), tail := [] );
                w := rec( word := Exponents( f[j]^-1 ), tail := [] );
                v := CollectedTwoCR( CR, v, w );
                w := rec( word := Exponents( f[i] ), tail := [] );
                v := CollectedTwoCR( CR, v, w );
                w := rec( word := Exponents( f[j] ), tail := [] );
                v := CollectedTwoCR( CR, v, w );
                b := ExpandedTail( v.tail, d );
                a := b * t;
                c[i][j] := a;
                c[j][i] := -a;
            od;
        od;

        # get determinant
        c := GenericDeterminantMat( c );
        return Length( ExtRepPolynomialRatFun(c) ) <> 0;
    fi;

    # the non-abelian case
    f := MinimalGeneratingSet( F );
    g := GeneratorsOfGroup( Centre( F ) );
    if Length(f) < Length( g ) then return false; fi;

    # find tails of commutators
    c := NullMat( Length(f), Length(g) );
    for i in [1..Length(f)] do
        for j in [1..Length(g)] do
            v := rec( word := Exponents( f[i]^-1 ), tail := [] );
            w := rec( word := Exponents( g[j]^-1 ), tail := [] );
            v := CollectedTwoCR( CR, v, w );
            w := rec( word := Exponents( f[i] ), tail := [] );
            v := CollectedTwoCR( CR, v, w );
            w := rec( word := Exponents( g[j] ), tail := [] );
            v := CollectedTwoCR( CR, v, w );
            b := ExpandedTail( v.tail, d );
            c[i][j] := b * t;
        od;
    od;

    # get determinant of rxr submats
    Error("not yet implemented");
    c := Determinant( c );
    c := ExtRepPolynomialRatFun( c );
    c := Filtered( c, x -> IsInt(x) );
    return ForAny( c, x -> x <> 0 );
end;

#############################################################################
##
#F HasExtensionOfType( G, torfree, mincent )
##
## Let G be a pcp group. This function checks if G has a torsion-free
## extension or an extension with Z(Fit(H)) = Z$ with the free abelian
## module Z.
##
InstallGlobalFunction( HasExtensionOfType, function( G, torfree, mincent )
    local mats, CR, found, i;

    # if both flags are false, then there is nothing to do
    if not torfree and not mincent then return true; fi;

    # first check if G is torsion-free or has non-cyclic finite subgrps
    Print("    check that all finite subgroups are cyclic\n");
    if not FiniteSubgroupsAreCyclic( G ) then return false; fi;

    # if G is torsion-free, then all extensions of G are torsion-free
    if Length( FiniteSubgroupClasses(G) ) = 1 then torfree := false; fi;
    if not torfree and not mincent then return true; fi;

    # now loop over actions - the trivial action first
    Print("    consider trivial action \n");
    mats := List( Igs(G), x -> IdentityMat( 1 ) );
    CR := CRRecordByMats( G, mats );
    CR.twocohom := TwoCohomologyCR( CR );
    if mincent then
        found := HasMinimalCentreExtension( G, CR );
        #if found <> HasMinimalCentreExtensionByCRRec2( G, CR ) then
        #    Error("something wrong with min cent");
        #fi;
    fi;
    if (not mincent) or (found and torfree) then
        found := HasTorsionFreeExtension( G, CR );
    fi;
    if found then return true; fi;

    # now consider the remaining actions
    mats := AllActionsForTorsionFreeExtension( G );
    for i in [2..Length(mats)] do
        Print("    consider non-trivial action number ",i-1,"\n");
        CR := CRRecordByMats( G, mats[i] );
        CR.twocohom := TwoCohomologyCR( CR );
        if mincent then
            found := HasMinimalCentreExtension( G, CR );
            #if found <> HasMinimalCentreExtensionByCRRec2( G, CR ) then
            #    Error("something wrong with min cent");
            #fi;
        fi;
        if (not mincent) or (found and torfree) then
            found := HasTorsionFreeExtension( G, CR );
        fi;
        if found then return true; fi;
    od;

    # we have not found a suitable extension
    return false;
end );


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

## Vector Multiplication operator
## VOLMultiplication(<number inputs>, <length inputs>, <length vector>)
Class(VOLMultiplication, RewritableObject, BaseMat, OLBase, rec(
    dims := self >> [self.rChildren()[2] * self.rChildren()[3], StripList(Replicate(self.rChildren()[1], self.rChildren()[2] * self.rChildren()[3]))],
));

#F RCVOLMultiplication(<number inputs>, <length inputs>, <length vector>)
#F   is a vRC(VOLMultiplication(<number inputs>, <length inputs>, <length vector>/2))
#F   inputs/output is in inteleaved complex format: [re1, im1, re2, im2, ...]

Class(RCVOLMultiplication, RewritableObject, BaseMat, OLBase, rec(
    dims := self >> [self.rChildren()[2] * self.rChildren()[3], StripList(Replicate(self.rChildren()[1], self.rChildren()[2] * self.rChildren()[3]))],
));

#F VRCOLMultiplication(<number inputs>, <length inputs>, <length vector>)
#F   inputs/output is in vector inteleaved complex format:
#F     [[re1, re2, ..., re_v], [im1, im2, ..., im_v], [re_(v+1), ...], ...]

Class(VRCOLMultiplication, RewritableObject, BaseMat, OLBase, rec(
    dims := self >> [2 * self.rChildren()[2] * self.rChildren()[3], StripList(Replicate(self.rChildren()[1], 2 * self.rChildren()[2] * self.rChildren()[3]))],
));

## VOLConjMultiplication(<number inputs>, <length inputs>, <length vector>)
Class(VOLConjMultiplication, RewritableObject, BaseMat, OLBase, rec(
    dims := self >> [self.rChildren()[2] * self.rChildren()[3], StripList(Replicate(self.rChildren()[1], self.rChildren()[2] * self.rChildren()[3]))],
));

#F RCVOLConjMultiplication(<complex input length>, <length vector>)
#F   is a vRC(VTensor(OLConjMultiplication(<complex input length>), <length vector>/2))
#F   inputs/output is in inteleaved complex format: [re1, im1, re2, im2, ...]

Class(RCVOLConjMultiplication, RewritableObject, BaseMat, OLBase, rec(
    dims := self >> [self.rChildren()[2] * self.rChildren()[3], StripList(Replicate(self.rChildren()[1], self.rChildren()[2] * self.rChildren()[3]))],
));

#F VRCOLConjMultiplication(<complex input length>, <length vector>)
#F   inputs/output is in vector inteleaved complex format:
#F     [[re1, re2, ..., re_v], [im1, im2, ..., im_v], [re_(v+1), ...], ...]

Class(VRCOLConjMultiplication, RewritableObject, BaseMat, OLBase, rec(
    dims := self >> [2 * self.rChildren()[2] * self.rChildren()[3], StripList(Replicate(self.rChildren()[1], 2 * self.rChildren()[2] * self.rChildren()[3]))],
));


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

# VGath_sv/VScat_sv may have bigger dimensions than their function domain so we need padding
# to have Diag with correct dimensions when commuting diag through VGath_sv and VScat_sv
_fComposeVGathScatSv_Diag := (m, dim, d) -> let(
    g := fTensor(m.func, fId(m.sv)),
    mdim := dim(m),
    rem := Cond(m.rem _is Unk, g.domain() mod mdim, m.rem=0, 0, m.v - m.rem * m.sv),
    f := fCompose(d.element, g),
    When(rem = 0, f, diagDirsum(f, fConst(TReal, mdim-rem, 0.0)))
);

Class(RulesVDiag, RuleSet);
RulesVDiag.__transparent__ := [ [Compose, [vRC]] ];  # creates extra rules, A*B->C is made into vRC(A)*vRC(B) -> vRC(C)

RewriteRules(RulesVDiag, rec(
    DiagConst_AnyScat := ARule(Compose, [[Diag, [fConst, @(1), @(2), @(3)]], @(4).cond(IsVScat)],
	e -> [ @(4).val, VDiag(fConst(@(1).val, Cols(@(4).val), @(3).val), getV(@(4).val))]),

    AnyGath_DiagConst := ARule(Compose, [@(4).cond(IsVGath), [Diag, [fConst, @(1), @(2), @(3)]]],
	e -> [ VDiag(fConst(@(1).val, Rows(@(4).val), @(3).val), getV(@(4).val)), @(4).val]),

    DiagConst_NoDiagPullin := ARule(Compose, [[Diag, [fConst, @(1), @(2), @(3)]], @(4, NoDiagPullin)],
	e -> [ @(4).val, Diag(fConst(@(1).val, Cols(@(4).val), @(3).val))]),

    # VGath * IDirSum
     CommuteVGathIDirSum := ARule( Compose,
	 [ [@(1, VGath), @(3)],
	   [@(0, [PushL, PushLR]), @(4, IDirSum, e -> (@(1).val.v mod Rows(e.child(1))) = 0)] ],
       e -> let(
             ratio := @(1).val.v / Rows(@(4).val.child(1)),
             f := When(ratio=1, @(3).val, fTensor(@(3).val, fId(ratio))),
             j := @(4).val.var,
             jj := Ind(f.domain()),

             [ ObjId(@(0).val)(IDirSum(jj,
                   Data(j, f.at(jj), SubstTopDown(@(4).val.child(1), @(5,fBase,e->e.params[2]=j),
                                       e -> fCompose(f, fBase(jj)))))),
               @(1).val ])
    ),

    # IDirSum * VScat
    CommuteIDirSumVScat := ARule( Compose,
       [ [@(0,[PushR, PushLR]), @(4, IDirSum)],
         [@(1, VScat, e -> (e.v mod Cols(@(4).val.child(1)))=0), @(3)] ],

       e -> let(
             ratio := @(1).val.v / Cols(@(4).val.child(1)),
             f := When(ratio=1, @(3).val, fTensor(@(3).val, fId(ratio))),
             j := @(4).val.var,
             jj := Ind(f.domain()),

             [ @(1).val,
               ObjId(@(0).val)(IDirSum(jj,
                   Data(j, f.at(jj), SubstTopDown(@(4).val.child(1), @(5,fBase,e->e.params[2]=j),
                                       e -> fCompose(f, fBase(jj)))))) ])
    ),

    # Gath * RCDiag
    CommuteVGathVRCDiag_fTensor := ARule( Compose,
       [ [@(1, VGath), [@(0,fTensor), ..., [fId, @(2).cond(_isEven)]]], @(4, VRCDiag) ],
       e -> [ VRCDiag(fCompose(@(4).val.element, @(0).val), @(4).val.v), @(1).val ]),

    # Gath * RCDiag
    CommuteVGathVRCDiag_H := ARule( Compose,
       [ [@(1, VGath), [@(2, H), @, @.cond(_isEven), @.cond(_isEven), _1]], @(4, VRCDiag) ],
       e -> [ VRCDiag(fCompose(@(4).val.element, @(2).val), @(4).val.v), @(1).val ]),

    # RCVDiag * VScat
    CommuteRCVDiagVScat := ARule( Compose,
       [ @(1, RCVDiag), @(2, VScat) ], # <-1 <-2 o
        e -> [ @2.val, RCVDiag(fCompose(@1.val.element, @2.val.func), @2.val.v).attrs(@(1).val) ]),

    # RCVDiagSplit * VScat
    CommuteRCVDiagSplitVScat := ARule( Compose,
       [ @(1, RCVDiagSplit), @(2, VScat) ], # <-1 <-2 o
        e -> [ @2.val, RCVDiagSplit(fCompose(@1.val.element, fTensor(@2.val.func, fId(2))), @2.val.v).attrs(@(1).val) ]),

    # VRCDiag * VScat
    CommuteVRCDiagVScat_fTensor := ARule( Compose,
        [ @(4, VRCDiag), [@(1, VScat), [@(0,fTensor), ..., [fId, @(2).cond(_isEven)]]] ],
        e -> [ @(1).val, VRCDiag(fCompose(@(4).val.element, @(0).val), @(4).val.v) ]),

    # VRCDiag * VScat
    CommuteVRCDiagVScat_H := ARule( Compose,
        [ @(4, VRCDiag), [@(1, VScat), [@(2, H), @, @.cond(_isEven), @.cond(_isEven), _1]] ],
        e -> [ @(1).val, VRCDiag(fCompose(@(4).val.element, @(2).val), @(4).val.v) ]),

    # VGath * Diag
    CommuteVGathDiag := ARule( Compose,
       [ @(1, VGath), @(2, Diag) ], # o 1-> 2->
        e -> [ VDiag(fCompose(@2.val.element, fTensor(@1.val.func, fId(@1.val.v))), @1.val.v).attrs(@(2).val), @1.val ]),

    # VGath * VDiag
    CommuteVGathVDiag := ARule( Compose,
       [ @(1, VGath), @(2, VDiag) ], # o 1-> 2->
        e -> [ VDiag(fCompose(@2.val.element, fTensor(@1.val.func, fId(@1.val.v))), @1.val.v).attrs(@(2).val), @1.val ]),

    # Diag * VScat
    CommuteDiagVScat := ARule( Compose,
       [ @(1, Diag), @(2, VScat) ], # <-1 <-2 o
        e -> [ @2.val, VDiag(fCompose(@1.val.element, fTensor(@2.val.func, fId(@2.val.v))), @2.val.v).attrs(@(1).val) ]),

    # VDiag * VScat
    CommuteVDiagVScat := ARule( Compose,
       [ @(1, VDiag), @(2, VScat) ], # <-1 <-2 o
        e -> [ @2.val, VDiag(fCompose(@1.val.element, fTensor(@2.val.func, fId(@2.val.v))), @2.val.v).attrs(@(1).val) ]),

    # RCVDiag * VScat
    CommuteRCVDiagVScat := ARule( Compose,
       [ @(1, RCVDiag), @(2, VScat) ], # <-1 <-2 o
        e -> [ @2.val, RCVDiag(fCompose(@1.val.element, @2.val.func), @2.val.v).attrs(@(1).val) ]),

    # RCVDiagSplit * VScat
    CommuteRCVDiagSplitVScat := ARule( Compose,
       [ @(1, RCVDiagSplit), @(2, VScat) ], # <-1 <-2 o
        e -> [ @2.val, RCVDiagSplit(fCompose(@1.val.element, fTensor(@2.val.func, fId(2))), @2.val.v).attrs(@(1).val) ]),

    # VGath * VDiag_x_I
    CommuteVGathVDiag_x_I := ARule( Compose,
        [ @(1, VGath), @(2, VDiag_x_I) ], # o 1-> 2->
    e -> [ VDiag_x_I(fCompose(@2.val.element, @1.val.func), @2.val.v).attrs(@(2).val), @1.val ]),

    # VDiag_x_I * VScat
    CommuteVDiag_x_IVScat := ARule( Compose,
        [ @(1, VDiag_x_I), @(2, VScat) ], # <-1 <-2 o
    e -> [ @2.val, VDiag_x_I(fCompose(@1.val.element, @2.val.func), @1.val.v).attrs(@(1).val) ]),

    #------------------------
    # VGath_sv * Diag
    CommuteVGathsvDiag := ARule( Compose,
       [ @(1, VGath_sv), @(2, Diag) ], # o 1-> 2->
       e -> [ VDiag(_fComposeVGathScatSv_Diag(@1.val, Rows, @2.val), @1.val.v).attrs(@2.val), @1.val ]
    ),

    # VGath_sv * VDiag
    CommuteVGathsvVDiag := ARule( Compose,
       [ @(1, VGath_sv), @(2, VDiag) ], # o 1-> 2->
       e -> [ VDiag(_fComposeVGathScatSv_Diag(@1.val, Rows, @2.val), @1.val.v).attrs(@2.val), @1.val ]
    ),

    # Diag * VScat_sv
    CommuteDiagVScatsv := ARule( Compose,
       [ @(1, Diag), @(2, VScat_sv) ], # <-1 <-2 o
       e -> [ @2.val, VDiag(_fComposeVGathScatSv_Diag(@2.val, Cols, @1.val), @2.val.v).attrs(@1.val) ]
    ),

    # VDiag * VScat_sv
    CommuteVDiagVScatsv := ARule( Compose,
       [ @(1, VDiag), @(2, VScat_sv) ], # <-1 <-2 o
       e -> [ @2.val, VDiag(_fComposeVGathScatSv_Diag(@2.val, Cols, @1.val), @2.val.v).attrs(@1.val) ]
    ),

    #-----------------------------------------------------------
    # VStretchGath * Diag
    CommuteVStretchGathDiag := ARule( Compose,
       [ @(1, VStretchGath), @(2, Diag) ], # o 1-> 2->
       e -> let( vg := @1.val, den := vg.func.domain()/vg.part, num := _roundup(den, vg.v),
            [ VDiag(fStretch(fCompose(@2.val.element, @1.val.func), num, den), @1.val.v).attrs(@2.val), @1.val ])
    ),

    # Diag * VStretchScat
    CommuteVStretchScatDiag := ARule( Compose,
       [ @(1, Diag), @(2, VStretchGath)],
       e -> let( vg := @2.val, den := vg.func.domain()/vg.part, num := _roundup(den, vg.v),
            [ @2.val, VDiag(fStretch(fCompose(@1.val.element, @2.val.func), num, den), @2.val.v).attrs(@1.val) ])
    ),

    #-----------------------------------------------------------
    # Diag * IxVScat_pc
    CommuteDiagIxVScat_pc := ARule( Compose,
	[ @(1, [Diag, VDiag]), @(2, IxVScat_pc) ], # <-1 <-2 o
        e -> let(s:=@(2).val, [ s, VDiag(fStretch(@(1).val.element, _roundup(s.n, s.v), s.n), s.v) ])),

    # IxVGath_pc * Diag
    CommuteIxVGath_pcDiag := ARule( Compose,
	[ @(1, IxVGath_pc), @(2, [Diag, VDiag]) ], # o 1-> 2->
        e -> let(g:=@(1).val, [ VDiag(fStretch(@(2).val.element, _roundup(g.n, g.v), g.n), g.v), g ])),

    # Diag * VScat_pc
    CommuteDiagVScat_pc := ARule(Compose,
       [ @(1, [Diag, VDiag]), @(2, VScat_pc) ], # <-1 <-2 o
       e -> let(s := @(2).val, f := @(1).val.element,
	   [ s, VDiag(fStretch(fCompose(f, fAdd(f.domain(), s.n, s.ofs)),
			       _roundup(s.n, s.v), s.n), s.v) ])),

    # VGath_pc * Diag
    CommuteVGath_pcDiag := ARule( Compose, # o 1-> 2->
       [ @(1, VGath_pc), @(2, [Diag, VDiag]) ],
       e -> let(g := @(1).val, f := @(2).val.element,
	   [ VDiag(fStretch(fCompose(f, fAdd(f.domain(), g.n, g.ofs)),
		            _roundup(g.n, g.v), g.n), g.v), g ])),

    #-----------------------------------------------------------
    # YSV: Below 2 rules have limitations which can be removed,
    #      But it seems that fStretch has built-in assumptions which makes it difficult
    #      to remove these limitations..

    # NOTE: Remove limitations, the rule as is, is invalid for g.ofs <> 0, or if g.N > g.v
    #        Hence the precondition
    ## Diag * IxRCVScat_pc
    CommuteDiagIxRCVScat_pc := ARule(Compose,
       [ @(1, [Diag, VDiag]), @(2, IxRCVScat_pc, e->e.ofs = 0 and not IsSymbolic(e.n) and e.n <= e.v) ], # <-1 <-2 o
       e -> let(s := @(2).val,
	   [ s, VDiag(fStretch(@(1).val.element,
			       _roundup(s.n, 2*s.v), 2*s.n), s.v) ])),

    # NOTE: Remove limitations, the rule as is, is invalid for g.ofs <> 0, or if g.N > g.v
    #        Hence the precondition
    # IxRCVGath_pc * Diag
    CommuteIxRCVGath_pcDiag := ARule( Compose, # o 1-> 2->
       [ @(1, IxRCVGath_pc, e->e.ofs = 0 and not IsSymbolic(e.n) and e.n <= e.v), @(2, [Diag, VDiag]) ],
       e -> let(g := @(1).val,
	   [ VDiag(fStretch(@(2).val.element,
		            _roundup(g.n, 2*g.v), 2*g.n), g.v), g ])),
    #-----------------------------------------------------------

    #-----------------------------------------------------------
# NOTE: unclear how to do. Rewriting rule missing: f^{n->N} o \pi^{n} -> \rho^{N} o h^{n->N}
#    # BlockVPerm * VDiag
#    CommuteBlockVPermVDiag := ARule( Compose,
#       [ @(1, BlockVPerm), @(2, VDiag) ], # o 1-> 2->
#         e -> let(vdiag := Copy(@2.val), bperm := Copy(@1.val),
#         [ VDiag(fCompose(vdiag.element, fTensor(fId(bperm.n), bperm.perm)), vdiag.v).attrs(vdiag), BlockVPerm2(bperm.n, bperm._children[1], bperm.perm) ])),
#
#    # VDiag * BlockVPerm
#    CommuteVDiagBlockVPerm := ARule( Compose,
#       [ @(1, VDiag), @(2, BlockVPerm) ], # <-1 <-2 o
#         e -> let(vdiag := Copy(@1.val), bperm := Copy(@2.val),
#        [ BlockVPerm2(bperm.n, bperm._children[1], bperm.perm), VDiag(fCompose(vdiag.element, fTensor(fId(bperm.n), bperm.perm)), vdiag.v).attrs(vdiag) ])),

# this is a hack that *DOES NOT* produce correct code
#    # BlockVPerm * VDiag: HACK that *DOES NOT* produce correct code
#    CommuteBlockVPermVDiag := ARule( Compose,
#       [ @(1, BlockVPerm), @(2, VDiag) ], # o 1-> 2->
#         e -> let(vdiag := Copy(@2.val), bperm := Copy(@1.val),
#         [ vdiag, BlockVPerm2(bperm.n, bperm._children[1], bperm.perm) ])),
#
#    # VDiag * BlockVPerm: HACK that *DOES NOT* produce correct code
#    CommuteVDiagBlockVPerm := ARule( Compose,
#       [ @(1, VDiag), @(2, BlockVPerm) ], # <-1 <-2 o
#         e -> let(vdiag := Copy(@1.val), bperm := Copy(@2.val),
#        [ BlockVPerm2(bperm.n, bperm._children[1], bperm.perm), vdiag ])),

    #--------------------------------------------------
    #   vectorize constructs
    ParDiagVPerm := ARule(Compose, [@(1, Diag), @(2, VPerm)],
        e -> [VDiag(@1.val.element, @(2).val.vlen), @(2).val]
    ),
    ParVPermDiag := ARule(Compose, [@(1, VPerm), @(2, Diag)],
        e -> [@(1).val, VDiag(@2.val.element, @(1).val.vlen)]
    ),
    ParScatVDiag := ARule(Compose,
    [[Scat, [@(0,fTensor), ..., @(2,fId)]], @(1,VDiag,e->@(2).val.size=Rows(e))],
    e -> let(ch := @(0).val.children(), X := @(1).val, v := X.v,
               [VTensor(Scat(fTensor(DropLast(ch,1), fId(Rows(X)/v))), v), X])
    ),
    ParVTensorDiag := ARule(Compose, [@(1, VTensor), @(2, Diag)],
        e -> [ @(1).val, VDiag(@2.val.element, @(1).val.vlen)]
    ),
    ParDiagVTensor := ARule(Compose, [@(1, Diag), @(2, VTensor)],
        e -> [ VDiag(@1.val.element, @(2).val.vlen), @(2).val ]
    ),

    ParVSDiag := ARule(Compose, [@(1, VS), @(2, Diag)],
        e -> [ @(1).val, VDiag(@2.val.element, @(1).val.v)]
    ),

    ParDiagVS := ARule(Compose, [@(1, Diag), @(2, VS)],
        e -> [ VDiag(@1.val.element, @(2).val.v), @(2).val ]
    )
));

RewriteRules(RulesFuncSimp, rec(

    RCData_VDupOnline := Rule( [RCData, VDupOnline],
        e -> VDupOnline(RCData(e.func.func), e.func.v)),

    RCVData_VDupOnline := Rule( [RCVData, VDupOnline],
        e -> VDupOnline(RCVData(VDup(e.func.func, 1)), e.func.v)),

    RCVDataSplitVec_VDupOnline := Rule( [RCVDataSplitVec, VDupOnline],
        e -> VDupOnline(RCVDataSplitVec(VDup(e.func.func, 1)), e.func.v)),

    fCompose_VDupOnline_FList := ARule(fCompose, [@(1, VDupOnline), @(2, FList)],
        e -> [VDupOnline(fCompose(@(1).val.func, @(2).val), @(1).val.v)]),

    fPrecompute_VDupOnline := Rule( [fPrecompute, VDupOnline],
        e -> VDupOnline(fPrecompute(e.child(1).func), e.child(1).v)),

    RCVData_VDup_CRData := Rule([RCVData, [VDup, [CRData, @(1)], @(2)]],
        e -> VDup( VData(@(1).val, 2), @(2).val) ),
));


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

Class(BaseVGath, BaseMat, SumsBase, rec(
    isVGath := true,
    sums := self >> self,
    isReal := self >> true,
    dims := self >> Error(ObjId(self), ".dims() is undefined"),
    needInterleavedLeft := False,
    needInterleavedRight := False,
    conjTranspose := self >> self.transpose(),
));

Class(BaseSVGath, BaseMat, SumsBase, rec(
    isVGath := true,
    isSVGath := true,
    #-----------------------------------------------------------------------
    abbrevs := [
	(func, v, sv) -> Checked(
	    IsFunction(func) or IsFuncExp(func), IsInt(v), IsInt(sv),
	    [func, v, sv, Cond(CanBeFullyEvaluated(func.domain()), EvalScalar((func.domain()*sv mod v) / sv), Unk(TInt))]),
	(func, v, sv, rem) -> Checked(
	    IsFunction(func) or IsFuncExp(func), IsInt(v), IsInt(sv), rem _is Unk or IsInt(_unwrap(rem)),
	    [func, v, sv, rem]),
    ],
    #-----------------------------------------------------------------------
    new := (self, func, v, sv, rem) >> SPL(WithBases(self,
        rec(func := func, v := v, sv := sv, rem := rem))).setDims(),
    #-----------------------------------------------------------------------
    dims := self >> Error(ObjId(self), ".dims() is undefined"),
    #-----------------------------------------------------------------------
    print := (self,i,is) >> Print(self.__name__, "(", self.func, ", ",
        self.v, ", ", self.sv, ", ", self.rem, ")", self.printA()),
    #-----------------------------------------------------------------------
    getConstantRem := self >> Cond(
	self.rem _is Unk and not IsSymbolic(EvalScalar(self.func.domain())),
	    EvalScalar(imod(self.func.domain()*self.sv, self.v) / self.sv),
	self.rem _is Unk,
	    Error("Can't generate sub-vector gather/scatter code for <self>, because its dimensions are unknown"),
	self.rem
    ),
    #-----------------------------------------------------------------------
    conjTranspose := self >> self.transpose(),

    sums := self >> self,
    isReal := self >> true,
    needInterleavedLeft := False,
    needInterleavedRight := True
));

IsVGath := x -> IsRec(x) and IsBound(x.isVGath) and x.isVGath;
IsSVGath := x -> IsRec(x) and IsBound(x.isSVGath) and x.isSVGath;

#F ==========================================================================
#F VGath(<func>, <v>) - vector gather (read) matrix, assumes aligned input
#F
#F <func> must be a vector index function returning vector indices
#F
#F VGath(f, v) == Gath(fTensor(f, fId(v)))
#F ==========================================================================
Class(VGath, BaseVGath, SumsBase, rec(
    #-----------------------------------------------------------------------
    #F NOTE: rChildren: does not expose .v!
    rChildren := self >> [self.func],
    rSetChild := rSetChildFields("func"),
    from_rChildren := (self, rch) >> ObjId(self)(rch[1], self.v),
    #-----------------------------------------------------------------------
    new := (self, func, v) >> SPL(WithBases(self,
        rec(func := func, v := v))).setDims(),
    #-----------------------------------------------------------------------
    dims := self >> [self.v*self.func.domain(), self.v*self.func.range()],
    #-----------------------------------------------------------------------
    area := self >> self.func.domain() * self.v,
    transpose := self >> VScat(self.func, self.v),
    #-----------------------------------------------------------------------
    print := (self,i,is) >> Print(self.name, "(", self.func, ", ", self.v,")", self.printA()),
    #-----------------------------------------------------------------------
    toAMat := self >> let(v:=self.v, n := EvalScalar(v*self.func.domain()),
        N := EvalScalar(v*self.func.range()),
        func := fTensor(self.func, fId(v)).lambda(),
        AMatMat(List([0..n-1], row -> BasisVec(N, EvalScalar(func.at(row).ev()))))),
));

#F ==========================================================================
#F VGath_u(<func>, <v>) -- assumes input is unaligned, and uses scalar <func>
#F
#F VGath_u performs an unaligned gather operations. Thus <func> must be an
#F index function returning scalar, not vector, addresses (unlike VGath).
#F
#F In particular:
#F   VGath(func, vlen) = VGath_u(fTensor(func, fBase(vlen, 0)), vlen)
#F ==========================================================================
Class(VGath_u, VGath, rec(
    new := (self, func, v) >> SPL(WithBases(self,
        rec(func := func, v := v))).setDims(),
    #-----------------------------------------------------------------------
    dims := self >> [self.v*self.func.domain(), self.func.range()],
    #-----------------------------------------------------------------------
    toAMat := self >> let(v:=self.v,
        n := EvalScalar(v*self.func.domain()),
        N := EvalScalar(self.func.range()),
        func := self.func.lambda(),
        AMatMat(List([0..n-1], row -> BasisVec(N, EvalScalar(add(func.at(idiv(row,v)), imod(row, v)).ev()))))),
    #-----------------------------------------------------------------------
    transpose := self >> VScat_u(self.func, self.v)
));

#F ==========================================================================
#F VGath_zero(N, n, <v>) - vector gather matrix
#F ==========================================================================
Class(VGath_zero, BaseVGath, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.N, self.n, self.v],
    rSetChild := rSetChildFields("N", "n", "v"),
    #-----------------------------------------------------------------------
    new := (self, N, n, v) >> SPL(WithBases(self,
        rec(n := n, N := N, v := v))).setDims(),
    #-----------------------------------------------------------------------
    dims := self >> [self.v * self.n, self.v * self.N],
    #-----------------------------------------------------------------------
    transpose := self >> VScat_zero(self.N, self.n, self.v),
    toAMat := self >> VGath(fAdd(self.N, self.n, 0), self.v).toAMat(),
 ));

#F ==========================================================================
#F VGath_dup(<func>, <v>) - gathers scalar values and replicates <v> times to
#F                          obtain a vector values (read) matrix
#F Example:
#F spiral> PrintMat(MatSPL(VGath_dup(fId(4), 2)));
#F [ [ 1,  ,  ,   ],
#F   [ 1,  ,  ,   ],
#F   [  , 1,  ,   ],
#F   [  , 1,  ,   ],
#F   [  ,  , 1,   ],
#F   [  ,  , 1,   ],
#F   [  ,  ,  , 1 ],
#F   [  ,  ,  , 1 ] ]
#F
#F This is performed using shuffles in SSE/SSE2, or movddup in SSE4.
#F
#F The transpose of VGath_dup is VScat_red (red = reduce) incurs
#F arithmetic ops.
#F
#F spiral> PrintMat(MatSPL(VScat_red(fId(4), 2)));
#F [ [ 1, 1,  ,  ,  ,  ,  ,   ],
#F   [  ,  , 1, 1,  ,  ,  ,   ],
#F   [  ,  ,  ,  , 1, 1,  ,   ],
#F   [  ,  ,  ,  ,  ,  , 1, 1 ] ]
#F
#F Some architectures have special instructions for this operation.
#F For example, SSE4 has 'hadd' == horizontal add.
#F
#F ==========================================================================
Declare(VGath_dup);
Class(VGath_dup, VGath, rec(
    #-----------------------------------------------------------------------
    new := (self, func, v) >> SPL(WithBases(self,
        rec(func := func, v := v))).setDims(),
    #-----------------------------------------------------------------------
    dims := self >> [self.v*self.func.domain(), self.func.range()],
    #-----------------------------------------------------------------------
    transpose := self >> Error("not yet supported"), #VScat_red(self.func, self.v),
    #-----------------------------------------------------------------------
    toAMat := self >> Tensor(
        Gath(self.func),
        Mat(TransposedMat([Replicate(self.v, 1)]))).toAMat(),
    #-----------------------------------------------------------------------
    toloop := (self, bksize) >> let(i := Ind(self.func.domain() / bksize),
	ISum(i, i.range,
            VScat(fTensor(fBase(i), fId(bksize)),self.v) *
            VGath_dup(fCompose(self.func, fTensor(fBase(i), fId(bksize))),self.v)))
    #-----------------------------------------------------------------------
));

#F ==========================================================================
#F VGath_dup_x_I(<func>, <v>) - gathers vector values and replicates <v> times
#F NOTE: EXPLAIN THIS
#F ==========================================================================
VGath_dup_x_I := (func, v) -> Cond(
    func.domain() = 1,
        VReplicate(v),
    # else
    let(i := Ind(func.domain()),
	ISum(i, i.range,
	    VScat(fTensor(fBase(i),fId(v)), v) *
	    VReplicate(v) *
	    VGath(fBase(i),v)) *
        VGath(func,v)
    )
);

#F ==========================================================================
#F VGath_sv(<func>, <v>, <sv>) - vector gather (read) matrix on subvectors
#F NOTE: EXPLAIN THIS
#F ==========================================================================
Class(VGath_sv, BaseSVGath, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.func],    # self >> [self.func, self.v, self.sv],
    rSetChild := rSetChildFields("func"), # rSetChildFields("func", "v", "sv"),
    from_rChildren := (self, rch) >> ObjId(self)(rch[1], self.v, self.sv, self.rem),
    #-----------------------------------------------------------------------
    dims := self >> [_roundup(self.sv * self.func.domain(), self.v),
                     self.sv * self.func.range() ],
    #-----------------------------------------------------------------------
    area := self >> 2 * self.func.domain() * self.sv,
    #-----------------------------------------------------------------------
    transpose := self >> VScat_sv(self.func, self.v, self.sv, self.rem),
    #-----------------------------------------------------------------------
    toAMat := self >> let(sv:=self.sv,
        n := EvalScalar(sv * self.func.domain()), N := EvalScalar(Cols(self)),
        nv := EvalScalar(Rows(self)),
	func := fTensor(self.func, fId(sv)).lambda(),
        Scat(fAdd(nv, n, 0)).toAMat() *
        AMatMat(List([0..n-1], row -> BasisVec(N, EvalScalar(func.at(row)))))),
));

#F ==========================================================================
#F RCVGath_sv(<func>, <v>, <sv>) - vector gather (read) matrix on subvectors
#F NOTE: EXPLAIN THIS
#F ==========================================================================
Class(RCVGath_sv, BaseSVGath, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.func],    # self >> [self.func, self.v, self.sv],
    rSetChild := rSetChildFields("func"), # rSetChildFields("func", "v", "sv"),
    from_rChildren := (self, rch) >> ObjId(self)(rch[1], self.v, self.sv, self.rem),
    #-----------------------------------------------------------------------
    dims := self >> let(
        n := self.func.domain() * self.sv, nv := _roundup(n, self.v),
	N := self.func.range() * self.sv,
        2 * [nv, N]),
    #-----------------------------------------------------------------------
    area := self >> 2 * self.func.domain() * self.sv,
    #-----------------------------------------------------------------------
    transpose := self >> RCVScat_sv(self.func, self.v, self.sv, self.rem),
    #-----------------------------------------------------------------------
    toAMat := self >> let(
	sv := EvalScalar(self.sv), n := EvalScalar(self.func.domain()),
	N  := EvalScalar(self.func.range()),
        func := fTensor(self.func, fId(sv), fId(2)).lambda(),
        Scat(fTensor(fAdd(_roundup(n, self.v), n, 0), fId(2))).toAMat() *
	AMatMat(List([0..2*n-1], row -> BasisVec(2*N, EvalScalar(func.at(row).ev()))))),
));

#F ==========================================================================
#F VStretchGath(<func>, <part>, <v>) -
#F   vector gather (read) matrix on subvectors
#F   reads according to func but fills up entire vectors
#F NOTE: EXPLAIN THIS
#F ==========================================================================
Class(VStretchGath, BaseSVGath, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.func],
    rSetChild := rSetChildFields("func"),
    from_rChildren := (self, rch) >> ObjId(self)(rch[1], self.part, self.v),
    #-----------------------------------------------------------------------
    dims := self >> let(
	n  := self.func.domain(), N := self.func.range(),
	nv := self.part * _roundup(n/self.part, self.v),
        [nv, N]),
    #-----------------------------------------------------------------------
    abbrevs := [],
    new := (self, func, part, v) >> SPL(WithBases(self,
        rec(func := func, part := part, v := v))).setDims(),
    #-----------------------------------------------------------------------
    area := self >> self.func.domain(),
    #-----------------------------------------------------------------------
    transpose := self >> VStretchScat(self.func, self.part, self.v),
    #-----------------------------------------------------------------------
    print := (self,i,is) >> Print(self.name, "(", self.func, ", ",
        self.part, ", ", self.v, ")", self.printA()),
    #-----------------------------------------------------------------------
    toAMat := self >> let(
	n  := EvalScalar(self.func.domain()),
	N  := EvalScalar(self.func.range()),
	nv := self.part * _roundup(n/self.part, self.v),
        Tensor(I(self.part), Scat(fAdd(nv/self.part, n/self.part, 0))).toAMat() *
	AMatMat(List([0..n-1], row ->
		    BasisVec(N, EvalScalar(self.func.at(row).ev()))))),
));

#F ==========================================================================
#F vRCStretchGath(<func>, <parts>, <v>) -
#F   vector gather (read) matrix on subvectors
#F   reads according to func but fills up entire vectors
#F NOTE: EXPLAIN THIS
#F ==========================================================================
Class(vRCStretchGath, BaseSVGath, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.func],
    rSetChild := rSetChildFields("func"),
    from_rChildren := (self, rch) >> ObjId(self)(rch[1], self.part, self.v),
    #-----------------------------------------------------------------------
    dims := self >> let(
	n  := self.func.domain(), N := self.func.range(),
	nv := self.part * _roundup(n/self.part, self.v/2),
	2*[nv, N]),
    #-----------------------------------------------------------------------
    abbrevs := [],
    new := (self, func, part, v) >> SPL(WithBases(self,
        rec(func := func, part := part, v := v))).setDims(),
    #-----------------------------------------------------------------------
    area := self >> 2 * self.func.domain(),
    #-----------------------------------------------------------------------
    transpose := self >> vRCStretchScat(self.func, self.part, self.v),
    #-----------------------------------------------------------------------
    print := (self,i,is) >> Print(self.name, "(", self.func, ", ",
        self.part, ", ", self.v, ")", self.printA()),
    #-----------------------------------------------------------------------
    toAMat := self >> let(
	n  := self.func.domain(), N := self.func.range(),
	nv := self.part * _roundup(n/self.part, self.v/2),
        func := fTensor(self.func, fId(2)),
        Tensor(I(self.part),
	       Scat(fAdd(nv/self.part, n/self.part, 0)), I(2)).toAMat() *
	AMatMat(List([0..2*n-1], row -> BasisVec(2*N, EvalScalar(func.at(row).ev()))))),
));

#F ==========================================================================
#F RCVStretchGath(<func>, <parts>, <v>) -
#F   vector gather (read) matrix on subvectors
#F   reads according to func but fills up entire vectors
#F NOTE: EXPLAIN THIS
#F ==========================================================================
Class(RCVStretchGath, BaseSVGath, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.func],
    rSetChild := rSetChildFields("func"),
    from_rChildren := (self, rch) >> ObjId(self)(rch[1], self.part, self.v),
    #-----------------------------------------------------------------------
    dims := self >> let(
	n  := self.func.domain(), N := self.func.range(),
	nv := self.part * _roundup(n/self.part, self.v),
	2*[nv, N]),
    #-----------------------------------------------------------------------
    abbrevs := [],
    new := (self, func, part, v) >> SPL(WithBases(self,
        rec(func := func, part := part, v := v))).setDims(),
    #-----------------------------------------------------------------------
    area := self >> 2 * self.func.domain() * self.v,
    #-----------------------------------------------------------------------
    transpose := self >> RCVStretchScat(self.func, self.part, self.v),
    #-----------------------------------------------------------------------
    print := (self,i,is) >> Print(self.name, "(", self.func, ", ",
        self.part, ", ", self.v, ")", self.printA()),
    #-----------------------------------------------------------------------
    toAMat := self >> Tensor(VStretchGath(self.func, self.part, self.v),
                             I(2)).toAMat(),
));

#F ==========================================================================
#F VGath_pc(N, n, ofs, v) - gather for unaligned partial contiguous vectors
#F   reads <n> contiguous values out of <N> at an offset <ofs>
#F   outputs ceil(<n> / <v>) vectors
#F
#F   .N   -- input size, only affects dimensions of VGath_pc matrix
#F   .n   -- how many contiguous points we are reading
#F   .ofs -- starting offset
#F   .v   -- vector length
#F
#F Example:
#F
#F spiral> PrintMat(MatSPL(VGath_pc(5, 3, 0, 4)));
#F [ [ 1,  ,  ,  ,   ],
#F   [  , 1,  ,  ,   ],
#F   [  ,  , 1,  ,   ],
#F   [  ,  ,  ,  ,   ] ]
#F
#F ==========================================================================
Class(VGath_pc, BaseSVGath, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.N, self.n, self.ofs, self.v],
    rSetChild := rSetChildFields("N", "n", "ofs", "v"),
    #-----------------------------------------------------------------------
    abbrevs := [],
    new := (self, N, n, ofs, v) >> SPL(WithBases(self, rec(
        N := N, n := n, ofs := ofs, v := v))).setDims(),
    #-----------------------------------------------------------------------
    dims := self >> [ _roundup(self.n, self.v), self.N ],
    #-----------------------------------------------------------------------
    area := self >> self.n,
    #-----------------------------------------------------------------------
    transpose := self >> VScat_pc(self.N, self.n, self.ofs, self.v),
    #-----------------------------------------------------------------------
    toAMat := self >> let(n := self.n, N := self.N, nv := _roundup(n, self.v),
        Scat(fAdd(nv, n, 0)).toAMat() * Gath(fAdd(N, n, self.ofs)).toAMat()),
    #-----------------------------------------------------------------------
    print :=  (self, i, is) >> Print(self.__name__, "(", self.N,
        ", ", self.n, ", ", self.ofs, ", ", self.v, ")", self.printA()),
));

#F ==========================================================================
#F IxVGath_pc(k, N, n, ofs, v)
#F
#F Performs multiple stacked VGath_pc operations
#F
#F IxVGath_pc(k, N, n, ofs, v) = I(k) tensor VGath_pc(N, n, ofs, v)
#F
#F See also: VGath_pc
#F
#F Example:
#F
#F spiral> PrintMat(MatSPL(VGath_pc(5, 3, 0, 4)));
#F [ [ 1,  ,  ,  ,   ],
#F   [  , 1,  ,  ,   ],
#F   [  ,  , 1,  ,   ],
#F   [  ,  ,  ,  ,   ] ]
#F
#F spiral> PrintMat(MatSPL(IxVGath_pc(2, 5, 3, 0, 4)));
#F [ [ 1,  ,  ,  ,  ,  ,  ,  ,  ,   ],
#F   [  , 1,  ,  ,  ,  ,  ,  ,  ,   ],
#F   [  ,  , 1,  ,  ,  ,  ,  ,  ,   ],
#F   [  ,  ,  ,  ,  ,  ,  ,  ,  ,   ],
#F   [  ,  ,  ,  ,  , 1,  ,  ,  ,   ],
#F   [  ,  ,  ,  ,  ,  , 1,  ,  ,   ],
#F   [  ,  ,  ,  ,  ,  ,  , 1,  ,   ],
#F   [  ,  ,  ,  ,  ,  ,  ,  ,  ,   ] ]
#F
#F ==========================================================================
Class(IxVGath_pc, BaseSVGath, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.k, self.N, self.n, self.ofs, self.v],
    rSetChild := rSetChildFields("k", "N", "n", "ofs", "v"),
    #-----------------------------------------------------------------------
    abbrevs := [],
    new := (self, k, N, n, ofs, v) >> SPL(WithBases(self, rec(
        k := k, N := N, n:= n, ofs := ofs, v := v))).setDims(),
    #-----------------------------------------------------------------------
    dims := self >> [ self.k*_roundup(self.n, self.v), self.k*self.N ],
    #-----------------------------------------------------------------------
    area := self >> self.k * self.n,
    #-----------------------------------------------------------------------
    transpose := self >> IxVScat_pc(self.k, self.N, self.n, self.ofs, self.v),
    #-----------------------------------------------------------------------
    toloop := (self, bksize) >> let(
	j := Ind(self.k),
	gath := VGath_pc(self.k*self.N, self.n, self.ofs + j*self.N, self.v),
	nvecs := Rows(gath) / self.v,
	ISum(j, j.range,
             VScat(HH(self.k*nvecs, nvecs, j*nvecs, [1]), self.v) *
	     gath).split(bksize)),
    #-----------------------------------------------------------------------
    toAMat := self >>
        Tensor(I(self.k), VGath_pc(self.N, self.n, self.ofs, self.v)).toAMat(),
    #-----------------------------------------------------------------------
    print :=  (self, i, is) >> Print(self.__name__, "(", self.k, ", ", self.N,
        ", ", self.n, ", ", self.ofs, ", ", self.v, ")", self.printA()),
));

#F ==========================================================================
#F IxRCVGath_pc(k, N, n, ofs, v)
#F   vector gather (read) matrix reading unaligned
#F   partial contiguous COMPLEX vectors
#F
#F   IxRCVGath_pc(..params..) == IxVGath_pc(..params..) tensor I(2)
#F
#F  Note: IxRCVGath_pc(k, N, n, ofs, v) == IxVGath_pc(k, 2*N, 2*n, 2*ofs, 2*v)
#F        observe 2*v inside IxVGath_pc.
#F
#F        IxVGath_pc(j, 2*N, 2*n, 2*ofs, v), however, is not exactly equal
#F        it has a different # of rows, if n < v/2
#F ==========================================================================
Class(IxRCVGath_pc, BaseSVGath, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.k, self.N, self.n, self.ofs, self.v],
    rSetChild := rSetChildFields("k", "N", "n", "ofs", "v"),
    #-----------------------------------------------------------------------
    abbrevs := [],
    new := (self, k, N, n, ofs, v) >> SPL(WithBases(self, rec(
        k := k, N := N, n := n, ofs := ofs, v := v))).setDims(),
    #-----------------------------------------------------------------------
    dims := self >> [ 2*self.k*_roundup(self.n, self.v),  2*self.k*self.N ],
    #-----------------------------------------------------------------------
    area := self >> 2 * self.k * self.n,
    #-----------------------------------------------------------------------
    transpose := self >> IxRCVScat_pc(self.k, self.N, self.n, self.ofs, self.v),
    #-----------------------------------------------------------------------
    toAMat := self >>
        Tensor(IxVGath_pc(self.k, self.N, self.n, self.ofs, self.v), I(2)).toAMat(),
    #-----------------------------------------------------------------------
    print :=  (self, i, is) >> Print(self.__name__, "(", self.k, ", ", self.N,
        ", ", self.n, ", ", self.ofs, ", ", self.v, ")", self.printA()),
));


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

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)
       [[1,1],[4,1],[6,1],[7,1],[8,1],[17,1],[18,1],[19,1]],           # E_8(a_3) e cel de mai jos dar ordonat
#       [[8,1],[18,1],[7,1],[6,1],[19,1],[1,1],[17,1],[4,1]],           # E_8(a_3)  merge si in char 5
#       [[7,1],[8,1],[9,1],[10,1],[11,1],[12,1],[13,1],[19,1]],         # E_8(a_3)
       [[1,1],[4,1],[6,1],[8,1],[17,1],[18,1],[19,1],[21,1]],          # E_8(a_4) e cel de mai jos dar ordonat
#       [[18,1],[6,1],[19,1],[1,1],[17,1],[21,1],[4,1],[8,1]],          # E_8(a_4) merge si in char 5
#       [[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
       [[1,1],[4,1],[7,1],[8,1],[14,1],[17,1],[19,1],[26,1]],          # E_8(b_4) e cel de mai jos dar ordonat
#       [[8,1],[7,1],[14,1],[26,1],[19,1],[1,1],[17,1],[4,1]],          # E_8(b_4) merge si in char 5
#       [[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)
       [[1,1],[12,1],[14,1],[15,1],[17,1],[21,1],[26,1],[27,1]],       # E_8(a_5) D_8(a_1) in a D_8: deduced from Seitz' E_8(a_6) used in proof
#       [[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) # works in char 5
       [[4,1],[7,1],[8,1],[18,1],[19,1],[23,1],[31,1],[33,1]],         # E_8(b_5) # e cel de sus ordonat
#       [[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) # works in char 5
       [[4,1],[7,1],[18,1],[19,1],[23,1],[29,1],[31,1],[33,1]],        # E_8(a_6) # e cel de sus ordonat
#       [[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)
#       [[4,1],[15,1],[18,1],[19,1],[23,1],[31,1],[33,1],[39,1]],       # E_8(b_6) # deduced from E_8(b_5) ?gp de componente?
       [[4,1],[8,1],[18,1],[19,1],[23,1],[31,1],[33,1],[39,-1],[41,1]], # E_8(b_6) # cel de mai jos ordonat
#       [[31,1],[33,1],[18,1],[4,1],[39,-1],[41,1],[8,1],[19,1],[23,1]], # E_8(b_6) # cu - merge si in char 5
#       [[4,1],[17,1],[12,1],[29,1],[30,1],[31,1],[34,1],[35,1]],        # E_8(b_6) Seitz' used in proof
#       [[15,1],[22,1],[23,1],[24,1],[25,1],[26,1],[27,1],[28,1]],       # E_8(b_6)
       [[1,1],[4,1],[6,1],[7,1],[17,1],[18,1],[19,1]],                  # E_7(a_3) e cel de mai jos dar ordonat
#       [[17,1],[1,1],[19,1],[6,1],[7,1],[18,1],[4,1]],                  # E_7(a_3) merge si in char 5
#       [[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)
       [[1,1],[4,1],[7,1],[14,1],[17,1],[18,1],[19,1],[26,1]],          # E_7(a_4) e cel de mai jos dar ordonat
#       [[17,1],[1,1],[19,1],[14,1],[26,1],[7,1],[18,1],[4,1]],          # E_7(a_4) merge si in char 5
#       [[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],[7,1],[23,1],[24,1],[25,1],[26,1],[27,1]],     # E_7(a_5) e cel de sus ordonat
#       [[4,1],[11,1],[14,1],[16,1],[24,1],[25,1],[26,1]],    # E_7(a_5)
       [[1,1],[4,1],[6,1],[8,1],[17,1],[18,1],[19,1]],       # E_6(a_3)A_1  e cel de mai jos dar ordonat
#       [[17,1],[1,1],[19,1],[6,1],[18,1],[4,1],[8,1]],       # E_6(a_3)A_1 merge si in char 5
#       [[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
       [[1,1],[4,1],[6,1],[17,1],[18,1],[19,1]],             # E_6(a_3)  e cel de mai jos dar ordonat
#       [[17,1],[1,1],[19,1],[6,1],[18,1],[4,1]],             # E_6(a_3) merge si in char 5
#       [[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 Ross'
#       [[2,1],[5,1],[10,1],[11,1],[7,1]],              # D_4(a_1)+A_1 Seitz'
       [[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,2,0,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,0,2,-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
    ];

#blocks:=[];
blocks:=[
    [[51, 1], [47, 1], [35, 1], [25, 4], [15, 1]],
    [[25, 9], [23, 1]],
    [[25, 7], [23, 1], [20, 2], [5, 2]],
    [[25, 5], [23, 2], [21, 1], [15, 2], [10, 2], [3, 2]],
    [[25, 3], [23, 1], [20, 2], [15, 4], [10, 4], [5, 2]],
    [[25, 4], [24, 2], [22, 2], [15, 2], [10, 2], [3, 1], [1, 3]],
    [[25, 2], [23, 1], [21, 1], [19, 1], [17, 2], [15, 2], [13, 1], [11, 3], [7, 2], [5, 1], [3, 2]],
    [[23, 2], [21, 1], [19, 1], [17, 1], [15, 3], [13, 2], [11, 3], [10, 2], [7, 1], [5, 1], [3, 3]],
    [[25, 2], [22, 2], [19, 1], [17, 1], [16, 2], [15, 1], [12, 2], [11, 2], [7, 1], [6, 2], [3, 1], [1, 3]],
    [[23, 1], [19, 2], [17, 3], [15, 3], [11, 1], [10, 4], [9, 1], [7, 2], [5, 1], [3, 4]],
    [[23, 1], [22, 2], [19, 1], [16, 2], [15, 1], [13, 3], [12, 2], [11, 1], [10, 2], [7, 1], [4, 2], [3, 1], [1, 3]],
    [[19, 2], [15, 5], [11, 1], [10, 8], [9, 1], [5, 7]],
    [[23, 1], [19, 1], [18, 2], [17, 1], [16, 2], [15, 2], [11, 1], [10, 4], [8, 2], [7, 1], [4, 2], [3, 2], [1, 3]],
    [[23, 1], [18, 2], [17, 3], [16, 2], [15, 1], [11, 1], [10, 2], [9, 3], [8, 2], [3, 2], [2, 4], [1, 3]],
    [[19, 1], [17, 2], [15, 3], [13, 1], [11, 3], [10, 6], [7, 2], [5, 4], [3, 3], [1, 1]],
    [[17, 1], [15, 3], [13, 2], [11, 3], [10, 6], [7, 3], [5, 8], [3, 2]],
    [[19, 1], [17, 1], [16, 2], [15, 2], [12, 2], [11, 2], [10, 6], [7, 1], [6, 2], [5, 3], [3, 1], [2, 2], [1, 3]],
    [[17, 1], [15, 1], [14, 2], [13, 2], [12, 2], [11, 1], [10, 4], [9, 2], [8, 2], [7, 1], [5, 7], [3, 2], [2, 2], [1, 1]],
    [[16, 2], [15, 1], [13, 3], [12, 2], [11, 1], [10, 2], [9, 3], [8, 4], [7, 1], [5, 7], [3, 1], [1, 3]],
    [[15, 1], [14, 2], [11, 1], [10, 10], [9, 2], [5, 15], [1, 1]],
    [[23, 1], [17, 7], [15, 1], [11, 1], [9, 7], [3, 1], [1, 14]],
    [[19, 1], [16, 4], [15, 1], [11, 6], [10, 4], [6, 4], [5, 2], [1, 10]],
    [[15, 1], [13, 2], [11, 3], [10, 8], [9, 1], [7, 2], [5, 11], [3, 5], [1, 1]],
    [[17, 1], [15, 1], [13, 6], [11, 1], [10, 2], [9, 6], [7, 1], [5, 7], [3, 1], [1, 8]],
    [[15, 1], [13, 1], [12, 2], [11, 2], [10, 8], [8, 2], [7, 1], [5, 10], [4, 2], [3, 2], [2, 2], [1, 3]],
    [[13, 3], [12, 2], [11, 1], [10, 2], [9, 3], [8, 4], [7, 5], [6, 4], [5, 3], [4, 2], [3, 2], [2, 2], [1, 3]],
    [[15, 1], [12, 4], [11, 1], [10, 6], [9, 4], [7, 1], [5, 10], [3, 5], [1, 6]],
    [[13, 3], [11, 5], [9, 3], [7, 13], [5, 3], [3, 5], [1, 6]],
    [[11, 1], [10, 6], [9, 3], [7, 5], [5, 20], [3, 5]],
    [[15, 1], [12, 2], [11, 5], [10, 4], [9, 3], [8, 2], [5, 10], [3, 1], [2, 6], [1, 6]],
    [[11, 1], [10, 6], [9, 1], [8, 4], [7, 2], [6, 2], [5, 18], [4, 2], [3, 3], [1, 3]],
    [[11, 1], [10, 4], [9, 3], [8, 4], [7, 3], [6, 2], [5, 17], [3, 3], [2, 4], [1, 3]],
    [[11, 1], [10, 6], [8, 4], [7, 5], [5, 17], [4, 4], [3, 1], [1, 6]],
    [[11, 1], [10, 2], [9, 3], [8, 6], [7, 4], [6, 4], [5, 10], [4, 2], [3, 7], [2, 4], [1, 3]],
    [[11, 1], [10, 4], [9, 3], [8, 2], [7, 4], [6, 6], [5, 13], [4, 2], [3, 1], [2, 4], [1, 6]],
    [[5, 48], [4, 2]],
    [[15, 1], [11, 9], [9, 7], [5, 10], [1, 21]],
    [[11, 1], [10, 2], [9, 7], [7, 7], [5, 17], [3, 2], [1, 14]],
    [[11, 1], [9, 6], [7, 14], [5, 7], [3, 14], [1, 8]],
    [[5, 46], [4, 2], [3, 2], [2, 2]],
    [[11, 1], [9, 3], [8, 6], [7, 8], [6, 6], [5, 3], [4, 2], [3, 7], [2, 10], [1, 6]],
    [[11, 1], [10, 2], [9, 7], [6, 14], [5, 9], [4, 2], [1, 17]],
    [[5, 46], [3, 5], [1, 3]],
    [[5, 45], [3, 4], [2, 4], [1, 3]],
    [[11, 1], [9, 1], [8, 8], [7, 8], [6, 8], [5, 1], [3, 8], [2, 8], [1, 15]],
    [[5, 38], [4, 12], [1, 10]],
    [[5, 45], [3, 1], [2, 6], [1, 8]],
    [[5, 36], [3, 20], [1, 8]],
    [[11, 1], [8, 6], [7, 14], [6, 6], [3, 2], [2, 14], [1, 21]],
    [[5, 32], [4, 8], [3, 10], [2, 10], [1, 6]],
    [[5, 45], [1, 23]],
    [[5, 30], [4, 8], [3, 13], [2, 8], [1, 11]],
    [[5, 29], [4, 6], [3, 14], [2, 14], [1, 9]],
    [[5, 25], [4, 10], [3, 14], [2, 14], [1, 13]],
    [[5, 18], [4, 12], [3, 20], [2, 20], [1, 10]],
    [[11, 1], [7, 26], [3, 1], [1, 52]],
    [[5, 29], [3, 25], [1, 28]],
    [[5, 21], [4, 16], [3, 9], [2, 14], [1, 24]],
    [[5, 14], [4, 14], [3, 23], [2, 18], [1, 17]],
    [[5, 14], [3, 50], [1, 28]],
    [[5, 7], [4, 14], [3, 28], [2, 28], [1, 17]],
    [[5, 13], [4, 32], [1, 55]],
    [[5, 3], [4, 16], [3, 27], [2, 32], [1, 24]],
    [[5, 1], [4, 12], [3, 32], [2, 32], [1, 35]],
    [[4, 8], [3, 28], [2, 48], [1, 36]],
    [[5, 1], [3, 55], [1, 78]],
    [[4, 2], [3, 27], [2, 52], [1, 55]],
    [[3, 14], [2, 64], [1, 78]],
    [[3, 1], [2, 56], [1, 133]]
    ];

#ords:=[];
ords:=[ 125, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 5, 25, 25,  25, 5, 25, 25, 5, 5, 25, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 25, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5 ];

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


#
# cls 15: A_2
#
handle15char2:=function()
    local tmp1,tmp2,o,info,rep,cent,u;
    o:=AllClasses(orbs)[15];
    info:=infos[15];

    rep:=Representative(o);
    #pr[7]=[0,0,1,1]-->[0,1,1,0]
    #pr[13]=[0,1,2,1]-->[1,1,2,0]
    tmp1:=ApplyRootsReflections(rep,[7,13]);
    u:=Unipotent(algebraicU(orbs),[[4,1]]);
    tmp1:=Conj(tmp1,u);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[7,13]);
    tmp2:=Conj(tmp2,u);

    return [tmp1,tmp2];
end;

#
# cls 14: \tilde A_2
#
handle14char2:=function()
    local tmp1,tmp2,o,info,rep,cent,u;
    o:=AllClasses(orbs)[14];
    info:=infos[14];

    rep:=Representative(o);
    #pr[8]=[1,0,1,1]-->[0,1,1,1]
    #pr[10]=[0,0,2,1]-->[0,1,2,0]
    # merge si [13,11],[19,20]
    tmp1:=ApplyRootsReflections(rep,[8,10]);
    u:=Unipotent(chevalleyAdj(rep),[[3,1]],Ordering(rep));
    tmp1:=Conj(tmp1,u);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[8,10]);
    u:=Unipotent(chevalleyAdj(rep),[[3,1]],Ordering(cent));
    tmp2:=Conj(tmp2,u);

    return [tmp1,tmp2];
end;

#
# cls 12: B_2
#
handle12char2:=function()
    local tmp1,tmp2,o,info,rep,cent;
    o:=AllClasses(orbs)[12];
    info:=infos[12];

    rep:=Representative(o);
    tmp1:=ApplyRootsReflections(rep,[9]);#pr[9]=[0,1,1,1]-->[1,1,1,0]
    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[9]);#pr[9]=[0,1,1,1]-->[1,1,1,0]
    return [tmp1,tmp2];
end;

#
# cls 11: B_2^{(2)} [6,12,22]
#
# here there is no component group
#
handle11char2:=function()
    local rep,cc,zz,uu;

    rep:=Unipotent(alg,[[6,1],[12,1],[22,1]]);
    cc:=UnipotentCentralizer(rep);
    zz:=Zentrum(cc[1]);
    uu:=Unipotent(alg,[[1,1]],Ordering(rep));# A_1 the reductive part of the Levi

    return [rep,cc,zz,uu,TorusWeights(zz,[1])];
end;

#
# cls 8: C_3(a_1)
#
# here there is no component group
#
handle8char2:=function()
    local o,rep,cc,ccc,zz,zzz,uu,dist_par;

    o:=AllClasses(orbs)[8];
    rep:=BorelRep(o);
    vals:=ShallowCopy(avars);
    vals[1]:=Zero(APR);
    vals[2]:=Zero(APR);
    vals[5]:=Zero(APR);
    vals[6]:=Zero(APR);
    vals[8]:=Zero(APR);
    vals[12]:=vals[11];
    vals[15]:=vals[9];
    vals[18]:=vals[13];
    vals[20]:=vals[16];
    cc:=Value(generic,avars,vals);
    # a_9+a_9^2+a_11^2=0
    #ccc:=Value(cc,[avars[9]],[Zero(APR)]); NU: a_9=0 <=> a_11=0

    zz:=Zentrum(infos[8][1]); # centrul centralizatorului calculat inainte
    dist_par:=ToPositiveBorel(o,0Level(DistinguishedParabolic(o)));
    zzz:=Subword(zz,dist_par);
#    uu:=Unipotent(alg,[[1,1]],Ordering(rep));# A_1 the reductive part of the Levi

    return [cc,zz,zzz];
end;

##
## cls 7: C_3(a_1)
##
#handle7char3:=function()
#    local tmp1,tmp2,o,info,rep,cent;
#    o:=AllClasses(orbs)[7];
#    info:=infos[7];
#
#    rep:=Representative(o);
#    tmp1:=ConjugateByTorus(rep,1,-1);# h_1 --> h_4
#    cent:=FromPositiveBorel(o,info[5]);
#    tmp2:=ConjugateByTorus(cent,1,-1);# h_1 --> h_4
#    return [tmp1,tmp2];
#end;

#
# cls 6: F_4(a_3) [4,6,10,18] (finished?)
#
handle6char2:=function()
    local tmp1,tmp2,tmp3,tmp4,tmp5,tmp6,o,info,rep,cent,uu;
    o:=AllClasses(orbs)[6];
    info:=infos[6];

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

    tmp3:=ApplyRootsReflections(rep,[5,1]);
    uu:=Unipotent(chevalleyAdj(rep),[[2,1]],Ordering(rep));#[1..24]);
    tmp3:=Conj(tmp3,uu);
    tmp3:=ConjNegUa(tmp3,2,1);
    uu:=Unipotent(chevalleyAdj(rep),[[6,1],[4,1]],Ordering(rep));
    tmp3:=Conj(tmp3,uu);

    #tmp5:=ConjugateByTori(rep,[3],[-1]);

    cent:=FromPositiveBorel(o,info[2]);
    tmp2:=ApplyRootsReflections(cent,[3]);#pr[3]=[0,0,1,0]-->[0,0,1,0]
    uu:=Unipotent(chevalleyAdj(cent),[[2,-1],[6,1]]);
    tmp2:=Conj(tmp2,uu);
    #tmp2:=ConjugateByTori(tmp2,[1,2,3,4],[1,-1,aaa,aaa^2]);

    tmp4:=ApplyRootsReflections(cent,[5,1]);
    uu:=Unipotent(chevalleyAdj(rep),[[2,1]],Ordering(cent));#[1..24]);
    tmp4:=Conj(tmp4,uu);
    tmp4:=ConjNegUa(tmp4,2,1);
    uu:=Unipotent(chevalleyAdj(rep),[[6,1],[4,1]],Ordering(cent));
    tmp4:=Conj(tmp4,uu);
    #tmp4:=ConjugateByTori(tmp4,[3],[aaa]);

    #tmp6:=ConjugateByTori(cent,[3],[-1]);

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

#
# cls 3: F_4(a_2) (finished?)
#
handle3char2:=function()
    local tmp1,tmp2,o,info,rep,cent,uu,
    avars,vals,comp1,comp2;
    o:=AllClasses(orbs)[3];
    info:=infos[3];

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

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

    avars:=IndeterminatesOfPolynomialRing(ring(chevalleyAdj(rep)));
    vals:=ShallowCopy(avars);
    vals[1]:=One(APR);
    vals[2]:=Zero(APR);
    vals[3]:=Zero(APR);
    vals[4]:=vals[7];
    vals[6]:=vals[7]+One(APR);
    vals[9]:=vals[7];
    vals[5]:=vals[6]+vals[9];
    vals[10]:=One(APR)+vals[7];
    vals[11]:=vals[16]+vals[7]+vals[7]^2;
    vals[12]:=vals[7]*vals[9]+vals[7]+vals[16];
    vals[13]:=vals[9];
    vals[15]:=vals[16]+vals[7]^2;
    vals[17]:=vals[19]+vals[16]+vals[7]+vals[7]^2;
    vals[18]:=vals[16]+vals[7]+vals[7]^2;
    vals[20]:=vals[16]^2+vals[7]+vals[7]^4;
    vals[22]:=vals[7]^4+vals[7]^2+vals[16]^2+vals[7]+vals[16];
    comp1:=Value(generic,avars,vals);

    vals:=ShallowCopy(avars);
    vals[1]:=Zero(APR);
    vals[2]:=Zero(APR);
    vals[3]:=Zero(APR);
    vals[7]:=vals[4];
    vals[6]:=vals[7];
    vals[9]:=vals[7];
    vals[5]:=vals[6]+vals[9];
    vals[10]:=vals[7];
    vals[13]:=vals[9];
    vals[12]:=vals[7]*vals[9]+vals[16];
    vals[11]:=vals[16]+vals[7]^2;
    vals[15]:=vals[16]+vals[7]^2;
    vals[17]:=vals[19]+vals[16]+vals[7]^2;
    vals[18]:=vals[16]+vals[7]^2;
    vals[20]:=vals[16]^2+vals[7]^4;
    vals[22]:=vals[7]^4+vals[7]^2+vals[16]^2+vals[16];
    comp2:=Value(generic,avars,vals);

    return [comp1,comp2,tmp1,tmp2];
end;
#vals:=ShallowCopy(avars);
#vals[1]:=One(APR);
#vals[2]:=Zero(APR);
#vals[3]:=Zero(APR);
#vals[4]:=vals[7];
#vals[6]:=vals[7]+One(APR);
#vals[9]:=vals[7];
#vals[5]:=vals[6]+vals[9];
#vals[10]:=One(APR)+vals[7];
#vals[11]:=vals[16]+vals[7]+vals[7]^2;
#vals[12]:=vals[7]*vals[9]+vals[7]+vals[16];
#vals[13]:=vals[9];
#vals[15]:=vals[16]+vals[7]^2;
#vals[17]:=vals[19]+vals[16]+vals[7]+vals[7]^2;
#vals[18]:=vals[16]+vals[7]+vals[7]^2;
#vals[20]:=vals[16]^2+vals[7]+vals[7]^4;
#vals[22]:=vals[7]^4+vals[7]^2+vals[16]^2+vals[7]+vals[16];
#
#xx:=Value(generic,avars,vals);
#vals:=List([1..Length(avars)],i->Zero(APR));
#vals[7]:=One(APR);
#xx0:=Value(xx,avars,vals);
#xxx:=Value(cc[1],[avars[1],avars[2],avars[3],avars[4],avars[5]],[Zero(APR),Zero(APR),Zero(APR),avars[7]^2+avars[8]*avars[7]+a#vars[7]*avars[14]+One(APR),One(APR)]);
#
#vals:=ShallowCopy(avars);
#vals[1]:=Zero(APR);
#vals[2]:=Zero(APR);
#vals[3]:=Zero(APR);
#vals[7]:=vals[4];
#vals[6]:=vals[7];
#vals[9]:=vals[7];
#vals[5]:=vals[6]+vals[9];
#vals[10]:=vals[7];
#vals[13]:=vals[9];
#vals[12]:=vals[7]*vals[9]+vals[16];
#vals[11]:=vals[16]+vals[7]^2;
#vals[15]:=vals[16]+vals[7]^2;
#vals[17]:=vals[19]+vals[16]+vals[7]^2;
#vals[18]:=vals[16]+vals[7]^2;
#vals[20]:=vals[16]^2+vals[7]^4;
#vals[22]:=vals[7]^4+vals[7]^2+vals[16]^2+vals[16];
##Print("ok");
#yy:=Value(generic,avars,vals);
#vals[8]:=avars[1];#
#vals[16]:=avars[2];#
#vals[14]:=avars[3];#
#vals[19]:=avars[5];#
#vals[21]:=avars[6];#
#vals[23]:=avars[9];#
#vals[24]:=avars[10];#
##vals:=ShallowCopy(avars);
#yy0:=Value(yy,avars,vals);
#yyy:=Value(cc[1],[avars[1],avars[2],avars[3],avars[4],avars[5]],[Zero(APR),Zero(APR),Zero(APR),avars[8]*avars[7]+avars[7]*avars[14],Zero(APR)]);
#
#vals:=List([1..Length(avars)],i->Zero(APR));
#vals[4]:=One(APR);
#vals[16]:=One(APR);
#yy1:=Value(yy,avars,vals);

#
# cls 2: F_4(a_1)
#
handle2char2:=function()
    local tmp1,tmp2,o,info,rep,cent,uu;
    o:=AllClasses(orbs)[2];
    info:=infos[2];

    rep:=Representative(o);
    tmp1:=ConjugateByTori(rep,[3,1],[-1,-1]);# h_1 --> h_4, h_3 --> h_3
    uu:=Unipotent(chevalleyAdj(rep),[[3,-1]],Ordering(rep));#pr[3]=[0,0,1,0]-->[0,0,1,0]
    tmp1:=Conj(tmp1,uu);

    cent:=FromPositiveBorel(o,info[5]);
    tmp2:=ConjugateByTori(cent,[3,1],[-1,-1]);# h_1 --> h_4, h_3 --> h_3
    uu:=Unipotent(chevalleyAdj(cent),[[3,-1]],Ordering(cent));#pr[3]=[0,0,1,0]-->[0,0,1,0]
    tmp2:=Conj(tmp2,uu);

    return [tmp1,tmp2];
end;