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

#F WHT( <log2(size)> ) - Walsh Hadamard Transform non-terminal
#F Definition: (2^n x 2^n)-matrix  DFT_2 tensor ... tensor DFT_2 (n-fold)
#F Note:       DFT_2 denotes the matrix [[1, 1], [1, -1]],
#F             WHT is symmetric.
#F Example:    WHT(3)
#F
#F Tweakable parameters: WHT_GeneralSplit.maxSize,
#F                       WHT_BinSplit.minSize
#F                       WHT_DDL.minSize
#F
#F WHT_GeneralSplit is a generalization of BinSplit, so it only makes
#F sense to have WHT_BinSplit.minSize := WHT_GeneralSplit.maxSize + 1
#F (not to have 2 rules enabled at the same time)
#F
#F By default, WHT_GeneralSplit is disabled.
#
Class(WHT, TaggedNonTerminal, rec(
  abbrevs   := [ n -> Checked(IsPosInt(n), [n]) ],
  dims      := self >> let(size := 2^self.params[1], [size, size]),
  terminate := self >> When(self.params[1] = 1, F(2), Tensor(Replicate(self.params[1], F(2)))),
  transpose := self >> Copy(self),
  isReal    := self >> true,
  SmallRandom := () -> Random([2..5]),
  LargeRandom := () -> Random([6..15]),
  normalizedArithCost := self >> self.params[1]*2^self.params[1],
  TType := T_Real(64)
));

# NOTE: A lot of WHT rules were moved to paradigms/common/wht.g, UGLY!
#
NewRulesFor(WHT, rec(
    #F WHT_Base: WHT_(2^1) = F_2
    #F
    WHT_Base := rec(
        info             := "WHT_(2^1) -> F_2",
        forTransposition := false,
        applicable       := (self, nt) >> nt.params[1]=1 and not nt.hasTags(),
        apply            := (nt, c, cnt) -> F(2),
    ),

    #F WHT_Base2: WHT_(2^1) = F_2
    # Added by Kyle 7-11-07
    # Need a better way to represent base cases for WHT so it is not fully expanded.
    WHT_Base2 := rec(
        info             := "WHT_(2^2) -> F_2",
        forTransposition := false,
        switch           := false,
        applicable       := (self, nt) >> nt.params[1]=2 and not nt.hasTags(),
        apply            := (nt, c, cnt) -> WHT(2),

    ),

    #F WHT_GeneralSplit: follows from definition
    #F
    #F   WHT_(2^k) =
    #F                        WHT_(2^k1) tensor I_(2^(k-k1)) *
    #F     I_(2^k1)    tensor WHT_(2^k2) tensor I_(2^(k-k1-k2)) *
    #F       ...
    #F       ...
    #F     I_(2^(k-kr)) tensor WHT_(2^kr)
    #F
    #F This rule has been restricted to small sizes
    #F
    WHT_GeneralSplit := rec (
        info             := "WHT_(2^k) -> (WHT_(2^k1) tensor I) .. (I tensor WHT_(2^kr))",
        forTransposition := false,
        switch           := false,
        maxSize          := 7,
        applicable       := (self, nt) >> nt.params[1] <> 1 and nt.params[1] <= self.maxSize,

        children := nt -> let(C := DropLast(OrderedPartitions(nt.params[1]), 1),
            List(C, p -> List(p, WHT))),

        # MRT 10-2007: .randomChildren does appear to be called in a certain case during RandomRuleTree()
        # but is it necessary? It only appears here!
        randomChildren := function ( Pin )
            local C, sum, rand, P;
            P := Pin[1];
            C := [];
            sum := 0;
            while sum < P do
                if sum = 0 then  rand := RandomList( [1..(P-1)] );
                else             rand := RandomList( [1..(P-sum)] );
                fi;
                Add( C, rand );
                sum := sum + rand;
            od;
            When(not (Sum(C)=P and Length(C) > 1),
                Error( "WHT_GeneralSplit.randomChildren has a bug"));
            return List(C, WHT);
        end,

        rule := function ( P, C )
            local P, left, right, i;
            right := 2^P / C[1].dimensions[1];
            left  := 1;
            P := [ Tensor(C[1], I(right)) ];

            # list of factors
            for i in [2..Length(C)-1] do
                left  := left * C[i-1].dimensions[1];
                right := right/C[i].dimensions[1];
                Add(P, Tensor(I(left), C[i], I(right)));
            od;
            left := left * C[Length(C)-1].dimensions[1];
            Add(P, Tensor(I(left), C[Length(C)]));

            return Compose(P); # return product
        end
    ),

    WHT_BinSplit_binloops := rec (
        info             := "WHT_(2^k) -> (WHT_(2^k1) tensor I) (I tensor WHT_(2^k2))",
        forTransposition := false,
        minSize          := 2,
        applicable       := (self, nt) >> nt.params[1] >= self.minSize and not nt.hasTags(),

        children := nt -> List([1..nt.params[1]-1], i -> [ WHT(i), WHT(nt.params[1]-i) ] ),

        apply := function(nt, c, cnt)
            local r1, r2, a, i, b;

            r1 := Rows(c[1]);
            r2 := Rows(c[2]);

            a := c[1];
            for i in [1..Log2Int(r2)] do
                a := Grp(Tensor(I(2), a));
            od;
            a := Grp(L(r1 * r2, r1) * a * L(r1 * r2, r2));

            b := c[2];
            for i in [1..Log2Int(r1)] do
                b := Grp(Tensor(I(2), b));
            od;

            return a * b;
        end,

        switch := true,
    )
));


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

########################################################################
#   (A x B) rules
NewRulesFor(TTensor, rec(
#   (A x B) -> L (B x I)(L(A x I))
    splitL_BxI__L_AxI := rec(
        info := "(A x B) -> L (B x I)(L(A x I))",
        forTransposition := false,
        applicable := nt -> let(P := nt.params,
            Rows(P[1]) = Cols(P[1])
            and Rows(P[2]) = Cols(P[2])
        ),
        children := nt -> let(P := nt.params,
            [[TCompose([TTensorI(P[2], P[1].dims()[1], AVec, AVec), TTensorI(P[1], P[2].dims()[2], APar, AVec)]).withTags(nt.getTags())]]
        ),
        apply := (t, C, nt) -> let(
            p:=nt[1].params[1][1],
            mn := Rows(nt[1]),
            n := p.params[2],
            DelayedPrm(L(mn, n)) * C[1]
        )

#D        isApplicable := P -> Rows(P[1]) = Cols(P[1]) and Rows(P[2]) = Cols(P[2]),
#D        allChildren := P -> [[TCompose([TTensorI(P[2], P[1].dims()[1], AVec, AVec), TTensorI(P[1], P[2].dims()[2], APar, AVec)], P[3])]],
#D        rule := (P, C, nt) -> let(p:=nt[1].params[1][1], mn := Rows(nt[1]), n := p.params[2], DelayedPrm(L(mn, n)) * C[1])
    ),
#   (A x B) -> ((A x I)L)(B x I) L
    AxI_L__BxI_splitL := rec(
        info := "(A x B) -> ((A x I)L)(B x I) L",
        forTransposition := false,

        applicable := nt -> let(P := nt.params,
            Rows(P[1]) = Cols(P[1])
            and Rows(P[2]) = Cols(P[2])
        ),

        children := nt -> let(P := nt.params,
            [[TCompose([TTensorI(P[1], P[2].dims()[1], AVec, APar), TTensorI(P[2], P[1].dims()[2], AVec, AVec)]).withTags(nt.getTags())]]
        ),

        apply := (t, C, nt) -> let(
            p := nt[1].params[1][1],
            mn := Rows(nt[1]),
            n := Rows(p.params[1]),
            C[1] * DelayedPrm(L(mn, n))
        )
#D        isApplicable := P -> Rows(P[1]) = Cols(P[1]) and Rows(P[2]) = Cols(P[2]),
#D        allChildren := P -> [[TCompose([TTensorI(P[1], P[2].dims()[1], AVec, APar), TTensorI(P[2], P[1].dims()[2], AVec, AVec)], P[3])]],
#D        rule := (P, C, nt) -> let(p:=nt[1].params[1][1], mn := Rows(nt[1]), n := Rows(p.params[1]), C[1] * DelayedPrm(L(mn, n)))
    ),
));


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

ImportAll(spiral);

SkewRDFT := BSkewPRDFT;
BRDFT := PolyBDFT;

alphai := (n,i,a) -> Cond(i mod 2 = 0, (a + Int(i/2))/n, (1-a + Int(i/2))/n);

time_r := a -> [[1, -CosPi(2*a)/SinPi(2*a)],
                [0, 1/SinPi(2*a)]];

r_time := a -> [[1, CosPi(2*a)],
                [0, SinPi(2*a)]];

B1_time_r := n -> Cond(IsOddInt(n),
    DirectSum(I(1), List([1..(n-1)/2], i-> Mat(time_r(i/n)))),
    DirectSum(I(1), List([1..n/2-1], i-> Mat(time_r(i/n))), I(1)));

RDFT1 := SymFunc("RDFT1", n -> Cond(
	n=2,F(2),
	IsEvenInt(n), VStack(Gath(H(n+2,1,0,1)), Gath(H(n+2,n-2,2,1)), Gath(H(n+2,1,n,1))) * PRDFT1(n),
	IsOddInt(n), VStack(Gath(H(n+1,1,0,1)), Gath(H(n+1,n-1,2,1))) * PRDFT1(n)));

RDFT3 := SymFunc("RDFT3", n -> Cond(
	n=2,I(2),
	IsEvenInt(n), PRDFT3(n),
	IsOddInt(n), VStack(Gath(H(n+1,n-1,0,1)), Gath(H(n+1,1,n-1,1))) * PRDFT3(n)));

BRDFT1 := SymFunc("BRDFT1", n -> B1_time_r(n) * RDFT1(n));

rdft1 := (k,m) ->
   Perm(PermFunc(rP(2*k*m, 2*k), 2*k*m), 2*k*m).transpose() *
   DirectSum(RDFT1(m), List([1..k-1], i->Mat(MatrDFT(2*m,i/(2*k)))), RDFT3(m)) *
   Tensor(RDFT1(2*k), I(m));

rdft1odd := (k,m) ->
   Perm(PermFunc(rPhat((2*k-1)*m, 2*k-1), (2*k-1)*m), (2*k-1)*m).transpose() *
   DirectSum(RDFT1(m), List([1..k-1], i->Mat(MatrDFT(2*m,i/(2*k-1))))) *
   Tensor(RDFT1(2*k-1), I(m));

test_rdft1 := (k,m) -> PrintLine(inf_norm(MatSPL(rdft1(k,m)) - MatSPL(RDFT1(2*k*m))));
test_rdft1odd := (k,m) -> PrintLine(inf_norm(MatSPL(rdft1odd(k,m)) - MatSPL(RDFT1((2*k-1)*m))));
do_test_rdft1 := function()
   test_rdft1(4,4);
   test_rdft1(4,8);
   test_rdft1(6,8);
   test_rdft1(6,2);
   test_rdft1(6,5);
   test_rdft1(11,5);
end;
do_test_rdft1odd := function()
   test_rdft1odd(4,4);
   test_rdft1odd(4,8);
   test_rdft1odd(6,8);
   test_rdft1odd(6,2);
   test_rdft1odd(6,5);
   test_rdft1odd(5,5);
   test_rdft1odd(11,5);
end;

rdft3 := (k,m) ->
   RC(K(k*m, m)) *
   DirectSum(List([0..k-1], i->Mat(MatrDFT(2*m,(i+1/2)/(2*k))))) * #alphai(k,i,1/4))))) *
   Tensor(RDFT3(2*k),I(m));

rdft3odd := (k,m) ->
   Perm(PermFunc(rQhat((2*k-1)*m, 2*k-1), (2*k-1)*m), (2*k-1)*m).transpose() *
   DirectSum(List([0..k-2], i->Mat(MatrDFT(2*m,(i+1/2)/(2*k-1)))), RDFT3(m)) * #alphai(k,i,1/4))))) *
   Tensor(RDFT3(2*k-1),I(m));

test_rdft3 := (k,m) -> PrintLine(inf_norm(MatSPL(rdft3(k,m)) - MatSPL(RDFT3(2*k*m))));
test_rdft3odd := (k,m) -> PrintLine(inf_norm(MatSPL(rdft3odd(k,m)) - MatSPL(RDFT3((2*k-1)*m))));
do_test_rdft3 := function()
   test_rdft3(4,4);
   test_rdft3(4,8);
   test_rdft3(6,8);
   test_rdft3(6,2);
   test_rdft3(6,5);
   test_rdft3(11,5);
end;
do_test_rdft3odd := function()
   test_rdft3odd(4,4);
   test_rdft3odd(4,8);
   test_rdft3odd(6,8);
   test_rdft3odd(6,2);
   test_rdft3odd(6,5);
   test_rdft3odd(5,5);
   test_rdft3odd(11,5);
end;

rrdft := (k,m,a) ->
   RC(K(k*m, m)) *
   DirectSum(List([0..k-1], i->Mat(MatrDFT(2*m,(i+a)/(2*k))))) *
   Tensor(Mat(MatrDFT(2*k,a)),I(m));

brdft := (k,m,a) ->
   RC(K(k*m, m)) *
   DirectSum(List([0..k-1], i->BRDFT(2*m,alphai(k,i,a)))) *
   Tensor(BRDFT(2*k, a), I(m));

bruun_rdft3 := (k,m) ->
   RC(K(k*m, m)) *
   DirectSum(List([0..k-1], i->SkewRDFT(2*m,alphai(k,i,a)))) *
   Tensor(BRDFT(2*k,1/4),I(m));

bruun_rdft1 := (k,m) ->
   DirectSum(RDFT1(m), List([1..k-1], i->SkewRDFT(2*m,i/(2*k))), RDFT3(m)) *
   Tensor(BRDFT1(2*k), I(m));

rcred := mat -> List(mat{[0..Length(mat)/2-1]*2+1}, row -> row{[0..Length(row)/2-1]*2+1});


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

Class(InterpolateDFT, TaggedNonTerminal, rec(
    abbrevs := [ (dsfunc,usfunc) -> [dsfunc, usfunc] ],
    dims      := self >> [self.params[1].domain(), self.params[2].domain()],
    terminate := self >> let(n:=self.params[2].domain(), N := self.params[1].range(),
        Downsample(self.params[1]).terminate() *
        DFT(N, 1).terminate() *
        Upsample(self.params[2]).terminate() *
        Scale(1/n, DFT(n, -1).terminate())),
    isReal    := False,
    normalizedArithCost := self >> let(n:=self.params[2].domain(), N := self.params[1].range(),
        n + IntDouble(5 * n * d_log(n) / d_log(2)) + IntDouble(5 * N * d_log(N) / d_log(2))),
    TType := T_Complex(T_Real(64))
));

NewRulesFor(InterpolateDFT, rec(
    InterpolateDFT_base := rec(
        applicable := (self, nt) >> not nt.hasTags(),
        children := nt -> let(n:=nt.params[2].domain(), N := nt.params[1].range(),
            [[ Downsample(nt.params[1]), DFT(N, 1), Upsample(nt.params[2]), DFT(n, -1) ]]),
        apply := (nt, C, cnt) -> C[1] * C[2] * C[3] * Scale(1/nt.params[2].domain(), C[4])
    ),
    InterpolateDFT_PrunedDFT := rec(
        applicable := (self, nt) >> not nt.hasTags() and ObjId(nt.params[2]) = fZeroPadMiddle,
        children := nt -> let(n:=nt.params[2].domain(), N := nt.params[1].range(), us := N/n, blk := n/2,
            [[ PrunedDFT(N, 1, blk, [0, 2*us-1]), DFT(n, -1) ]]),
        apply := (nt, C, cnt) -> let(n:=nt.params[2].domain(), N := nt.params[1].range(), us := N/n, blk := n/2,
                Gath(nt.params[1]) * C[1] * Scale(1/n, C[2])
       )
    )
));


handle:=function(alg)
    local orbs,centralizers,centres;
    orbs:=UnipotentClasses(alg,"");
    centralizers:=List(Classes(orbs),i->UnipotentCentralizer(BorelRep(i)));

    return centralizers;
end;

handleClass:=function(orb)
    local rep,dist_par,co_dist_par,centralizer,center,sys,w,decomp,supp_roots,
    in_borel,not_in_borel,k,
    p,dimensions;

    Print(Label(orb),"\n");
#    orb:=Classes(orbs)[i];
    dist_par:=DistinguishedParabolic(orb);
    rep:=BorelRep(orb);
    centralizer:=UnipotentCentralizer(rep);
    center:=Zentrum(centralizer[1]);
    sys:=chevalleyAdj(rep);
    w:=Witt(LogInt(Order(rep),Characteristic(sys)),sys);
    decomp:=WittDecomposition(center,w);
    supp_roots:=List(decomp,i->List(coefficients(i),j->j[1]));
    dist_par:=ToPositiveBorel(orb,0Level(dist_par));
    co_dist_par:=Filtered([1..Length(positiveRoots(sys))],i->not i in dist_par);

    in_borel:=[];
    not_in_borel:=[];
    for k in [1..Length(decomp)] do
        if IsSubset(dist_par,supp_roots[k]) then Add(in_borel,k);
        elif IsSubset(co_dist_par,supp_roots[k]) then Add(not_in_borel,k);
        else Print("handleClass: the centre is skew !!!\n"); fi;
    od;

    #p:=Characteristic(sys);

    return [centralizer,center,decomp,[in_borel,not_in_borel],dist_par];
end;

handleClassShort:=function(orb)
    local rep,dist_par,co_dist_par,centralizer,center,sys,w,decomp,supp_roots,
    in_borel,not_in_borel,k,
    p,dimensions;

    Print(Label(orb),"\n");
#    orb:=Classes(orbs)[i];
    dist_par:=DistinguishedParabolic(orb);
    rep:=BorelRep(orb);
    centralizer:=UnipotentCentralizer(rep);
    center:=Zentrum(centralizer[1]);

    decomp:=ParameterPieces(center);
    sys:=chevalleyAdj(rep);
    supp_roots:=List(decomp,i->List(coefficients(i),j->j[1]));
    dist_par:=ToPositiveBorel(orb,0Level(dist_par));
    co_dist_par:=Filtered([1..Length(positiveRoots(sys))],i->not i in dist_par);

    #in_borel:=[];
    #not_in_borel:=[];
    #Error("1");
    for k in [1..Length(decomp)] do
        if not IsSubset(dist_par,supp_roots[k]) and not IsSubset(co_dist_par,supp_roots[k]) then
            Print("handleClass: the centre is skew !!!\n");
        fi;
    od;

    return [centralizer[1],Subword(center,dist_par),center];
#    return [Dimension(centralizer[1]),Dimension(Subword(center,dist_par))];
end;

handleClassesShort:=function(orb)
    local o,info,fis_tmp;

    fis_tmp:=Concatenation("~/Workspace/ChevalleyAdj/short",String(Characteristic(algebraicU(orb))),".txt");

    PrintTo(fis_tmp,"");

    for o in Classes(orb) do
        Print(Label(o),"\n");
        info:=handleClassShort(o);
        latexClassShort(o,info);
        #AppendTo(fis_tmp,Label(o)," ",info[1]," ",info[2],"\n");
    od;
end;

#-------------------------------------------------------------------------------

#"reflect unipotent element by w_root's: w_r*x_s*w_r^-1",
ApplyRootsReflections:=function(u,roots)
              local result,
              sys,perm_mat,R,pr,eta,W,ref_mats,ref_mat,root,sign,i,coeffs,check;

              sys:=chevalleyAdj(u);
              #R:=rootSystem(chevalleyAdj(u));
              pr:=positiveRoots(sys);
              #eta:=Eta(R);
              #W:=weylGroup(sys);
              #ref_mats:=RootReflections(W);
              #ref_mat:=Product(ref_mats{roots});
              #perm_matrix:=Inverse(PermutationMatrix(weylGroup(sys),perm));

              #??perm_mat:=Inverse(PermutationMatrix(weylGroup(sys),roots)); ? De ce am inversat aiciasta?
              perm_mat:=PermutationMatrix(weylGroup(sys),roots);

              result:=[];
              coeffs:=coefficients(u);
              for i in coeffs do
                  root:=Position(pr,pr[i[1]]*perm_mat);
                  sign:=PermutationSign(sys,roots,i[1]);
                  result:=Concatenation(result,[[root,sign*i[2]]]);
              od;
#              result:=List([1..Length(result)],i->[Position(pr,ref_mats[root]*pr[result[i][1]]),
#                                                   eta[root][result[i][1]]*result[i][2]]);

#return result;

              check:=[];
              for i in [1..Length(result)] do
                  if result[i][1]=fail then
                      check:=Concatenation(check,[coeffs[i][1]]);
                  fi;
              od;

              if check<>[] then return Concatenation(String(check)," nu ramane pozitiv\n");
              else
                  result:=Unipotent(sys,result,Ordering(u));
                  if HasOrder(u) then SetOrder(result,Order(u)); fi;
                  return result;
              fi;
end;

ConjugateByTorus:=function(u,root,t)
    local result,coeffs,sys,A_rs;
    sys:=chevalleyAdj(u);
    A_rs:=A(sys);
    coeffs:=coefficients(u);
    coeffs:=List([1..Length(coeffs)],i->[coeffs[i][1],t^A_rs[root][coeffs[i][1]]*coeffs[i][2]]);
    result:=Unipotent(sys,coeffs,Ordering(u));
    if HasOrder(u) then SetOrder(result,Order(u)); fi;
    return result;
end;

ConjugateByTori:=function(u,roots,ts)
    local result,coeffs,sys,A_rs,j;
    sys:=chevalleyAdj(u);
    A_rs:=A(sys);
    coeffs:=coefficients(u);
    for j in [1..Length(roots)] do
        coeffs:=List([1..Length(coeffs)],i->[coeffs[i][1],ts[j]^A_rs[roots[j]][coeffs[i][1]]*coeffs[i][2]]);
    od;
    result:=Unipotent(sys,coeffs,Ordering(u));
    if HasOrder(u) then SetOrder(result,Order(u)); fi;
    return result;
end;

TorusWeight:=function(u,root)
    local result,coeffs,sys,A_rs;
    sys:=chevalleyAdj(u);
    A_rs:=A(sys);
    coeffs:=coefficients(u);
    result:=List([1..Length(coeffs)],i->A_rs[root][coeffs[i][1]]);#???
    return result;
end;

TorusWeight:=function(sys,ts,root)
    local result,A_rs,i;
    A_rs:=A(sys);
    result:=List([1..Length(ts)],i->A_rs[ts[i]][root]);
#    result:=[];
#    for i in [1..Length(ts)] do
#        if ts[i]=0 then Add(result,0);
#        else Add(result,A_rs[ts[i]][root]); #Add(result,A_rs[ts[i]][root]);
#        fi;
#    od;
    return result;
end;

TorusWeights:=function(ts_pow,u)
    local result,u_roots,sys,r,ts;
    sys:=chevalleyAdj(u);
    u_roots:=List(coefficients(u),i->i[1]);
    ts:=Concatenation(List([1..Length(ts_pow)],i->List([1..ts_pow[i]],j->i)));
    Print(ts);

    result:=[];
    for r in u_roots do
        #Add(result,List([1..Length(roots)],i->A_rs[roots[i]][coeffs[j][1]])); !!! gresit
        Add(result,TorusWeight(sys,ts,r));
    od;
    return result;
end;

Subsystem:=function(u)
    local result,roots,flag,i,j,pos,AA;

    roots:=allRoots(chevalleyAdj(u));
    AA:=A(chevalleyAdj(u));
    result:=List(coefficients(u),i->roots[i[1]]);
    result:=Concatenation(result,-result);
    result:=List(result,i->Position(roots,i));

    flag:=true;
    while flag do
        flag:=false;
        for i in result do
            for j in result do
                pos:=Position(roots,roots[i]+roots[j]);
                if pos<>fail and not pos in result then #and AA[i][j]<> 0 then
                    Add(result,pos);
                    flag:=true;
                fi;
#                pos:=Position(roots,roots[i]-roots[j]);
#                if pos<>fail and not pos in result then
#                    Add(result,pos);
#                    flag:=true;
#                fi;
            od;
        od;
    od;

    return result;
end;

ConjNegUa:=function(u,root,t)
    local result,Cijrs,sys,neg_root,roots,
    suma,param,k,i,cc;

    sys:=chevalleyAdj(u);
    Cijrs:=C(sys);
    neg_root:=root+Length(positiveRoots(sys));
    roots:=allRoots(sys);

    result:=ShallowCopy(coefficients(u));
    i:=1;
    #for i in [1..Length(result)] do
    while i<=Length(result) do
        #aici r si s sunt neg_root si repsectiv i
        k:=1;
        if Cijrs[neg_root][result[i][1]]=fail then return fail; fi;

        for cc in Cijrs[neg_root][result[i][1]] do
            suma:=cc[1]*roots[neg_root]+cc[2]*roots[result[i][1]];
            param:=cc[3]*((-1)^cc[1])*(t^cc[1])*(result[i][2]^cc[2]);
            Add(result,[Position(roots,suma),param],i+k);
            k:=k+1;
        od;
        i:=i+k;
    od;

#    Print(result,"\n");
    result:=Unipotent(sys,result,Ordering(u));
    return result;
end;

#-------------------------------------------------------------------------------

Coroot:=function(R,root)
    local bil,pr;
    pr:=positiveRoots(R);
    bil:=BilinearFormMat(R);
    return 2*pr[root]/(pr[root]*bil*pr[root]);
end;

InFundamentalChamber:=function(R,roots)
    local result,
    pr,r,bil,rang,fs,a,flag;

    pr:=positiveRoots(R);
    bil:=BilinearFormMat(R);
    rang:=Length(CanonicalGenerators(R)[1]);
    fs:=pr{[1..rang]};

    result:=[];
    for r in roots do
        flag:=true;
        for a in fs do
        if a*bil*r<0 then flag:=false; fi;
        od;
        if flag then Add(result,r); fi;
    od;

    return result;
end;


#############################################################################
##
#W  vspc.gi                     GAP library                     Thomas Breuer
##
##
#Y  Copyright (C)  1997,  Lehrstuhl D für Mathematik,  RWTH Aachen,  Germany
#Y  (C) 1998 School Math and Comp. Sci., University of St Andrews, Scotland
#Y  Copyright (C) 2002 The GAP Group
##
##  This file contains generic methods for vector spaces.
##

#############################################################################
##
#M  SetLeftActingDomain( <extL>, <D> )
##
##  check whether the left acting domain <D> of the external left set <extL>
##  knows that it is a division ring.
##  This is used, e.g.,  to tell a free module over a division ring
##  that it is a vector space.
##
InstallOtherMethod( SetLeftActingDomain,
    "method to set also 'IsLeftActedOnByDivisionRing'",
    [ IsAttributeStoringRep and IsLeftActedOnByRing, IsObject ],0,
    function( extL, D )
    if HasIsDivisionRing( D ) and IsDivisionRing( D ) then
      SetIsLeftActedOnByDivisionRing( extL, true );
    fi;
    TryNextMethod();
    end );

#############################################################################
##
#M  IsLeftActedOnByDivisionRing( <M> )
##
InstallMethod( IsLeftActedOnByDivisionRing,
    "method for external left set that is left acted on by a ring",
    [ IsExtLSet and IsLeftActedOnByRing ],
    function( M )
    if IsIdenticalObj( M, LeftActingDomain( M ) ) then
      TryNextMethod();
    else
      return IsDivisionRing( LeftActingDomain( M ) );
    fi;
    end );

#############################################################################
##
#F  VectorSpace( <F>, <gens>[, <zero>][, "basis"] )
##
##  The only difference between `VectorSpace' and `FreeLeftModule' shall be
##  that the left acting domain of a vector space must be a division ring.
##
InstallGlobalFunction( VectorSpace, function( arg )
    if Length( arg ) = 0 or not IsDivisionRing( arg[1] ) then
      Error( "usage: VectorSpace( <F>, <gens>[, <zero>][, \"basis\"] )" );
    fi;
    return CallFuncList( FreeLeftModule, arg );
    end );

#############################################################################
##
#M  AsSubspace( <V>, <C> )  . . . . . . . for a vector space and a collection
##
InstallMethod( AsSubspace,
    "for a vector space and a collection",
    [ IsVectorSpace, IsCollection ],
    function( V, C )
    local newC;

    if not IsSubset( V, C ) then
      return fail;
    fi;
    newC:= AsVectorSpace( LeftActingDomain( V ), C );
    if newC = fail then
      return fail;
    fi;
    SetParent( newC, V );
    UseIsomorphismRelation( C, newC );
    UseSubsetRelation( C, newC );

    return newC;
    end );

#############################################################################
##
#M  AsLeftModule( <F>, <V> )  . . . . . .  for division ring and vector space
##
##  View the vector space <V> as a vector space over the division ring <F>.
##
InstallMethod( AsLeftModule,
    "method for a division ring and a vector space",
    [ IsDivisionRing, IsVectorSpace ],
    function( F, V )

    local W,        # the space, result
          base,     # basis vectors of field extension
          gen,      # loop over generators of 'V'
          b,        # loop over 'base'
          gens,     # generators of 'V'
          newgens;  # extended list of generators

    if Characteristic( F ) <> Characteristic( LeftActingDomain( V ) ) then

      # This is impossible.
      return fail;

    elif F = LeftActingDomain( V ) then

      # No change of the left acting domain is necessary.
      return V;

    elif IsSubset( F, LeftActingDomain( V ) ) then

      # Check whether 'V' is really a space over the bigger field,
      # that is, whether the set of elements does not change.
      base:= BasisVectors( Basis( AsField( LeftActingDomain( V ), F ) ) );
      for gen in GeneratorsOfLeftModule( V ) do
        for b in base do
          if not b * gen in V then

            # The field extension would change the set of elements.
            return fail;

          fi;
        od;
      od;

      # Construct the space.
      W:= LeftModuleByGenerators( F, GeneratorsOfLeftModule(V), Zero(V) );

    elif IsSubset( LeftActingDomain( V ), F ) then

      # View 'V' as a space over a smaller field.
      # For that, the list of generators must be extended.
      gens:= GeneratorsOfLeftModule( V );
      if IsEmpty( gens ) then
        W:= LeftModuleByGenerators( F, [], Zero( V ) );
      else

        base:= BasisVectors( Basis( AsField( F, LeftActingDomain( V ) ) ) );
        newgens:= [];
        for b in base do
          for gen in gens do
            Add( newgens, b * gen );
          od;
        od;
        W:= LeftModuleByGenerators( F, newgens );

      fi;

    else

      # View 'V' first as space over the intersection of fields,
      # and then over the desired field.
      return AsLeftModule( F,
                 AsLeftModule( Intersection( F,
                     LeftActingDomain( V ) ), V ) );

    fi;

    UseIsomorphismRelation( V, W );
    UseSubsetRelation( V, W );
    return W;
    end );

#############################################################################
##
#M  ViewObj( <V> )  . . . . . . . . . . . . . . . . . . . view a vector space
##
##  print left acting domain, if known also dimension or no. of generators
##
InstallMethod( ViewObj,
    "for vector space with known generators",
    [ IsVectorSpace and HasGeneratorsOfLeftModule ],
    function( V )
    Print( "<vector space over ", LeftActingDomain( V ), ", with ",
           Length( GeneratorsOfLeftModule( V ) ), " generators>" );
    end );

InstallMethod( ViewObj,
    "for vector space with known dimension",
    [ IsVectorSpace and HasDimension ],
    1, # override method for known generators
    function( V )
    Print( "<vector space of dimension ", Dimension( V ),
           " over ", LeftActingDomain( V ), ">" );
    end );

InstallMethod( ViewObj,
    "for vector space",
    [ IsVectorSpace ],
    function( V )
    Print( "<vector space over ", LeftActingDomain( V ), ">" );
    end );

#############################################################################
##
#M  PrintObj( <V> ) . . . . . . . . . . . . . . . . . . .  for a vector space
##
InstallMethod( PrintObj,
    "method for vector space with left module generators",
    [ IsVectorSpace and HasGeneratorsOfLeftModule ],
    function( V )
    Print( "VectorSpace( ", LeftActingDomain( V ), ", ",
           GeneratorsOfLeftModule( V ) );
    if IsEmpty( GeneratorsOfLeftModule( V ) ) and HasZero( V ) then
      Print( ", ", Zero( V ), " )" );
    else
      Print( " )" );
    fi;
    end );

InstallMethod( PrintObj,
    "method for vector space",
    [ IsVectorSpace ],
    function( V )
    Print( "VectorSpace( ", LeftActingDomain( V ), ", ... )" );
    end );

#############################################################################
##
#M  \/( <V>, <W> )  . . . . . . . . .  factor of a vector space by a subspace
#M  \/( <V>, <vectors> )  . . . . . .  factor of a vector space by a subspace
##
InstallOtherMethod( \/,
    "method for vector space and collection",
    IsIdenticalObj,
    [ IsVectorSpace, IsCollection ],
    function( V, vectors )
    if IsVectorSpace( vectors ) then
      TryNextMethod();
    else
      return V / Subspace( V, vectors );
    fi;
    end );

InstallOtherMethod( \/,
    "generic method for two vector spaces",
    IsIdenticalObj,
    [ IsVectorSpace, IsVectorSpace ],
    function( V, W )
    return ImagesSource( NaturalHomomorphismBySubspace( V, W ) );
    end );

#############################################################################
##
#M  Intersection2Spaces( <AsStruct>, <Substruct>, <Struct> )
##
InstallGlobalFunction( Intersection2Spaces,
    function( AsStructure, Substructure, Structure )
    return function( V, W )
    local inters,  # intersection, result
          F,       # coefficients field
          gensV,   # list of generators of 'V'
          gensW,   # list of generators of 'W'
          VW,      # sum of 'V' and 'W'
          B;       # basis of 'VW'

    if LeftActingDomain( V ) <> LeftActingDomain( W ) then

      # Compute the intersection as vector space over the intersection
      # of the coefficients fields.
      # (Note that the characteristic is the same.)
      F:= Intersection2( LeftActingDomain( V ), LeftActingDomain( W ) );
      return Intersection2( AsStructure( F, V ), AsStructure( F, W ) );

    elif IsFiniteDimensional( V ) and IsFiniteDimensional( W ) then

      # Compute the intersection of two spaces over the same field.
      gensV:= GeneratorsOfLeftModule( V );
      gensW:= GeneratorsOfLeftModule( W );
      if   IsEmpty( gensV ) then
        if Zero( V ) in W then
          inters:= V;
        else
          inters:= [];
        fi;
      elif IsEmpty( gensW ) then
        if Zero( V ) in W then
          inters:= W;
        else
          inters:= [];
        fi;
      else
        # Compute a common coefficient space.
        VW:= LeftModuleByGenerators( LeftActingDomain( V ),
                                     Concatenation( gensV, gensW ) );
        B:= Basis( VW );

        # Construct the coefficient subspaces corresponding to 'V' and 'W'.
        gensV:= List( gensV, x -> Coefficients( B, x ) );
        gensW:= List( gensW, x -> Coefficients( B, x ) );

        # Construct the intersection of row spaces, and carry back to VW.
        inters:= List( SumIntersectionMat( gensV, gensW )[2],
                       x -> LinearCombination( B, x ) );

        # Construct the intersection space, if possible with a parent.
        if     HasParent( V ) and HasParent( W )
           and IsIdenticalObj( Parent( V ), Parent( W ) ) then
          inters:= Substructure( Parent( V ), inters, "basis" );
        elif IsEmpty( inters ) then
          inters:= Substructure( V, inters, "basis" );
          SetIsTrivial( inters, true );
        else
          inters:= Structure( LeftActingDomain( V ), inters, "basis" );
        fi;

        # Run implications by the subset relation.
        UseSubsetRelation( V, inters );
        UseSubsetRelation( W, inters );
      fi;

      # Return the result.
      return inters;

    else
      TryNextMethod();
    fi;
    end;
end );

#############################################################################
##
#M  Intersection2( <V>, <W> ) . . . . . . . . . . . . . for two vector spaces
##
InstallMethod( Intersection2,
    "method for two vector spaces",
    IsIdenticalObj,
    [ IsVectorSpace, IsVectorSpace ],
    Intersection2Spaces( AsLeftModule, SubspaceNC, VectorSpace ) );

#############################################################################
##
#M  ClosureLeftModule( <V>, <a> ) . . . . . . . . . closure of a vector space
##
InstallMethod( ClosureLeftModule,
    "method for a vector space with basis, and a vector",
    IsCollsElms,
    [ IsVectorSpace and HasBasis, IsVector ],
    function( V, w )
    local   B; # basis of 'V'

    # We can test membership easily.
    B:= Basis( V );
#T why easily?
    if Coefficients( B, w ) = fail then

      # In the case of a vector space, we know a basis of the closure.
      B:= Concatenation( BasisVectors( B ), [ w ] );
      V:= LeftModuleByGenerators( LeftActingDomain( V ), B );
      UseBasis( V, B );

    fi;
    return V;
    end );

#############################################################################
##
##  Methods for collections of subspaces of a vector space
##

#############################################################################
##
#R  IsSubspacesVectorSpaceDefaultRep( <D> )
##
##  is the representation of domains of subspaces of a vector space <V>,
##  with the components 'structure' (with value <V>) and 'dimension'
##  (with value either the dimension of the subspaces in the domain
##  or the string '\"all\"', which means that the domain contains all
##  subspaces of <V>).
##
DeclareRepresentation(
    "IsSubspacesVectorSpaceDefaultRep",
    IsComponentObjectRep,
    [ "dimension", "structure" ] );
#T not IsAttributeStoringRep?

#############################################################################
##
#M  PrintObj( <D> )  . . . . . . . . . . . . . . . . . for a subspaces domain
##
InstallMethod( PrintObj,
    "method for a subspaces domain",
    [ IsSubspacesVectorSpace and IsSubspacesVectorSpaceDefaultRep ],
    function( D )
    if IsInt( D!.dimension ) then
      Print( "Subspaces( ", D!.structure, ", ", D!.dimension, " )" );
    else
      Print( "Subspaces( ", D!.structure, " )" );
    fi;
    end );

#############################################################################
##
#M  Size( <D> ) . . . . . . . . . . . . . . . . . . .  for a subspaces domain
##
##  The number of $k$-dimensional subspaces in a $n$-dimensional space over
##  the field with $q$ elements is
##  $$
##  a(n,k) = \prod_{i=0}^{k-1} \frac{q^n-q^i}{q^k-q^i} =
##           \prod_{i=0}^{k-1} \frac{q^{n-i}-1}{q^{k-i}-1}.
##  $$
##  We have the recursion
##  $$
##  a(n,k+1) = a(n,k) \frac{q^{n-i}-1}{q^{i+1}-1}.
##  $$
##
##  (The number of all subspaces is $\sum_{k=0}^n a(n,k)$.)
##
InstallMethod( Size,
    "method for a subspaces domain",
    [ IsSubspacesVectorSpace and IsSubspacesVectorSpaceDefaultRep ],
    function( D )

    local k,
          n,
          q,
          size,
          qn,
          qd,
          ank,
          i;

    if D!.dimension = "all" then

      # all subspaces of the space
      n:= Dimension( D!.structure );

      q:= Size( LeftActingDomain( D!.structure ) );
      size:= 1;
      qn:= q^n;
      qd:= q;

      # $a(n,0)$
      ank:= 1;

      for k in [ 1 .. Int( (n-1)/2 ) ] do

        # Compute $a(n,k)$.
        ank:= ank * ( qn - 1 ) / ( qd - 1 );
        qn:= qn / q;
        qd:= qd * q;

        size:= size + ank;

      od;

      size:= 2 * size;

      if n mod 2 = 0 then

        # Add the number of spaces of dimension $n/2$.
        size:= size + ank * ( qn - 1 ) / ( qd - 1 );
      fi;

    else

      # number of spaces of dimension 'k' only
      n:= Dimension( D!.structure );
      if   D!.dimension < 0 or
           n < D!.dimension then
        return 0;
      elif n / 2 < D!.dimension then
        k:= n - D!.dimension;
      else
        k:= D!.dimension;
      fi;

      q:= Size( LeftActingDomain( D!.structure ) );
      size:= 1;

      qn:= q^n;
      qd:= q;
      for i in [ 1 .. k ] do
        size:= size * ( qn - 1 ) / ( qd - 1 );
        qn:= qn / q;
        qd:= qd * q;
      od;

    fi;

    # Return the result.
    return size;
    end );

#############################################################################
##
#M  Enumerator( <D> ) . . . . . . . . . . . . . . . .  for a subspaces domain
##
##  Use the iterator to compute the elements list.
#T This is not allowed!
##
InstallMethod( Enumerator,
    "method for a subspaces domain",
    [ IsSubspacesVectorSpace and IsSubspacesVectorSpaceDefaultRep ],
    function( D )
    local iter,    # iterator for 'D'
          elms;    # elements list, result

    iter:= Iterator( D );
    elms:= [];
    while not IsDoneIterator( iter ) do
      Add( elms, NextIterator( iter ) );
    od;
    return elms;
    end );
#T necessary?

#############################################################################
##
#M  Iterator( <D> ) . . . . . . . . . . . . . . . . .  for a subspaces domain
##
##  uses the subspaces iterator for full row spaces and the mechanism of
##  associated row spaces.
##
BindGlobal( "IsDoneIterator_Subspaces",
    iter -> IsDoneIterator( iter!.associatedIterator ) );

BindGlobal( "NextIterator_Subspaces", function( iter )
    local next;
    next:= NextIterator( iter!.associatedIterator );
    next:= List( GeneratorsOfLeftModule( next ),
                 x -> LinearCombination( iter!.basis, x ) );
    return Subspace( iter!.structure, next, "basis" );
    end );

BindGlobal( "ShallowCopy_Subspaces",
    iter -> rec( structure          := iter!.structure,
                 basis              := iter!.basis,
                 associatedIterator := ShallowCopy(
                                           iter!.associatedIterator ) ) );

InstallMethod( Iterator,
    "for a subspaces domain",
    [ IsSubspacesVectorSpace and IsSubspacesVectorSpaceDefaultRep ],
    function( D )
    local V;      # the vector space

    V:= D!.structure;
    return IteratorByFunctions( rec(
               IsDoneIterator     := IsDoneIterator_Subspaces,
               NextIterator       := NextIterator_Subspaces,
               ShallowCopy        := ShallowCopy_Subspaces,
               structure          := V,
               basis              := Basis( V ),
               associatedIterator := Iterator(
                      Subspaces( FullRowSpace( LeftActingDomain( V ),
                                               Dimension( V ) ),
                                 D!.dimension ) ) ) );
    end );

#############################################################################
##
#M  Subspaces( <V>, <dim> )
##
InstallMethod( Subspaces,
    "for a vector space, and an integer",
    [ IsVectorSpace, IsInt ],
    function( V, dim )
    if IsFinite( V ) then
      return Objectify( NewType( CollectionsFamily( FamilyObj( V ) ),
                                     IsSubspacesVectorSpace
                                 and IsSubspacesVectorSpaceDefaultRep ),
                        rec(
                             structure  := V,
                             dimension  := dim
                           )
                      );
    else
      TryNextMethod();
    fi;
    end );

#############################################################################
##
#M  Subspaces( <V> )
##
InstallMethod( Subspaces,
    "for a vector space",
    [ IsVectorSpace ],
    function( V )
    if IsFinite( V ) then
      return Objectify( NewType( CollectionsFamily( FamilyObj( V ) ),
                                     IsSubspacesVectorSpace
                                 and IsSubspacesVectorSpaceDefaultRep ),
                        rec(
                             structure  := V,
                             dimension  := "all"
                           )
                      );
    else
      TryNextMethod();
    fi;
    end );

#############################################################################
##
#F  IsSubspace( <V>, <U> ) . . . . . . . . . . . . . . . . . check <U> <= <V>
##
InstallGlobalFunction( IsSubspace, function( V, U )
    return IsVectorSpace( U ) and IsSubset( V, U );
end );

#############################################################################
##
#M  IsVectorSpaceHomomorphism( <map> )
##
InstallMethod( IsVectorSpaceHomomorphism,
    [ IsGeneralMapping ],
    function( map )
    local S, R, F;
    S:= Source( map );
    if not IsVectorSpace( S ) then
      return false;
    fi;
    R:= Range( map );
    if not IsVectorSpace( R ) then
      return false;
    fi;
    F:= LeftActingDomain( S );
    return ( F = LeftActingDomain( R ) ) and IsLinearMapping( F, map );
    end );

#############################################################################
##
#E


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

#F RDFT(<size>) -  Discrete Fourier Transform of a real sequence, non-terminal
#F This is an obsolete real DFT, please use PRDFT
#F
#F Definition: (n x n)-matrix
#F              [ cos(2*pi*k*l/n) | k = 0...Int(n/2), l = 0...n-1 ]
#F              [-sin(2*pi*k*l/n) | k = Int(n/2)+1...n, l = 0...n-1 ]
#F              k and l are row and column indices, respectively.
#F Note:      RDFT is obtained using symmetry of  DFT on a real-valued input
#F Example:   RDFT(8)
Class(RDFT, NonTerminal, rec(
    abbrevs := [ N -> Checked(IsInt(N), N > 1, [N]) ],
    terminate := self >> When(self.params[1]=2, F(2),
	Mat(ApplyFunc(Global.RDFT, self.params))),
    isReal := True,
    dims := self >> [self.params[1], self.params[1]],
    SmallRandom := () -> Random([2, 3, 4, 6, 8, 12, 16, 24, 32])
));

RulesFor(RDFT, rec(
    #F RuleRDFT_Base: RDFT_2 = F_2
    #F
    RDFT_Base := rec (
	info             := "RDFT_2 -> F_2",
	isApplicable     := L -> L[1] = 2,
	allChildren      := L -> [ [ ] ],
	rule            := (L, C) -> F(2)
    ),

    #F RDFT_Trigonometric: 1984
    #F
    #F   RDFT_n = (DCT1_(n/2+1) dirsum DST1_(n/2-1)) * blocks
    #F
    #F   Wang:
    #F     Fast Algorithms for the Discrete W Transform and the
    #F     Discrete Fourier Transform.
    #F     IEEE Trans. on ASSP, 1984, pp. 803--814
    #F   Britanak/Rao:
    #F     The fast generalized discrete Fourier transforms: A unified
    #F     approach to the discrete sinusoidal transforms computation.
    #F     Signal Processing 79, 1999, pp. 135--150
    #F
    RDFT_Trigonometric := rec (
	info             := "RDFT_n -> DCT1_(n/2+1), DST1_(n/2-1)",
        # 3 can occur only once
	isApplicable     := L -> L[1] >= 4 and
	    (Is2Power(L[1]) or (L[1] mod 3 = 0 and Is2Power(L[1]/3))),

        allChildren := L -> [[ DCT1(L[1]/2 + 1), DST1(L[1]/2 - 1) ]],

	rule := (L, C) -> let(N := L[1], exp := 1,
	   DirectSum(C[1], exp * C[2] ^ J(N/2 - 1)) *
	   DirectSum(I(1), blocks1(N - 1)))
    ),

    #F RDFT_CT_Radix2  : Cooley-Tukey Radix-2
    #F
    #F   RDFT_n = M * (F_2 tensor)' * (I_n/2 dirsum rots^(L^n/2_n/4)) *
    #F            (I_2 tensor RDFT_n/2) * L^n_2
    #F
    #F   derived by hand; the prime denotes that one F_2 is missing in the
    #F   tensor product.
    #F
    RDFT_CT_Radix2 := rec (
	info             := "RDFT_n -> RDFT_n/2",
	isApplicable     := L -> let(N := L[1], exp := 1,
	    N mod 4 = 0 and exp = 1 and
	    (IsPrimePowerInt(N) or       # 3 or 5 can occur only once
		(N mod 3 = 0 and IsPrimePowerInt(N/3)) or
		(N mod 5 = 0 and IsPrimePowerInt(N/5)))),

	allChildren := L -> [[ RDFT(L[1]/2) ]],

	rule := (P, C) -> let(N := P[1], i := Ind(N/4-1),
	    When(N=4,
		Conjugate(DirectSum(F(2),I(2)), L(4, 2)) *
		Tensor(I(2), C[1]) * L(4, 2),

		monomial1(N) *
		DirectSum(Tensor(I(N/4), F(2)), I(2), Tensor(I(N/4 - 1), F(2))) ^ L(N, N/2) *
		DirectSum(
		    I(N/2),
		    DirectSum(I(1), IterDirectSum(i, Rot(fdiv(i+1, N/2))), I(1)*(-1)^(N/2))
		       ^ M(N/2,N/4)) *
		Tensor(I(2), C[1]) * L(N, 2)))
    ),

    #F RDFT_toDCT2: 1989
    #F
    #F   RDFT_n = M * DCT2_n * P,   n odd
    #F
    #F   Chan/Ho: Efficient Index-Mapping for Computing the Discrete Cosine Transform
    #F     Electronics Letters, 1989
    #F   see also:
    #F   Heideman: Computation of an Odd-Length DCT from a Real-Valued DFT of
    #F     the same Length, IEEE Trans. Sig. Proc. 40(1), 1992
    #F
    RDFT_toDCT2 := rec(
	info             := "RDFT_n -> DCT2_n",
	switch           := false,
	isApplicable     := L ->
            # we block odd sizes larger than 3 for now
	    L[1] mod 2 = 1 and L[1] >= 3,

        allChildren := L ->  [[ DCT2(L[1]) ]],

	rule := (L, C) -> let(N := L[1],
	    Diag(LambdaList([0..N-1], i -> (-1)^i)) *
	    OS(N, 2) *
	    C[1] *
	    perm7(N))
    )
));