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

saveComment := function(r, fld)
    local str, last;
    str := CommentBuffer();
    last := Position(List(str), "");
    ClearCommentBuffer();
    r.(fld) := str{[3..last-3]};
end;

Declare(AsmX86Unparser);

Unparse := function(code, unparser, i, is)
    local o, res;
    # D($(..)) magic allows to deal with functions that do not return a value.
    # In this case res := D(_bag_0) without gap complaining.
    # We need this so that cx_leave runs before the function returns
    if not IsBound(unparser.cx) then unparser := WithBases(unparser, rec(cx := empty_cx())); fi;
    if IsRec(code) then
    cx_enter(unparser.cx, code);
        o := ObjId(code);
    if IsBound(unparser.(o.name)) then
            res := D($(unparser.(o.name)(code, i, is)));

    elif IsBound(o.unparse) and IsBound(unparser.(o.unparse)) then
            res := D($(unparser.(o.unparse)(code, i, is)));

    else return Error("Cannot unparse <o>. Unparser ", unparser, " does not have field '",
                o.name, "'", When(IsBound(o.unparse), Concat(" or ",o.unparse), ""));
    fi;
    else
    res := D($(unparser.atomic(code, i, is)));
    fi;
    cx_leave(unparser.cx, code);
    return $res;
end;

Class(Unparser, rec(
   gen := meth(self, subname, o, opts)
        local oo;
        # using WithBases prevents mem leaks, by avoiding of keeping extra state around
    self := WithBases(self, rec(opts := opts, cx := empty_cx()));
        oo := self.preprocess(o);
        Print(
            self.header(subname, oo),
            Unparse(oo, self, 4, 2),
            self.footer(subname, oo)
        );
    end,

   __call__ := (self, o, i, is) >> Unparse(o,self,i,is),
   fileinfo := (self,opts) >> Print(""),

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

    # printf with initial indentation
    indprintf := (self, i, is, str, args) >> Print(Blanks(i),
        ApplyFunc(PrintEvalF, Concatenation([str],
                List(args, a-> Cond(IsFunc(a), a,
         () -> Cond(IsType(a), self.declare(a, [], i+is, is), self(a, i+is, is))))))),

    prefix := meth(self, f, lst)
        local first, c;
        Print(f, "(");
        first := true;
        for c in lst do
            if first then first := false; else Print(", "); fi;
            self(c,0,4);
        od;
        Print(")");
    end,

    infixbreak := 8,

    #F infix(<lst>, <sep>, <i>)
    #F infix takes 2 - 3 arguments. the 3rd argument specifies the number of spaces to indent, otherwise 0
    #F
    infix := meth(arg)
        local self, lst, sep, i, count, c, first;
        if not Length(arg) in [3, 4] then
            Error("Usage: infix(<lst>, <sep>, [<i>])\n");
        fi;

        if Length(arg)=3 then
            [self, lst, sep] := arg;
            i := 0;
        else
            [self, lst, sep, i] := arg;
        fi;

        count := 0;
        first := true;
        for c in lst do
            if (count > 0) then
                if ((count mod self.infixbreak) = 0) then
                    Print(sep, "\n",Blanks(i+4));
                    first := true;
                fi;
				if not first then
                    Print(sep);
                fi;
            fi;
            count := count + 1;
            self(c,0,4);
            first := false;
        od;
    end,

    # finfix( <list>, <func>, <separator> ) - applying <func> to each element of <list>
    # and printing <separator> in between.

    finfix := meth(self, lst, func, sep)
        local first, c;
        first := true;
        for c in lst do
            if first then first := false; else Print(sep); fi;
            func(c);
        od;
    end,

    # pinfix(<lst>, <sep>), similar to infix, but parenthesizes the expression
    pinfix := meth(self, lst, sep)
        Print("(");
        self.finfix(lst, c -> self(c, 0, 4), sep);
        Print(")");
    end,

    # ppinfix(<lst>, <sep>), similar to infix, but parenthesizes inside the expression
    ppinfix := meth(self, lst, sep)
        Print("(");
        self.finfix(lst, c -> Print("(", self(c, 0, 4), ")"), sep);
        Print(")");
    end,

    # condinfix(<lst>, <sep>), for conditions like leq.
    # condinfix([a,b,c], " sep1 ", " sep2 ") unparses to
    # (a sep1 b) sep2 (b sep1 c)
    #
    condinfix := meth(self, lst, sep1, sep2)
        local first, c, i;
        Print("(");
        for i in [1..Length(lst)-1] do
            if i<>1 then Print(sep2); fi;
            self.pinfix([lst[i], lst[i+1]], sep1);
        od;
        Print(")");
    end,
));

#F LayeredUnparser(<unparser-class1>, <unparser-class2>, ...)
#F
#F This creates an unnamed class with superclasses listed in arguments.
#F unparser-class1 takes priority over 2, etc.
#F
Class(LayeredUnparser, rec(
    __call__ := arg >> Checked(Length(arg)>1, ForAll(Drop(arg, 1), IsClass),
        WithBases(arg, rec(operations := PrintOps, __call__ := Unparser.__call__))),

    print := self >> Print("LayeredUnparser(", PrintCS(Drop(self.__bases__,1)), ")")
));

Loc.unparse     := "Loc";
Exp.unparse     := "Exp";
Command.unparse := "Command";

Class(CUnparserBase, Unparser, rec(
    preprocess := (self, o) >> o,
    preprocess_init := (self, o) >> o,

    # example: includes := [ "<math.h>" ]
    includes := [],

    generated_by := Concat("\
/*\
 * This code was generated by Spiral ", SpiralVersion, ", www.spiral.net\
 */\
\n"),

    extraHeader := "",

    header_top := meth(self, subname, o)
        local precomputed_data;
        Print(self.generated_by);

        self.fileinfo(self.opts);

        DoForAll(Concatenation(self.includes, self.opts.includes),
             inc -> Print("#include ", inc, "\n"));

        if IsBound(o.dimensions) then Print("/* ", o.dimensions, " */\n"); fi;

        if IsBound(o.runtime_data) then
            DoForAll(o.runtime_data, x->Print(self.opts.arrayDataModifier, " ",
                                              self.declare(x.t, x, 0, 4), ";\n"));
            Print("\n");
        fi;

        precomputed_data := List(Collect(o, data), x->[x.var, x.value]);
    DoForAll(precomputed_data, d -> When(IsArrayT(d[1].t), self.genData(d[1], d[2])));
    end,

    header_func := meth(self, subname, o)
        local loopvars;
        Print("void ", self.opts.funcModifier, " ", subname, "(",
          self.declare(Y.t, Y, 0, 0), ", ", self.declare(X.t, X, 0, 0));

        if IsBound(self.opts.sig) then
            DoForAll(self.opts.sig, p -> Print(", ", self.declare(p.t, p, 0, 0)));
        fi;
        Print(") {\n");

        loopvars := Set(List(Collect(o, @(1).cond(IsLoop)), x->x.var));
        if loopvars <> [] then Print(Blanks(4), "int ", PrintCS(loopvars), ";\n"); fi;
    end,

    header := meth(self, subname, o)
        self.header_top(subname, o);
        self.header_func(subname, o);
    end,

    footer := meth(self, subname, o)
        local init, loopvars;
        Print("}\n");
        Print("void init_", subname, "() {\n");
        if IsBound(o.runtime_init) then # unparse initialization code

            loopvars := Union(List(o.runtime_init, cc -> List(Collect(cc, @(1).cond(IsLoop)), x->x.var)));
            if loopvars <> [  ]  then
                Print(Blanks(4), "int ", PrintCS(loopvars), ";\n"); fi;
            for init in o.runtime_init do
                init := self.preprocess_init(init);
                self(SReduce(init, o), 4, 4);
            od;
        fi;
        Print(" }\n");
    end,

    genData := (self, v, val) >> Print(
        When(IsArrayT(val.t), self.opts.arrayDataModifier, self.opts.scalarDataModifier), " ",
        self.declare(val.t, v, 0, 4), " = ", self(val,2,2), ";\n",
        When(IsArrayT(val.t), "\n", "")),

    ####################
    ## General
    ####################
    atomic  := (self,o,i,is) >> Print(o),
    param   := (self,o,i,is) >> Print(o.id),
    var     := (self,o,i,is) >> Print(o.id),
    Loc     := (self,o,i,is) >> o.cprint(),

    ####################
    ## Commands
    ####################

    asmvolatile := (self,o,i,is) >> AsmX86Unparser.asmvolatile(o.asm),

    skip := (self,o,i,is) >> Print(Blanks(i), "/* skip */\n"),

    noUnparse :=(self,o,i,is) >> Print(o.str),

    assign := (self,o,i,is) >> Print(Blanks(i), self(o.loc,i,is), " = ", self(o.exp,i,is), ";\n"),

    assign_acc := (self,o,i,is) >> Print(Blanks(i), self(o.loc,i,is), " += ", self(o.exp,i,is), ";\n"),

    chain    := (self,o,i,is) >> DoForAll(o.cmds, c -> self(c, i, is)),
    brackets := (self,o,i,is) >> self.printf("($1)", [o.args[1]]),

    unparseChain := (self,o,i,is) >> DoForAll(o.cmds, c -> self(c, i, is)),

    kern := (self, o, i, is) >> When( IsBound(self.opts.SimFlexKernelFlag) and IsBound(o.bbnum),
        Print(
            "#if !defined(KERN) || defined(KERN", String(o.bbnum), ")\n",
            self(o.cmd, i, is),
            "#endif\n"
        ),
        self(o.cmd, i, is)
    ),
    unroll_cmd := ~.chain,

    # all datas are handled in the header, just proceed to children
    data := (self,o,i,is) >> Print(
        When(not IsArrayT(o.var.t), Print(Blanks(i), self.genData(o.var, o.value))),
        self(o.cmd, i, is)
    ),

    _lt := " <= ",
    loop := (self,o,i,is) >> Checked(IsRange(o.range),
        let(v := o.var, lo := o.range[1], hi := Last(o.range),
            Print(Blanks(i), "for(", v, " = ", lo, "; ", v, self._lt, hi, "; ", v, "++) {\n",
                self(o.cmd,i+is,is),
                Blanks(i), "}\n"))),

    loopn := (self,o,i,is) >>
        let(v := o.var, n := o.range,
            Print(Blanks(i), "for(", v, " = ", 0, "; ", v, self._lt, self(n-1,i,is), "; ", v, "++) {\n",
                self(o.cmd,i+is,is),
                Blanks(i), "}\n")),

    doloop := (self,o,i,is) >>
        let(v := o.var, n := o.range,
            Print(Blanks(i), "do {\n",
                self(o.cmd,i+is,is),
                Blanks(i), "} while( ", self(v,i,is), "<", self(n,i,is), " );\n")),

    loopn := (self, o, i, is) >> self.loop(o, i, is),

    IF := (self,o,i,is) >> Print(Blanks(i),
        "if (", self(o.cond,i,is), ") {\n", self(o.then_cmd,i+is,is), Blanks(i), "}",
        When(o.else_cmd = skip(),
             "\n",
             Print(" else {\n", self(o.else_cmd,i+is,is), Blanks(i), "}\n"))),

    DOWHILE := (self,o,i,is) >> Print(Blanks(i),
    "do \n",  Blanks(i), "{\n", self(o.then_cmd,i+is,is), Blanks(i), "}",
        "while (", self(o.cond,i,is), ");\n" ),

    WHILE := (self,o,i,is) >> Print(Blanks(i),
    "while (", self(o.cond,i,is), ")\n" ,
    Blanks(i), "{\n", self(o.then_cmd,i+is,is), Blanks(i), "}\n"),

    PRINT := (self,o, i, is) >> Print(Blanks(i),
        "printf(\"", o.fmt, "\"",
            When(o.vars <> [],
                Print(", ",
                    DoForAll( DropLast(o.vars,1),
                        e -> Print(self(e,i,is), ", ")
                    ),
                    self(Last(o.vars),i,is)
                ),
                Print("")
            ),
            ");\n"
    ),

    multi_if := meth(self,o,i,is)
        local j, conds;
    conds := o.args { [1..Int(Length(o.args)/2)]*2 - 1 };

        # degenerate case, no conditions, else branch only
        if Length(o.args)=1 then
        self(o.args[1], i, is);

    # generate switch stmt
    elif ForAll(conds, c -> ObjId(c)=eq and ObjId(c.args[1]) in [var,param] and c.args[1]=conds[1].args[1] and IsValue(c.args[2])) then
        Print(Blanks(i), "switch(", self(conds[1].args[1], i, is), ") { \n");
            j := 1;
            while j < Length(o.args) do
                Print(Blanks(i+Int(is/2)), "case ", self(o.args[j].args[2], i, is), ": ");
        Cond(ObjId(o.args[j+1])=ret,
             Print(self(o.args[j+1],0,is), Blanks(i+is), "break;\n "),
             Print("{\n", self(o.args[j+1],i+is,is), Blanks(i+is), "break; }\n"));
        j := j+2;
            od;
            # Print out the else branch if it exists (j=Length)
            if j = Length(o.args) then
        Print(Blanks(i+Int(is/2)), "default: ");
        Cond(ObjId(o.args[j])=ret,
             Print(self(o.args[j], 0, is)),
             Print("{\n", self(o.args[j], i+is, is), Blanks(i+Int(is/2)), "}\n"));
            fi;
        Print(Blanks(i), "}\n");

    # general IF cascade
    else

            j := 1;
            while j < Length(o.args) do
                Print(Blanks(i), When(j<>1, "else "), "if (",
                      self(o.args[j],  i+is,is), ") {\n",
                      self(o.args[j+1],i+is,is), Blanks(i), "}\n");
        j := j+2;
            od;
            # Print out the else branch if it exists (j=Length)
            if j = Length(o.args) then
        Print(Blanks(i), "else {\n",
                      self(o.args[j],  i+is, is), Blanks(i), "}\n");
            fi;
    fi;
    end,

    zallocate := (self, o, i, is) >> Print(
        self(allocate(o.loc,o.exp),i,is),
        Blanks(i),"memset(",self(o.loc, i, is)," ,'\\0', sizeof(",
        self.declare(o.exp.t,[],0,0), ") * ", self(o.exp.size,i,is), ");\n"),
#        Blanks(i),"for(int iinit = 0; iinit <  ",self(o.exp.size,i,is),"; iinit++)\n",
#        Blanks(i),"    ", self(o.loc, i, is),"[iinit]=0;\n"),

    ####################
    ## Expressions
    ####################

    RewritableObjectExp := (self,o,i,is) >> Print(o.name, self.pinfix(o.rChildren(), ", ")),
    Exp := (self,o,i,is) >> Print(o.name, self.pinfix(o.args, ", ")),
    ExpCommand := (self,o,i,is) >> Print(Blanks(i), o.name, self.pinfix(o.args, ", "), ";\n"),
    Command := (self,o,i,is) >> Print(Blanks(i), o.name, self.pinfix(o.rChildren(), ", "), ";\n"),
    call := (self,o,i,is) >> Print(Blanks(i), self(o.args[1],i,is), self.pinfix(Drop(o.args,1), ", "), ";\n"),
    errExp := (self, o, i, is) >> self(o.t.zero(), i, is),

    eq  := (self, o, i, is) >> self.condinfix(o.args, " == ", " && "),
    neq := (self, o, i, is) >> self.condinfix(o.args, " != ", " && "),
    geq := (self, o, i, is) >> self.condinfix(o.args, " >= ", " && "),
    leq := (self, o, i, is) >> self.condinfix(o.args, " <= ", " && "),
    gt  := (self, o, i, is) >> self.condinfix(o.args, " > ",  " && "),
    lt  := (self, o, i, is) >> self.condinfix(o.args, " < ",  " && "),

    nth := (self,o,i,is) >> Print(self(o.loc,i,is), "[", self(o.idx,i,is), "]"),
    deref := (self,o,i,is) >> Print("*(", self(o.loc,i,is), ")"),
    addrof := (self,o,i,is) >> Print("&(", self(o.loc,i,is), ")"),
    fdiv := (self,o,i,is) >> Print("(((", self.declare(TReal, [],i,is), ")",
    self(o.args[1],i,is), ") / ", self(o.args[2],i,is), ")"),
    add := (self,o,i,is) >> self.pinfix(o.args, " + "),
    logic_and := (self,o,i,is) >> self.ppinfix(o.args, " && "),
    logic_or := (self,o,i,is) >> self.ppinfix(o.args, " || "),
    logic_neg := (self,o,i,is) >> Print("( !(",self(o.args[1],i,is), ") )"),
    sub := (self,o,i,is) >> self.pinfix(o.args, " - "),
    neg := (self,o,i,is) >> Print("-(", self(o.args[1],i,is), ")"),

    mul := (self,o,i,is) >> self.pinfix(o.args, "*"),
    div := (self,o,i,is) >> self.pinfix(o.args, " / "),
    idiv := (self,o,i,is) >> self.pinfix(o.args, " / "),
    imod := (self,o,i,is) >> self.pinfix(List(o.args, x -> When(IsPtrT(x.t), tcast(TSym("size_t"),x) ,x)), " % "),
    no_mod := (self,o,i,is) >> self(o.args[1],i,is),
    re     := (self,o,i,is) >> self.printf("creal($1)", [o.args[1]]),
    im     := (self,o,i,is) >> self.printf("cimag($1)", [o.args[1]]),
    cxpack := (self,o,i,is) >> self.printf("($1) + _Complex_I*($2)", [o.args[1], o.args[2]]),

    bin_and := (self,o,i,is) >> Print("((", self.pinfix(List(o.args, x -> When(IsPtrT(x.t), tcast(TSym("size_t"),x) ,x)), ")&("), "))"),
    bin_or := (self,o,i,is) >> Print("((", self.pinfix(List(o.args, x -> When(IsPtrT(x.t), tcast(TSym("size_t"),x) ,x)), ")|("), "))"),
    bin_xor := (self,o,i,is) >> Print("((", self(o.args[1],i,is), ")^(", self(o.args[2],i,is),"))"),

    abs := (self,o,i,is) >> Print("abs(", self(o.args[1],i,is), ")"),

    floor := (self,o,i,is) >> Print("((int)(", self(o.args[1],i,is), "))"),

    lShift := (self,o,i,is) >> Cond(IsBound(o.args[3]),
            Error("non implemented"),
            Print("((",self(o.args[1],i,is),") << (",self(o.args[2],i,is), "))")),

    rShift := (self,o,i,is) >> Cond(IsBound(o.args[3]),
            Error("non implemented"),
            Print("((",self(o.args[1],i,is),") >> (",self(o.args[2],i,is), "))")),

    arith_shr := (self, o, i, is) >> Cond( o.t.isSigned(), self.printf("(($1) >> ($2))", [o.args[1], o.args[2]]),
                                           Error("implement arith_shr for unsigned data type")),
    arith_shl := (self, o, i, is) >> Cond( o.t.isSigned(), self.printf("(($1) \<\< ($2))", [o.args[1], o.args[2]]),
                                           Error("implement arith_shl for unsigned data type")),

    xor := meth(self,o,i,is)
        Print("((", self(o.args[1],i,is));
        DoForAll(o.args{[2..Length(o.args)]}, e -> Print(")^(", self(e,i,is)));
        Print("))");
    end,

    max := (self,o,i,is) >> self(cond(geq(o.args[1],o.args[2]),o.args[1],o.args[2]),i,is),

    min := (self,o,i,is) >> self(cond(leq(o.args[1],o.args[2]),o.args[1],o.args[2]),i,is),

    log := (self,o,i,is) >> Cond( Length(o.args)=1,
        Cond( o.t = T_Real(32) or (IsBound(self.opts.TRealCtype) and self.opts.TRealCtype = "float"),
                self.printf("logf($1)", [o.args[1]]),
              # else
              self.printf("log((double)($1))", [o.args[1]])),
        Cond( o.t = T_Real(32) or (IsBound(self.opts.TRealCtype) and self.opts.TRealCtype = "float"),
                self.printf("logf($1)/logf($2)", [o.args[1], o.args[2]]),
              # else
              self.printf("log((double)($1))/log((double)($2))", [o.args[1], o.args[2]]))),

    pow := (self,o,i,is) >> Cond(
        o.args[2]=2,
            self(mul(o.args[1], o.args[1]), i, is),
        # else
            Error("Implement pow()")),

    sqrt  := (self,o,i,is) >> self.printf("sqrt($1)",    [o.args[1]]),
    rsqrt := (self,o,i,is) >> self.printf("$1/sqrt($2)", [o.t.one(), o.args[1]]),

    cond := (self,o,i,is) >> Cond(
      Length(o.args)=3,
          Cond(ObjId(o.args[3]) = errExp,
           self(o.args[2], i, is),
               Print("((",self(o.args[1],i,is),") ? (",self(o.args[2],i,is),") : (",self(o.args[3],i,is),"))")),

      # NOTE: no else case here, maybe just leave it like this?
      Length(o.args)=2,
          self(o.args[2],i,is),

      # more than 3 args: do a binsplit trick
      Print("((",self(o.args[1],i,is),") ? (",self(o.args[2],i,is),") : ",
      self(ApplyFunc(cond, Drop(o.args, 2)), i,is),")")),

    _decval :=(self, v) >> let(pf := When(IsBound(self.opts.valuePostfix), self.opts.valuePostfix, ""),
            Print(v, pf)),

    Value := (self,o,i,is) >>
        Cond(
        o.t = TComplex, let(c:=Complex(o.v), re:=ReComplex(c), im:=ImComplex(c),
            Cond(re=0 and im=1,  Print(self.opts.c99.I),
                 re=0 and im=-1, Print("(- ", self.opts.c99.I, ")"),
                 im=0,           Print(self._decval(re)),
                 re=0,           Print("(", self._decval(im), " * ", self.opts.c99.I, ")"),
                 im < 0,         Print("(", self._decval(re), " - ", self.opts.c99.I, " * ", self._decval(-im), ")"),
                 Print("(", self._decval(re), " + ", self.opts.c99.I, " * ", self._decval(im), ")"))),

        o.t = TReal, let(
        v := Cond(IsCyc(o.v), ReComplex(Complex(o.v)), o.v),
        Cond(v < 0,
         Print("(", self._decval(v), ")"),
         Print(self._decval(v)))),

        IsArray(o.t),                      Print("{", WithBases(self, rec(infixbreak:=4)).infix(o.v, ", ", i), "}"),
        o.v < 0,                           Print("(", o.v, ")"),
        o.t = TBool,                       When(o.v in [true, 1], Print("1"), Print("0")),

        o.t = TUInt,                       Print(o.v, "u"),
                                           Print(o.v)
    ),

    ##########################
    ## Types and declarations
    ##########################

    # This function unparses the standard decl(var, value, code) Command.
    # First, we unparse array variables then group variables by their type,
    # and then call CUnparserBase.declare on each group

    decl := meth(self,o,i,is)
        local arrays, other, l, arri, myMem;
        [arrays, other] := SplitBy(o.vars, x->IsArray(x.t));
        DoForAll(arrays, v -> Print(Blanks(i),
                                    When(self.opts.arrayBufModifier <> "", self.opts.arrayBufModifier::" ", ""),
                                    self.declare(v.t, v, i, is), ";\n"));

        if (Length(other)>0) then
            other:=SortRecordList(other,x->x.t);
            for l in other do
               Sort(l, (a,b)->a.id < b.id);
               Print(Blanks(i), self.declare(l[1].t, l, i, is), ";\n");
            od;
        fi;

        self(o.cmd, i, is);

        #Pop arena for this decl
        if IsBound(self.opts.useMemoryArena) and self.opts.useMemoryArena and Length(arrays) > 0 and arrays[1].id[1] <> 'D' then
          myMem := 0;
          for arri in arrays do
             # Account for vector allocations in memory arena (which is scalar)
             myMem := myMem + (arri.t.size * When(IsBound(arri.t.t) and ObjId(arri.t.t)=TVect, arri.t.t.size, 1));
          od;
          if ObjId(myMem) = Value then myMem := myMem.v; fi;
          Print(Blanks(i));
          Print("arenalevel += ", myMem, ";\n" );
        fi;
    end,

    # defines a struct
    define := meth(self, o, i, is)
        local e, ee;
        for e in o.types do
            Print(Blanks(i), "typedef struct {\n");
            for ee in e.getVars() do
                Print(Blanks(i+is));
                Print(self.declare(ee.t, [ee], i, is), ";\n");
            od;

            Print(Blanks(i), "} ", e.getName(), ";\n");
        od;
    end,

    tcast := (self, o, i, is) >> Print("((", self.declare(o.args[1], [], i, is), ") ", self(o.args[2],i,is), ")"),

    sizeof := (self, o, i, is) >> Print("sizeof(", self.declare(o.args[1], [], i, is), ")"),

    declare := (self, t, vars, i, is) >> When(
        IsBound(self.(t.name)),
        self.(t.name)(t, When(IsList(vars), vars, [vars]), i, is),
        Error("Can't declare ", vars, " no method '", t.name, "' in ", self)),

    TVect := (self, t, vars, i, is) >> Print("__m128 ", self.infix(vars, ", ", i+is)),

    TComplex := (self, t, vars, i, is) >> Print(
        When(IsBound(self.opts.TComplexCtype), self.opts.TComplexCtype, "complex_t "), " ",
        self.infix(vars, ", ",i+is)),

    T_Complex := (self, t, vars, i, is) >> Print("_Complex ", self.declare(t.params[1], vars, i, is)),

    T_Real  := (self, t, vars, i, is) >> Print(Cond(
            t.params[1] = 128, "long double",
            t.params[1] = 80, "long double",   #x86 specific
            t.params[1] = 64, "double",
            t.params[1] = 32, "float",
            Error("Type is not supported")
        )," ",self.infix(vars, ", ",i+is)),

    T_Int  := (self, t, vars, i, is) >> Print(Cond(
            t.params[1] = 64, "__int64",
            t.params[1] = 32, "__int32",
            t.params[1] = 16, "__int16",
            t.params[1] = 8, "__int8",
            Error("Type is not supported")
        ), " ", self.infix(vars, ", ",i+is)),

    T_UInt  := (self, t, vars, i, is) >> Print("unsigned ", Cond(
            t.params[1] = 64, "__int64",
            t.params[1] = 32, "__int32",
            t.params[1] = 16, "__int16",
            t.params[1] = 8, "__int8",
            t.params[1] = 1, "__bit",
            Error("Type is not supported")
        ), " ", self.infix(vars, ", ",i+is)),

    T_Struct := (self, t, vars, i, is) >> Print(
        t.getName(), " ", self.infix(vars, ", ", i+is)
    ),

    TReal  := (self, t, vars, i, is) >> Print(
        When(IsBound(self.opts.TRealCtype), self.opts.TRealCtype, TReal.ctype), " ",
        self.infix(vars, ", ",i+is)),

    TInt  := (self, t, vars, i, is) >> Print(
        When(IsBound(self.opts.TIntCtype), self.opts.TIntCtype, TInt.ctype), " ",
        self.infix(vars, ", ",i+is)),

    TDummy := ~.TInt,

    TBool  := (self, t, vars, i, is) >> Print(
        When(IsBound(self.opts.TIntCtype), self.opts.TIntCtype, TInt.ctype), " ",
        self.infix(vars, ", ",i+is)),

    TUInt  := (self, t, vars, i, is) >> Print(
        When(IsBound(self.opts.TUIntCtype), self.opts.TUIntCtype, TUInt.ctype), " ",
        self.infix(vars, ", ",i+is)),

    TULongLong  := (self, t, vars, i, is) >> Print(
        When(IsBound(self.opts.TULongLongCtype), self.opts.TULongLongCtype, TULongLong.ctype), " ",
        self.infix(vars, ", ",i+is)),

    TChar  := (self, t, vars, i, is) >> Print(
        When(IsBound(self.opts.TCharCtype), self.opts.TCharCtype, TChar.ctype), " ",
        self.infix(vars, ", ",i+is)),

    TUChar  := (self, t, vars, i, is) >> Print(
        When(IsBound(self.opts.TUCharCtype), self.opts.TUCharCtype, TUChar.ctype), " ",
        self.infix(vars, ", ",i+is)),

    TVoid  := (self, t, vars, i, is) >> Print("void ", self.infix(vars, ", ",i+is)),

    _restrict := (self, t) >> let(opts := self.opts, rst := Concat(When(IsBound(opts.restrict),
                    opts.restrict(), "restrict"), " "), When(t._restrict, rst, "")),

    TPtr  := (self, t, vars, i, is) >>
        Print(Cond(not IsBound(t.qualifiers) or t.qualifiers=[], "", Print(self.infix(t.qualifiers, " "), " ")),
          Cond(vars=[],
          Print(self.declare(t.t, [], i, is), " *", self._restrict(t)),
          Print(self.declare(t.t, [], i, is),
              Print(" *", self._restrict(t) ),
              self.infix(vars, Concatenation(", *", self._restrict(t)), i+is)))),

    TSym := (self, t, vars, i, is) >> Print(t.id, " ", self.infix(vars, ", ")),

    _TFunc_args := (self, t, i, is) >> let(params := DropLast(t.params, 1),
        Print(
            DoForAllButLast(params, p -> Print(self.declare(p, [], i, is), ", ")),
            self.declare(Last(params), [], i, is))),

    TFunc  := (self, t, vars, i, is) >>
        Cond(Length(vars) in [0,1],
               PrintEvalF("$1 (*$2)($3)",
                 () -> self.declare(Last(t.params), [], i, is),
                 When(vars=[], "", vars[1].id),
                 () -> self._TFunc_args(t, i, is)),
               Print(
                   DoForAllButLast(vars, v -> Print(self.declare(t, v, i, is), ", ")),
                   self.declare(t, [Last(vars)], i, is))),

    TArray := meth(self,t,vars,i,is)
        local dims, elt, v, ptype, vsize;
        if Length(vars) > 1 then DoForAll(vars, v->Print(self.TArray(t, [v], i, is), "; "));
        elif Length(vars) = 0 then
            Print(self.declare(t.t, [], i, is), " *");
        else
            # at this point Length(vars)=1
            v := When(IsList(vars), vars[1], vars);
            dims := []; elt := t;
            while IsArray(elt) do Add(dims, elt.size); elt := elt.t; od;

          #NOTE: Ignoring twiddles by looking for "D" in .id
          # Better way: look for func.name="init" in parent/context
          if IsBound(self.opts.useMemoryArena) and self.opts.useMemoryArena and v.id[1] <> 'D' then
            #NOTE: Arena currently doesn't handle multiple dims.
            #NOTE: Slightly ugly hack to get this to be a pointer

            # To handle vectors. Arena is declared only for scalars. For
            # vectors, we must manually scale the allocation by vector length.
            vsize := 1; if ObjId(elt)=TVect then vsize := elt.size; fi;
#            ptype := Concatenation(elt.name, "Pointer");
#            self.(ptype)(elt, [v], i, is);
            self.TPtr(TPtr(elt), [v], i, is);
            Print(" =  &(ARENA[ (arenalevel-=",self(dims[1]*vsize,i,is),") ])");
          else
            self.(elt.name)(elt, [v], i, is);
            DoForAll(dims, d->Print("[",self(d,i,is),"]"));
          fi;

        fi;
    end,

    # comments embedded in code

    comment := (self, o, i, is) >> When(Length(o.exp) = 0,
        PrintLine(),
        PrintLine(Blanks(i), "/* ", o.exp, " */")
    ),

    quote := (self, o, i, is) >> Print("\"", self(o.cmd,i,is), "\"")
));

# Locate instances of assign(loc, Value), where types of Value and loc
# do not match. Fix the value to be of correct type.
# Note: For linear transforms the only possible Value is 0 (otherwise
# transform is not linear)
FixAssign0 := c -> SubstTopDownNR(c, [assign, @(1), @(2,Value,e->e.t <> @(1).val.t)],
    e -> assign(@(1).val,
            When(@(2).val.v = 0, @(1).val.t.zero(),
                                 @(1).val.t.value(@(2).val.v))));

Class(CMacroUnparser, CUnparserBase, rec(
    preprocess := (self, c) >> FixAssign0(PropagateTypes(c)),
    preprocess_init := (self, c) >> FixAssign0(PropagateTypes(c)),

    # Split non-binary "+" into nested binary ops
    add := (self,o,i,is) >> Cond(
        o.t=TInt or IsPtrT(o.t), Inherited(o, i, is),
        Length(o.args)=2, self.prefixTT("ADD", o.args[1].t, o.args[2].t, o.args),
        let(rem := ApplyFunc(add, Drop(o.args, 1)),
            self.prefixTT("ADD", o.args[1].t, rem.t, [o.args[1], rem]))),

    sub := (self,o,i,is) >> Cond(o.t=TInt or IsPtrT(o.t), Inherited(o, i, is),
    self.prefixTT("SUB", o.args[1].t, o.args[2].t, o.args)),

    nth := (self,o,i,is) >> Cond(o.t=TInt or IsPtrT(o.t), Inherited(o, i, is), self.prefixT("NTH", o.t, [o.loc, o.idx])),
    neg := (self,o,i,is) >> Cond(o.t=TInt or IsPtrT(o.t), Inherited(o, i, is), self.prefixT("NEG", o.t, o.args)),

    bin_and := (self, o, i, is) >> Cond(o.t=TInt or ObjId(o.t) in [T_Int, T_UInt], Inherited(o, i, is), self.prefixT("AND", o.t, o.args)),
    bin_xor := (self, o, i, is) >> Cond(o.t=TInt or ObjId(o.t) in [T_Int, T_UInt], Inherited(o, i, is), self.prefixT("XOR", o.t, o.args)),
    div  := (self,o,i,is) >> Cond(o.t=TInt, Inherited(o, i, is), self.prefixTT("DIV", o.args[1].t, o.args[2].t, o.args)),
    imod := (self,o,i,is) >> self.prefixT("IMOD", o.t, o.args),
    max  := (self,o,i,is) >> self.prefixTT("MAX", o.args[1].t, o.args[2].t, o.args),
    min  := (self,o,i,is) >> self.prefixTT("MIN", o.args[1].t, o.args[2].t, o.args),

    idiv := (self,o,i,is) >> Cond(o.t=TInt and ForAll(o.args,x->x.t=TInt), Inherited(o, i, is),
        self.prefixT("IDIV", o.t, o.args)),

    fdiv := (self,o,i,is) >> self.prefixTT("FDIV", o.args[1].t, o.args[2].t, o.args),

    re     := (self,o,i,is) >> self.prefixT("RE", o.args[1].t, o.args),
    im     := (self,o,i,is) >> self.prefixT("IM", o.args[1].t, o.args),
    cxpack := (self,o,i,is) >> self.prefixT("C", o.t, o.args),

    no_mod := (self,o,i,is) >> self(o.args[1],i,is),

    Value := (self, o, i, is) >> Cond(
        IsArray(o.t), Print("{", self.infix(o.v, ", "), "}"),
        let(fmt := self._const(o),
        pfx := Cond(self.cx.isInside(data), "CD_", "C_"),
            Cond(fmt[2]=[], Print(pfx, fmt[1]),
                 fmt[1]="INT", Print(fmt[2][1]),
                 self.prefix(Concat(pfx, fmt[1]), fmt[2])))),

    # mults by different constants are unparsed differently
    # Split non-binary "*" into nested binary ops
    mul := (self,o,i,is) >> Cond(
        o.t=TInt, Inherited(o, i, is),
        Length(o.args)<>2, self(mul(o.args[1], ApplyFunc(mul, Drop(o.args, 1))), i, is),
        let(
         # check if constant ended up in slot #2
         a := When(IsValue(o.args[2]), o.args[2], o.args[1]),
         b := When(IsValue(o.args[2]), o.args[1], o.args[2]),
         When(not (IsValue(a) or (IsVar(a) and IsBound(a.value))),
            # <a> is not a constant
            self.prefix(Concat("MUL_", self._pfx(a.t), "_", self._pfx(b.t)), [a,b]),
            # <a> is a constant
            let(fmt := self._const( When(IsValue(a), a, a.value) ),
                self.prefix(Concat("MUL_", fmt[1], "_", self._pfx(b.t)),
                     # check if constant is special, and does not go into MUL args
                     # for example fmt[1]="I" denotes sqrt(-1), one such constant
                     When(fmt[2]=[], [b], [a, b])))))),

    prefixT := (self, funcname, t, args) >>
        self.prefix(Concat(funcname, "_", self._pfx(t)), args),

    prefixTT := (self, funcname, t1, t2, args) >>
        self.prefix(Concat(funcname, "_", self._pfx(t1), "_", self._pfx(t2)), args),

    prefixTTT := (self, funcname, t1, t2, t3, args) >>
        self.prefix(Concat(funcname, "_", self._pfx(t1), "_", self._pfx(t2), "_", self._pfx(t3)), args),
    # this is getting really ugly
    prefixTTTT := (self, funcname, t1, t2, t3, t4, args) >>
        self.prefix(Concat(funcname, "_", self._pfx(t1), "_", self._pfx(t2), "_", self._pfx(t3), "_", self._pfx(t4)), args),

    prefix_T := (self, funcname, args) >>
        self.prefix(funcname :: ConcatList(args, a -> "_" :: self._pfx(a.t)), args),

    _pfx := (self, t) >> Cond(
        IsComplexT(t),   "CPX",
        IsRealT(t),      "FLT",
        IsOrdT(t),       "INT",
        t = TUnknown,    "UNK",
        ObjId(t) = TSym, "SYM",
        IsPtrT(t),       "P" :: self._pfx(t.t),
        IsArrayT(t),     "A" :: self._pfx(t.t),
        IsVecT(t), Cond(
            IsComplexT(t.t), "FC" :: StringInt(t.size),
            IsRealT(t.t),    "FV" :: StringInt(t.size),
            IsOrdT(t.t),     "IV" :: StringInt(t.size),
            Error("Can't handle type ", t)
        ),
        Error("Can't handle type ", t)),

    # returns a tuple [suffix, args], where suffix is used for MUL_XXX or C_XXX,
    # and args are additional parameters into C_XXX
    _const := (self,o) >> Cond(
        # we assume here that complex constants with 0 imaginary parts have
        # been converted to TReal already
        o.t = TComplex, let(c:=Complex(o.v), re:=ReComplex(c), im:=ImComplex(c),
            Cond(re=0 and im=1,  ["I", []],
                re=0 and im=-1, ["NI", []],
                re=0,           ["IM", [im]],
                im < 0,         ["CPXN", [re, -im]],
                               ["CPX", [re, im]])),
        ObjId(o.t) = T_Complex, let(c:=Complex(o.v), re:=ReComplex(c), im:=ImComplex(c),
            Cond(re=0 and im=1,  ["I", []],
                re=0 and im=-1, ["NI", []],
                re=0,           ["IM", [im]],
                im < 0,         ["CPXN", [re, -im]],
                               ["CPX", [re, im]])),
        o.t = TReal and IsCyc(o.v),        ["FLT", [ReComplex(Complex(o.v))]],
        o.t = TReal,                       ["FLT", [o.v]],
        o.t = TInt,                        ["INT", [o.v]],
        o.t = TUnknown,                    ["INT", [o.v]], # NOTE: there is a bug that creates V(0) with TUnknown
        o.t = TString,                     ["STR", [o.v]],
        o.t = TBool,                       ["INT", [When(o.v in [true, 1], 1, 0)]],
        IsVecT(o.t), Cond(
            o.t.t = TReal, [Concat("FV",StringInt(o.t.size)), List(o.v, x->x.v)],
            o.t.t = TComplex, [Concat("FC",StringInt(o.t.size)), List(o.v, x->x.v)],
            o.t.t = TInt, [Concat("IV",StringInt(o.t.size)), List(o.v, x->x.v)],
            Error("Don't know how to handle constant of type ", o.t)
        ),
        Error("Don't know how to handle constant of type ", o.t)
    ),

    tcast := (self, o, i, is) >> Print("((", self.declare(o.args[1], [], i, is), ") ", self(o.args[2],i,is), ")"),

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

    gen := meth(self, subname, o, opts)
        local oo;
        self.opts := CopyFields(opts, rec(subName := subname));
        oo := self.preprocess(o);
        Print(self.header(subname, oo), Unparse(oo, self, 0, 4), self.footer(subname, oo));
    end,

    fld := (self, o, i, is) >> Print(self(o.loc, i, is), When(IsPtrT(o.loc.t), "->", "."), o.id),
    ufld := ~.fld,

    header := (self, subname, o) >> Print(
        self.generated_by,
        self.extraHeader,
        self.fileinfo(self.opts),
        DoForAll(self.includes, inc -> Print("#include ", inc, "\n")),
        DoForAll(self.opts.includes, inc -> Print("#include ", inc, "\n"))
    ),

    footer := Ignore,

    data := (self,o,i,is) >> Print(Blanks(i), self.genData(o.var, o.value), self(o.cmd, i, is)),

    func := (self, o, i, is) >> let(
        parameters:=Flat(o.params),
        id := Cond(o.id="transform" and IsBound(self.opts.subName),
                     self.opts.subName,
                   o.id="init"      and IsBound(self.opts.subName),
                     Concat("init_",self.opts.subName),
                   o.id="destroy"   and IsBound(self.opts.subName),
                     Concat("destroy_",self.opts.subName),
                   o.id),
        Print("\n", Blanks(i),
            When(IsBound(o.inline) and o.inline,"inline ",""),
            self.opts.funcModifier, self.declare(o.ret, var(id, o.ret), i, is), "(",
            DoForAllButLast(parameters, p->Print(self.declare(p.t, p,i,is), ", ")),
            When(Length(parameters)>0, self.declare(Last(parameters).t, Last(parameters),i,is), ""), ") ",
            "{\n",
            self(o.cmd, i+is, is),
            Blanks(i),
            "}\n")),

    # C99 style, loop var declared inside
    # - needed for correct operation of OpenMP
    # - simplifies function declarations (no need to worry about declaring loop vars)
    loop := (self, o, i, is) >> let(v := o.var, lo := o.range[1], hi := Last(o.range),
        Print(Blanks(i), "for(int ", v, " = ", lo, "; ", v, " <= ", hi, "; ", v, "++) {\n",
          self(o.cmd,i+is,is),
          Blanks(i), "}\n")),

    loopn := (self, o, i, is) >> let(v := o.var, lo := 0, hi := o.range, #NOTE: YSV what is the right thing here?
        Print(Blanks(i), "for(int ", self(v, i, is), " = ", self(lo, i, is), "; ", self(v, i, is), " < ", self(hi, i, is), "; ", self(v, i, is), "++) {\n",
          self(o.cmd,i+is,is),
          Blanks(i), "}\n")),

    program := (self,o,i,is) >> DoForAll(o.cmds, c -> self(c,i,is)),

    allocate := (self, o, i, is) >> Print(Blanks(i),
        self(o.loc, i, is), " = (", self.declare(o.exp.t, [],0,0), "*) MALLOC(sizeof(",
        self.declare(o.exp.t,[],0,0), ") * ", self(o.exp.size,i,is), ");\n"),

#        Blanks(i),"for(int iinit = 0; iinit <  ",self(o.exp.size,i,is),"; iinit++)\n",
#        Blanks(i),"    ", self(o.loc, i, is),"[iinit]=0;\n"),

    deallocate := (self, o, i, is) >> Print(
        Blanks(i),
        "FREE(",
        self(o.loc, i, is),
        ");\n"
    ),

    ret := (self, o, i, is) >> Print(Blanks(i), "return ", self(o.args[1], i+is, is), ";\n"),

    tcvt := (self, o, i, is) >> self.printf("(($1)($2))", [ o.args[1], o.args[2] ])
));

# can handle program/func, etc
#
Class(CUnparser, CUnparserBase, rec(
    gen := meth(self, subname, o, opts)
        local oo;
        self.opts := CopyFields(opts, rec(subName := subname));
        oo := self.preprocess(o);
		self.checkPrintRuleTree(o, opts);
        Print(self.header(subname, oo), Unparse(oo, self, 0, 4), self.footer(subname, oo));
    end,

    fld := (self, o, i, is) >> Print(self(o.loc, i, is), When(IsPtrT(o.loc.t), "->", "."), o.id),
    ufld := ~.fld,

    header := (self, subname, o) >> Print(
        self.generated_by,
        self.extraHeader,
        self.fileinfo(self.opts),
        DoForAll(self.includes, inc -> Print("#include ", inc, "\n")),
        DoForAll(self.opts.includes, inc -> Print("#include ", inc, "\n"))
    ),

	checkPrintRuleTree := meth(self, o, opts)
		if IsBound(opts.printRuleTree) and opts.printRuleTree and IsBound(o.ruletree) then
			Print("/* RuleTree:\nrt :=\n");
			Print(o.ruletree);
			Print("\n;\n*/\n\n");
		fi;
	end,

    footer := Ignore,

    data := (self,o,i,is) >> Print(Blanks(i), self.genData(o.var, o.value), self(o.cmd, i, is)),

    func := (self, o, i, is) >> let(
        parameters:=Flat(o.params),
        id := Cond(o.id="transform" and IsBound(self.opts.subName),
                     self.opts.subName,
                   o.id="init"      and IsBound(self.opts.subName),
                     Concat("init_",self.opts.subName),
                   o.id="destroy"   and IsBound(self.opts.subName),
                     Concat("destroy_",self.opts.subName),
                   o.id),
        Print("\n", Blanks(i),
            self.opts.funcModifier, self.declare(o.ret, var(id, o.ret), i, is), "(",
            DoForAllButLast(parameters, p->Print(self.declare(p.t, p,i,is), ", ")),
            When(Length(parameters)>0, self.declare(Last(parameters).t, Last(parameters),i,is), ""), ") ",
            "{\n",
            When(IsBound(self.opts.postalign), DoForAll(parameters, p->self.opts.postalign(p,i+is,is))),
            self(o.cmd, i+is, is),
            Blanks(i),
            "}\n")),

    # C99 style, loop var declared inside
    loop := (self, o, i, is) >> let(v := o.var, lo := o.range[1], hi := Last(o.range),
        Print(When(IsBound(self.opts.looppragma), self.opts.looppragma(o,i,is)),
          Blanks(i), "for(int ", v, " = ", lo, "; ", v, " <= ", hi, "; ", v, "++) {\n",
          self(o.cmd,i+is,is),
          Blanks(i), "}\n")),

    loopn := (self, o, i, is) >> let(v := o.var.id, lo := 0, hi := o.range,
        Print(Blanks(i), "for(int ", v, " = ", lo, "; ", v, " < ", self(hi,i,is), "; ", v, "++) {\n",
          self(o.cmd,i+is,is),
          Blanks(i), "}\n")),

    program := (self,o,i,is) >> DoForAll(o.cmds, c -> self(c,i,is)),

    allocate := (self, o, i, is) >> Print(Blanks(i),
        self(o.loc, i, is), " = (", self.declare(o.exp.t, [],0,0),
        "*) calloc(", self(o.exp.size,i,is), ", sizeof(", self.declare(o.exp.t,[],0,0), "));\n"),

    deallocate := (self, o, i, is) >> Print(
        Blanks(i),
        "free(",
        self(o.loc, i, is),
        ");\n"
    ),

    ret := (self, o, i, is) >> Print(Blanks(i), "return ", self(o.args[1], i+is, is), ";\n"),

    call := (self, o, i, is) >> Print(Blanks(i), o.args[1].id, self.pinfix(Drop(o.args, 1), ", "), ";\n"),

    fcall := (self, o, i, is) >> Print(self(o.args[1],0,0), "(", self.infix(Drop(o.args, 1), ", "), ")"),

    # structure definition
    struct := (self, o, i, is) >> Print(
        Blanks(i), "typedef struct {\n",
        DoForAll(o.fields, f ->
            Print(Blanks(i+is), self.declare(f.t, f, i+is, is), ";\n")
        ),
        Blanks(i), "} ", o.id, ";\n\n"
    )
));

# old style variable declarations. this is for older/stricter compilers (like gcc-2.5.2 used by simplescalar)
# {int v; for(v=0; ....  rather than for(int v=0; ...
# and
# { double x; .... rather than double x
Class(C89Unparser, CUnparser, rec(

    loop := (self, o, i, is) >> let(v := o.var, lo := o.range[1], hi := Last(o.range),
        Print(Blanks(i), "{int ", v, "; for(", v, " = ", lo, "; ", v, " <= ", hi, "; ", v, "++) {\n",
          self(o.cmd,i+is,is),
          Blanks(i), "}}\n")),

    loopn := (self, o, i, is) >> let(v := o.var.id, lo := 0, hi := o.range,
        Print(Blanks(i), "{int ", v, "; for(", v, " = ", lo, "; ", v, " < ", self(hi,i,is), "; ", v, "++) {\n",
          self(o.cmd,i+is,is),
          Blanks(i), "}}\n")),

    # exactly like the normal decl except wrapped in {}
#   decl := meth(self,o,i,is)
#       local arrays, other, l;
#       Print("{");
#       [arrays, other] := SplitBy(o.vars, x->IsArray(x.t));
#       DoForAll(arrays, v -> Print(Blanks(i), self.opts.arrayBufModifier, " ", self.declare(v.t, v, i, is), ";\n"));

#       if (Length(other)>0) then
#           other:=SortRecordList(other,x->x.t);
#           for l in other do
#              Sort(l, (a,b)->a.id < b.id);
#              Print(Blanks(i), self.declare(l[1].t, l, i, is), ";\n");
#           od;
#       fi;

#       self(o.cmd, i, is);
#       Print("}");
#   end,
));

# FFTX: Temporary to handle idiv(imod()).
CUnparser.("idivmod") := (self,o,i,is) >> self(imod(idiv(o.args[1], o.args[3]), o.args[2]), i, is);

#
# NOTE: These are obsolete names
#
CUnparserProg := CUnparser;
CMacroUnparserProg := CMacroUnparser;
C89UnparserProg := C89Unparser;


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

Class(RulesSMP, RuleSet);

RewriteRules(RulesSMP, rec(
    PSD_SMPSumRight := ARule(Compose,  [ @(1, [RCDiag, Diag, Prm, Scat, FormatPrm]), @(2, SMPSum) ],
     e -> let(s:=@(2).val, [ SMPSum(s.nthreads, s.tid, s.var, s.domain, @(1).val * s.child(1)) ])),

    PGD_SMPSumLeft  := ARule(Compose, [ @(1, SMPSum), @(2, [Prm, Gath, Diag, RCDiag, FormatPrm]) ],
     e -> let(s:=@(1).val, [ SMPSum(s.nthreads, s.tid, s.var, s.domain, s.child(1) * @(2).val) ])),

    PSD_SMPBarrierRight := ARule(Compose,  [ @(1, [RCDiag, Diag, Prm, Scat, FormatPrm]), @(2, SMPBarrier) ],
     e -> let(s:=@(2).val, [ SMPBarrier(s.nthreads, s.tid, @(1).val * s.child(1)) ])),

    PGD_SMPBarrierLeft  := ARule(Compose, [ @(1, SMPBarrier), @(2, [Prm, Gath, Diag, RCDiag, FormatPrm]) ],
     e -> let(s:=@(1).val, [ SMPBarrier(s.nthreads, s.tid, s.child(1) * @(2).val) ])),

    Drop_GrpSPSum := Rule([Grp, @(1, SMPSum)], e -> @(1).val),
    Drop_GrpISum := Rule([Grp, @(1, ISum)], e -> @(1).val),

    SMP_ISum := Rule([SMP, @(1), @(2), @(3, ISum)], e ->
        SMPSum(@(1).val, @(2).val, @(3).val.var, @(3).val.domain, @(3).val.child(1))),
));

RewriteRules(RulesRC, rec(
    RC_SMPSum := Rule([RC, @(1, SMPSum)],
        e -> let(s:=@(1).val, SMPSum(s.nthreads, s.tid, s.var, s.domain, RC(s.child(1))))),

    RC_SMPBarrier := Rule([RC, @(1, SMPBarrier)],
        e -> let(s:=@(1).val, SMPBarrier(s.nthreads, s.tid, RC(s.child(1))))),
));

ext: gi


text:
# The field addition group as a permutation group.
BindGlobal("FieldAdditionPermutationGroup",
    q -> Group(List(Elements(Basis(GF(q))),
        g -> Permutation(g, GF(q), function(x, y) return x+y; end)))
);

# The field multiplication group as a permutation group.
BindGlobal("FieldMultiplicationPermutationGroup",
    q -> CyclicGroup(IsPermGroup, q-1)
);

# The field exponentiation group as a permutation group.
BindGlobal("FieldExponentiationPermutationGroup",
    q -> Action(Group(FrobeniusAutomorphism(GF(q))), GF(q))
);

# The group of even permutations of columns of a matrix
# as a permutation group over matrix elements.
BindGlobal("MatrixColumnEvenPermutationGroup", function(d, e)
    return Action(AlternatingGroup(e), Cartesian([1..d], [1..e]),
        function(x, g)
            return [x[1], OnPoints(x[2], g)];
        end);
end);

# The group of permutations of columns of a matrix
# as a permutation group over matrix elements.
BindGlobal("MatrixColumnPermutationGroup", function(d, e)
    return Action(SymmetricGroup(e), Cartesian([1..d], [1..e]),
        function(x, g)
            return [x[1], OnPoints(x[2], g)];
        end);
end);

# The group of simultaneous permutations of rows and columns
# of a square matrix as a permutation group over matrix elements.
BindGlobal("MatrixRowColumnPermutationGroup", function(d)
    return Action(SymmetricGroup(d), Cartesian([1..d], [1..d]), OnTuples);
end);

# The wreath product of two symmetric groups.
BindGlobal("WreathProductSymmetricGroups", function(m, n)
    local g;
    if not (IsInt(m) and IsInt(n) and m >= 0 and n >= 0) then
        Error("m or n not a nonnegative integer");
        return fail;
    fi;
    if m <= 1 then
        return SymmetricGroup(n);
    elif n <= 1 then
        return SymmetricGroup(m);
    fi;
    g := Concatenation(GeneratorsOfGroup(SymmetricGroup(m)),
                        [PermList(Concatenation([m+1..2*m], [1..m]))]);
    if n > 2 then
        Add(g, PermList(Concatenation([m+1..n*m], [1..m])));
    fi;
    return Group(g);
end);

# The automorphism group of a (hyper)conic
# in a Desarguesian projective plane of order q.
BindGlobal("HyperconicAutomorphismGroup", q -> Group([
    [[-Z(q)^0, 0*Z(q), 0*Z(q)],
     [ 0*Z(q), 0*Z(q), Z(q)^0],
     [ 0*Z(q), Z(q)^0, 0*Z(q)]],
    [[-Z(q)^0, 0*Z(q), -2*Z(q)^0],
     [ Z(q)^0, Z(q)^0,   Z(q)^0],
     [ 0*Z(q), 0*Z(q),   Z(q)^0]],
    [[-Z(q),   0*Z(q), -2*Z(q)^0],
     [ Z(q),   Z(q)^2,   Z(q)^0],
     [ 0*Z(q), 0*Z(q),   Z(q)^0]],
    Z(q)*IdentityMat(3, GF(q))
]));

ext: gap

max_stars_repo_name: jaanos/gap-graphs

max_issues_repo_name: jaanos/gap-graphs

max_forks_repo_name: jaanos/gap-graphs


text:
NewRulesFor(PrunedMDPRDFT, rec(
    PrunedMDPRDFT_Base := rec(
        applicable     := nt -> Length(nt.params[1]) = 1,

        children       := nt -> [[ PrunedPRDFT(nt.params[1][1], nt.params[2]).withTags(nt.getTags()) ]],

        apply          := (nt, C, Nonterms) -> C[1]
    ),

    PrunedMDPRDFT_RowCol1 := rec(
        applicable := nt -> Length(nt.params[1]) > 1 and not nt.hasTags(),

        children  := nt -> [[ PrunedMDDFT(DropLast(nt.params[1], 1), nt.params[3], 1, DropLast(nt.params[2], 1)),
                              PrunedPRDFT(Last(nt.params[1]), nt.params[3], 1, Last(nt.params[2])) ]],

        apply := (nt, C, cnt) ->  RC(Tensor(C[1], I(C[2].dims()[1]/2))) * Tensor(I(Product(List(cnt[1].params[4], i->Length(i)))), C[2])
    )
));

NewRulesFor(PrunedIMDPRDFT, rec(
    PrunedIMDPRDFT_Base := rec(
        applicable     := nt -> Length(nt.params[1]) = 1,

        children       := nt -> [[ PrunedIPRDFT(nt.params[1][1], nt.params[2]).withTags(nt.getTags()) ]],

        apply          := (nt, C, Nonterms) -> C[1]
    ),

    PrunedIMDPRDFT_RowCol1 := rec(
        applicable := nt -> Length(nt.params[1]) > 1 and not nt.hasTags(),

        children  := nt -> [[ PrunedIPRDFT(Last(nt.params[1]), nt.params[3], 1, Last(nt.params[2])),
                              PrunedIMDDFT(DropLast(nt.params[1], 1), nt.params[3], 1, DropLast(nt.params[2], 1)) ]],

        apply := (nt, C, cnt) -> Tensor(I(Product(List(cnt[2].params[4], i->Length(i)))), C[1]) * RC(Tensor(C[2], I(C[1].dims()[2]/2)))
    )
));

max_stars_repo_name: mikefranusich/spiral-package-fftx

max_issues_repo_name: mikefranusich/spiral-package-fftx

max_forks_repo_name: mikefranusich/spiral-package-fftx


text:
#
# Read("~/Workspace/groupsSB/epi/A2/testRootGroup.gi");
#

type:="A";
rank:=2;
nr_pos_roots:=3;

L:= SimpleLieAlgebra(type,rank,Rationals);
cb0:=CanonicalBasis(L);
V:= HighestWeightModule( L, highest_weight );
extA:=List([1..Dimension(V)],i->ExteriorPowerOfAlgebraModule( V, i ));
extA_basis:=List(extA,i->Basis(i));

#
# change basis order of first exteriror power to match chevalley basis order
#
b:=extA_basis[1];
#SetBasis(extA[1],bbb);
#extA_basis[1]:=Basis(extA[1]);
#extA_basis[1]:=
#b:=[
#    b[7],
#    b[6],
#    b[8],   # alpha + beta
#    b[3],
#    b[2],
#    b[1],
#    b[5],
#    b[4],
#    ];

#plist:=[7,6,8,3,2,1,5,4];
plist:=[3,2,1,6,7,8,4,5];
bbb:=b{plist};
apply_plist:=function(mat)
    local result;
    result:=List(mat,i->i{plist});
    result:=TransposedMat(result);
    result:=List(result,i->i{plist});
    result:=TransposedMat(result);
    return result;
end;

semne:=DiagonalMat([1,-1,1,-1, 1,1,-1,1]);
apply_signs:=function(mat)
    local result;
    result := semne*mat*semne;
    return result;
end;

ext1e:=function(e)
	local extb,result,v;
    extb:=extA_basis[1];

	result:=[];
	for v in extb do
		Append(result,[Coefficients(extb,e^v)]);
	od;
	result:=TransposedMat(result);
	return result;
end;

ade:=function(e)
	local result,v;
	result:=[];
	for v in cb0 do
		Append(result,[Coefficients(cb0,e*v)]);
	od;
	result:=TransposedMat(result);
	return result;
end;

ext1_root_group:=function(index,t)
	local ee,tmp,result,i;
	ee:=ext1e(cb0[index]);
	tmp:=ee;
	result:=tmp^0;
	i:=1;
	while Length(Set(Concatenation(tmp)))<>1 do
		result:=result+t^i*tmp/Factorial(i);
		i:=i+1;
		tmp:=tmp*ee;
	od;
	return result;
end;

ad_root_group:=function(index,t)
	local ee,tmp,result,i;
	ee:=ade(cb0[index]);
	tmp:=ee;
	result:=tmp^0;
	i:=1;
	while Length(Set(Concatenation(tmp)))<>1 do
		result:=result+t^i*tmp/Factorial(i);
		i:=i+1;
		tmp:=tmp*ee;
	od;
	return result;
end;

#
# Test:
#

# ade1
yyy:=ext1e(cb0[1]);
yyy:=apply_plist(yyy);
yyy:=apply_signs(yyy);
xxx:=ade0(cb0[1]);
Display(yyy);
Display(xxx);
Display(xxx=yyy);

# root group 1
yyy:=ext1_root_group(1,1);
yyy:=apply_plist(yyy);
yyy:=apply_signs(yyy);
xxx:=ad_root_group(1,1);
Display(yyy);
Display(xxx);
Display(xxx=yyy);

# ade2 --------------------> Distinct
yyy:=ext1e(cb0[2]);
yyy:=apply_plist(yyy);
yyy:=apply_signs(yyy);
xxx:=ade0(cb0[2]);
Display(yyy);
Display(xxx);
Display(xxx=yyy);

# root group 2 --------------------> Distinct
yyy:=ext1_root_group(2,1);
yyy:=apply_plist(yyy);
yyy:=apply_signs(yyy);
xxx:=ad_root_group(2,1);
Display(yyy);
Display(xxx);
Display(xxx=yyy);

max_stars_repo_name: iuliansimion/groupsSB

max_issues_repo_name: iuliansimion/groupsSB

max_forks_repo_name: iuliansimion/groupsSB


text:
# Return the list L after applying Knuth shuffle. GAP also has the function Shuffle, which does the same.
ShuffleAlt := function(a)
    local i, j, n, t;
    n := Length(a);
    for i in [n, n - 1 .. 2] do
        j := Random(1, i);
        t := a[i];
        a[i] := a[j];
        a[j] := t;
    od;
    return a;
end;

# Return a "Permutation" object (a permutation of 1 .. n).
# They are printed in GAP, in cycle decomposition form.
PermShuffle := n -> PermList(ShuffleAlt([1 .. n]));

ShuffleAlt([1 .. 10]);
# [ 4, 7, 1, 5, 8, 2, 6, 9, 10, 3 ]

PermShuffle(10);
# (1,9)(2,3,6,4,5,10,8,7)

# One may also call the built-in random generator on the symmetric group :
Random(SymmetricGroup(10));
(1,8,2,5,9,6)(3,4,10,7)

max_issues_repo_name: seanwallawalla-forks/RosettaCodeData

max_forks_repo_name: seanwallawalla-forks/RosettaCodeData


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

ImportAll(paradigms.common);
ImportAll(paradigms.vector);
Import(formgen);

buildBench := function(sizes, isa, optrec, filter, name)
    local opts, goodsizes, dpbench, exp;

    opts := SIMDGlobals.getOpts(isa, optrec);
    goodsizes := Filtered(sizes, i->let(rt := RandomRuleTreeDP(TRC(DFT(i)).withTags(opts.tags), opts), IsRuleTree(rt) and rt.children[1].children[1].rule in filter));
    dpbench :=  doSimdDft(goodsizes, isa, optrec);
    exp := Filtered(RecFields(dpbench.exp), i-> not i in SystemRecFields)[1];

    dpbench.exp.(exp).name := Concat(exp, "_", name);

    dpbench.fileTransform := (exp, t, opts) -> Concat(isa.name, "_", name, "_", "dft_ic_", StringInt(Rows(t)/2), ".c");
    dpbench.funcTransform := (exp, t, opts) -> Concat("dft", StringInt(Rows(t)/2));
    dpbench.txtFileName   := (exp, runMethod) -> Concat(isa.name, "_", name, "_dft_ic_", SubString(runMethod, 5), ".txt");
    dpbench.exp.(exp).hashFile := Concat(isa.name, "_", name, "_dft_ic_", "DP", ".hash");
    dpbench.exp.(exp).outputVecStatistics := true;

    return dpbench;
end;

buildExperiments := function(isas, sizes, setups)
    local benches, isa, vect, vmode, exp, b;

    benches := [];
    for isa in isas do
        for vect in SIMDGlobals.experiments.vects do
            for vmode in SIMDGlobals.experiments.vmodes do
                for exp in setups do
                    b := buildBench(sizes, isa, ApplyFunc(CopyFields, [vmode, vect]::exp[1]), exp[2], Concat(vect.name, "_", vmode.name, "_", exp[3]));
                    Add(benches, b);
                    PrintLine(isa, ", ", vect.name, ", ", vmode.name, ", ", exp[3], "", ": ", List(b.exp.(Filtered(RecFields(b.exp), i-> not i in SystemRecFields)[1]).benchTransforms, i->Rows(i)/2));
                od;
            od;
        od;
    od;

    return benches;
end;

# preset for unrolled DFT code
Class(unrollDFT, rec(
    # scalar algorithms
    PD := true,
    PFA := true,
    PFA_maxSize := 10000,
    RealRader := true,
    Rader := true,
    Rader_maxSize := 10000,
    PRDFT:=true,
    URDFT:= true,
    URDFT_maxRadix := 10000,
    CT := true,
    minCost := true,
    Mincost_maxSize := 10000,
    splitRadix := false,
    SplitRadix_maxSize := 10000,
    # basic vector configuration
    stdTTensor := false,
    pushTag := true,
    flipIxA := true,
    useConj := false,
    interleavedComplex := true,
    # standard codegen
    verify := true,
    globalUnrolling := 10000,
    includeMath := true,
));

# preset for looped DFT code
Class(loopDFT, rec(
    # scalar algorithms
    PD := true,
    PFA := true,
    RealRader := true,
    Rader := true,
    PRDFT:=false,
    URDFT:= true,
    CT := true,
    minCost := true,
    splitRadix := false,
    # basic vector configuration
    stdTTensor := false,
    pushTag := true,
    flipIxA := true,
    useConj := false,
    interleavedComplex := true,
    # standard codegen
    verify := true,
    includeMath := true,
));

Class(baseUnroll, rec(
    PFA_maxSize := 16,
    Rader_maxSize := 16,
    Mincost_maxSize := 16,
    URDFT_maxRadix := 16,
    globalUnrolling := 32,
));

# vectorization methods: real/complex
Class(cxVect, rec(realVect := false, cplxVect := true));
Class(realVect, rec(realVect := true, cplxVect := false));

# vectorization: split L vs. SVCT
Class(vecmem, rec(svct := true, splitL := false));
Class(stride, rec(svct := false, splitL := true));

# zero padding
Class(noPadd, rec(oddSizes := false));
Class(padd, rec(oddSizes := true));

# top-level algorithms
Class(F2, rec(tsplVBase := true, tsplCxVBase := true));
Class(CT, rec(tsplCT := true, tsplPFA := false, tsplRader := false, tsplBluestein := false, tsplVBase := false, tsplCxVBase := false));
Class(PFA, rec(tsplCT := false, tsplPFA := true, tsplRader := false, tsplBluestein := false, tsplVBase := false, tsplCxVBase := false));
Class(Rader_CT, rec(tsplCT := true, tsplPFA := false, tsplRader := true, raderAvoidSizes := [ ], tsplBluestein := false, tsplVBase := false, tsplCxVBase := false));
Class(Rader_PFA, rec(tsplCT := false, tsplPFA := true, tsplRader := true, raderAvoidSizes := [ ], tsplBluestein := false, tsplVBase := false, tsplCxVBase := false));
Class(Bluestein_CT, rec(tsplCT := true, tsplPFA := false, tsplRader := false, tsplBluestein := true,
    bluesteinMinPrime := 997, bluesteinExtraSizes := [ 2..100 ], tsplVBase := false, tsplCxVBase := false));

# all algorithms turned on -- unrolled code only
Class(Small_DFT_RuleSet, rec( tsplCT := true, tsplPFA := true, tsplRader := true, raderAvoidSizes := [ 47, 59, 107 ], tsplBluestein := true, bluesteinMinPrime := 997,
    bluesteinExtraSizes := [ 23, 46, 47, 49, 59, 67, 79, 83, 94, 103, 106, 107, 115 ], tsplVBase := true, tsplCxVBase := true));

SIMDGlobals.experiments := rec(
    vects := [realVect, cxVect],
    vmodes := [vecmem],
    experiments := [
        [[unrollDFT, noPadd, CT], [DFT_tSPL_CT], "SVCT"], # original SVCT
        [[unrollDFT, padd, CT], [DFT_tSPL_CT], "paddSVCT"], # zeropadded SVCT [ICASSP:07]
        [[unrollDFT, padd, PFA], [DFT_tSPL_GoodThomas], "PFA"], # PFA
        [[unrollDFT, padd, F2], [DFT_tSPL_VBase, DFT_tSPL_CxVBase], "baseF2"], # Base case
        [[unrollDFT, noPadd, Rader_CT], [DFT_tSPL_Rader],"Rader+SVCT"], # Rader + SVCT == best Rader sizes
        [[unrollDFT, padd, Rader_CT], [DFT_tSPL_Rader], "Rader+paddSVCT"], # Rader with CT
        [[unrollDFT, padd, Rader_PFA], [DFT_tSPL_Rader], "Rader+PFA"], # Rader with PFA
        [[unrollDFT, noPadd, Bluestein_CT], [DFT_tSPL_Bluestein], "Bluestein"], # Bluestein for all sizes
    ],
    benches := rec(
        unrolled := [[[unrollDFT, padd, Small_DFT_RuleSet], [DFT_tSPL_CT, DFT_tSPL_GoodThomas,
            DFT_tSPL_VBase, DFT_tSPL_CxVBase, DFT_tSPL_Rader, DFT_tSPL_Bluestein], "smallDFT"]],
        looped := [[[loopDFT, baseUnroll, noPadd, CT], [DFT_tSPL_CT], "loopSVCT"]]
    )
);


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

Import(paradigms.common, paradigms.vector.breakdown);

#   NOTE: Phase out!!
Enable_tSPL := function()
    Print("\n\nImportant change: \"Enable_tSPL\" is to be phased out -- use \"tSPL_Globals.getDPOpts()\" and ask Franz\n\n");
    SwitchRulesQuiet(DFT, []);
    SwitchRulesQuiet(WHT, []);
    SwitchRulesQuiet(MDDFT, []);
    SwitchRulesName([DFT_Base, DFT_Rader, DFT_SplitRadix, DFT_tSPL_CT, DFT_CT, WHT_Base, WHT_tSPL_BinSplit, MDDFT_Base, MDDFT_tSPL_RowCol, WHT_tSPL_Pease], true);
    SwitchRulesName([DFT_vecrec, IxA_L_vecrec, DFT_vecrec_T, L_IxA_vecrec], USE_VECREC);
    SwitchRulesName([IxA_L_vec, L_IxA_vec], true);
end;

Class(tSPL_Globals, rec(
    getDPOpts := self >> rec(breakdownRules := self.breakdownRules()),
    breakdownRules := self >>
        rec(
            DFT := [ DFT_Base, DFT_Rader, DFT_SplitRadix, DFT_CT_Mincost, DFT_GoodThomas, DFT_tSPL_CT, DFT_tSPL_Rader, DFT_CT ],
            WHT := [ WHT_Base, WHT_tSPL_BinSplit, WHT_tSPL_Pease ],
            MDDFT := [ MDDFT_Base, MDDFT_tSPL_RowCol ],
            TTensor := [ AxI_IxB, IxB_AxI ],
            TTensorI := [ IxA_base, AxI_base, IxA_L_base, L_IxA_base ],
            TL := [ L_base ],
#D            TTag := [ TTag_down ],
            TRC := [TRC_tag ],
            TDiag := [ TDiag_tag ],
            TRaderMid:= [ TRaderMid_tag ],
            TDirectSum := [ A_dirsum_B ],
            TCompose := [ TCompose_tag ],
            TICompose := [ TICompose_tag ],
            TGrp := [ TGrp_tag ],
            TDR := [TDR_base ]
        )
));

#    [DFT_vecrec, IxA_L_vecrec, DFT_vecrec_T, L_IxA_vecrec], USE_VECREC
#    [IxA_L_vec_LS, L_IxA_vec_LS], USE_LOOPSPLITTING


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

EmptyCS := [
    Compile.pullDataDeclsRefs,
    Compile.declareVars
];

BaseCS := [
    c -> Compile.pullDataDeclsRefs(c),
    c -> Compile.fastScalarize(c),
    c -> UnrollCode(c),
    c -> FlattenCode(c),
    c -> UntangleChain(c),
    CopyPropagate,
    (c, opts) -> HashConsts(c, opts),
];

NoCSE := Concatenation(BaseCS, [
    Compile.declareVars
]);

NoSchedCSE_CS := Concatenation(BaseCS, [
    (c, opts) -> BinSplit(c, opts),
    CSE,  MarkDefUse, CopyPropagate,
    Compile.declareVars
]);

SimpleCS := Concatenation(BaseCS, [
    (c, opts) -> BinSplit(c, opts),
    CSE, MarkDefUse, DFSChain, CopyPropagate,
    Compile.declareVars
]);

# IsCoarseType: checks if <coarse_t> is more general version of <fine_t> data type as defined by UnifyPair.
#        ex: TReal is a general version of T_Real(32) data type.
IsCoarseType := (coarse_t, fine_t) -> When(
    coarse_t = fine_t, false,
    Try(UnifyPair(fine_t, coarse_t)) = [true, fine_t]
);

# FixValueTypes: unifies value type with the type of surrounding expression.
#        SSE unparser needs this for figuring out actual data type of constants.
#        Fixed point backends rely on this to convert constants to fixed point...
#
FixValueTypes := c -> SubstTopDownRulesNR(c, rec(
    fixValueTypes := Rule(
        @@(1, Value, (x, cx) ->
            ObjId(Last(cx.parents)) in
	        [add, sub, mul, bin_and, bin_xor, bin_or, absdiff, absdiff2, idiv, ddiv] and
		IsCoarseType(x.t, Last(cx.parents).t) and not IsPtrT(Last(cx.parents).t)),
	(e, cx) -> Last(cx.parents).t.value(e.v)
    )
));

DerefNthCode := c -> SubstTopDownRules(c, rec(
    deref_nth := Rule(
	[nth, @(1).cond(e -> not(ObjId(e) in [Value, param])), @(2)],
	e -> let(
	    b := @(1).val, idx := @(2).val,
	    Cond(
		ObjId(idx) = add, deref(ApplyFunc(add, [b] :: idx.args)),
		ObjId(idx) = sub, deref(ApplyFunc(add, [b] :: [idx.args[1], neg(idx.args)])),
                                  deref(b + idx))))
));

NthDerefCode := c -> SubstTopDownRules(c, rec(
    deref_var := Rule([deref, @(1, var)], e -> nth(@(1).val, TInt.value(0))),
    deref_add := Rule([deref, [add, @(1, var), @(2, Value)]], e -> nth(@(1).val, @(2).val))
));

BaseIndicesCS := [
    c -> Compile.pullDataDeclsRefs(c),
    c -> Compile.fastScalarize(c),
    c -> UnrollCode(c),
    c -> FlattenCode(c),
    c -> UntangleChain(c),
    (c, opts) -> CopyPropagate.initial(c, opts),
    (c, opts) -> HashConsts(c, opts),
    c -> MarkDefUse(c),
    (c, opts) -> BinSplit(c, opts),
    c -> MarkDefUse(c),
    CopyPropagate, # does CSE
];

# Uses a fast (no strength reduction) final CopyPropagate pass, which
# kicks out vars used only once. Sometimes (MMM?) its not good (prevents hoisting)
# and then IndicesCS2 should be used.
#
IndicesCS0 := Concatenation(BaseIndicesCS, [
    c -> MarkDefUse(c),
    # kicks out vars used only once or never
    (c, opts) -> CopyPropagate.fast(c, CopyFields(opts, rec(autoinline := true))),
    (c, opts) -> Cond(opts.finalBinSplit, BinSplit(c, opts), c),
    (c, opts) -> Cond(IsBound(opts.scheduler), opts.scheduler(c, opts), c),
    c -> Compile.declareVars(c),
]);

# Uses a full final CopyPropagate pass, which kicks out vars used only once
# and properly simplifies out redundant double butterfly structures, i.e. F(2)*F(2)
#
IndicesCS := Concatenation(BaseIndicesCS, [
    c -> MarkDefUse(c),
    (c, opts) -> CopyPropagate(c, CopyFields(opts, rec(autoinline := true))),
    c -> MarkDefUse(c),
    (c, opts) -> CopyPropagate(c, CopyFields(opts, rec(autoinline := true))),
    (c, opts) -> Cond(opts.finalBinSplit, BinSplit(c, opts), c),
    (c, opts) -> Cond(IsBound(opts.scheduler), opts.scheduler(c, opts), c),
    c -> FixValueTypes(c),
    c -> Compile.declareVars(c)
]);

# IndicesCS + extra CopyPropagate pass to do DAG pruning
IndicesCS_Prune := Concatenation(BaseIndicesCS, [
    c -> MarkDefUse(c),
    (c, opts) -> CopyPropagate(c, CopyFields(opts, rec(autoinline := true))),
    c -> MarkDefUse(c),
    (c, opts) -> CopyPropagate(c, CopyFields(opts, rec(autoinline := true))),
    (c, opts) -> Cond(opts.finalBinSplit, BinSplit(c, opts), c),
    (c, opts) -> Cond(IsBound(opts.scheduler), opts.scheduler(c, opts), c),
    c -> FixValueTypes(c),
    c -> Compile.declareVars(c)
]);

# Does not use a final CopyPropagate pass, to keep variables used once, and
# not prevent hoisting.
#
IndicesCS2 := Concatenation(BaseIndicesCS, [
    # - kicking out variables never used is fine.
    # - kicking out variables used once should be a GLOBAL pass because
    # - right now it prevents hoisting
    (c, opts) -> Cond(opts.finalBinSplit, BinSplit(c, opts), c),
    (c, opts) -> Cond(IsBound(opts.scheduler), opts.scheduler(c, opts), c),
    c -> FixValueTypes(c),
    c -> Compile.declareVars(c)
]);

IndicesCS_FMA := Concatenation(BaseIndicesCS, [
    DoFMA,
    MarkDefUse, #
    (c, opts) -> CopyPropagate(c, CopyFields(opts, rec(autoinline := true))),
    (c, opts) -> Cond(opts.finalBinSplit, BinSplit(c, opts), c),
    (c, opts) -> Cond(IsBound(opts.scheduler), opts.scheduler(c, opts), c),
    c -> FixValueTypes(c),
    c -> Compile.declareVars(c)
]);

IndicesCS_Fixed := (bitwidth, fracbits) -> (
    IndicesCS ::
    [ c -> FixedPointCode(c, bitwidth, fracbits) ]
);

IndicesCS_FixedNew :=
    IndicesCS ::
    [ (c, opts) -> FixedPointCode2(c) ];

IndicesCS2_FMA := Concatenation(BaseIndicesCS, [
    DoFMA,
    CopyPropagate,
    (c, opts) -> Cond(opts.finalBinSplit, BinSplit(c, opts), c),
    (c, opts) -> Cond(IsBound(opts.scheduler), opts.scheduler(c, opts), c),
    c -> FixValueTypes(c),
    c -> Compile.declareVars(c)
]);

IndicesCS_CXFMA := Concatenation(BaseIndicesCS, [
    DoCXFMA,
    MarkDefUse, #
    (c, opts) -> CopyPropagate(c, CopyFields(opts, rec(autoinline := true))),
    (c, opts) -> Cond(opts.finalBinSplit, BinSplit(c, opts), c),
    (c, opts) -> Cond(IsBound(opts.scheduler), opts.scheduler(c, opts), c),
    c -> FixValueTypes(c),
    c -> Compile.declareVars(c)
]);

IndicesCS2_CXFMA := Concatenation(BaseIndicesCS, [
    DoCXFMA,
    CopyPropagate,
    (c, opts) -> Cond(opts.finalBinSplit, BinSplit(c, opts), c),
    (c, opts) -> Cond(IsBound(opts.scheduler), opts.scheduler(c, opts), c),
    c -> FixValueTypes(c),
    c -> Compile.declareVars(c)
]);

# OLD STUFF

# RCSE_CS := Concatenation(BaseCS, [
#     (c, opts) -> BinSplit(c, opts), RCSE,
#     MarkDefUse, DFSChain, CopyPropagate,
#     Compile.declareVars
# ]);

# FFTW_CS := Concatenation(BaseCS, [
#     (c, opts) -> BinSplit(c, opts), RCSE,
#     MarkDefUse, FFTWScheduleAssignments, CopyPropagate,
#     Compile.declareVars
# ]);

#
# FMA
#
FMA_CS := Concatenation(BaseCS, [
    (c, opts) -> BinSplit(c, opts), (c, opts) -> BinSplit(c, opts), CSE,
    MarkDefUse, DFSChain, CopyPropagate,
    DoFMA,
    Compile.declareVars
]);

FMA_FFTW_CS := Concatenation(BaseCS, [
    (c, opts) -> BinSplit(c, opts), (c, opts) -> BinSplit(c, opts), CSE,
    MarkDefUse, FMA, ClearDefUse,
    MarkDefUse, FFTWScheduleAssignments, CopyPropagate,
    DoFMA,
    Compile.declareVars
]);

# # seems to be slower
# NewUnrollCS := [
#     myUnrollCode, CopyPropagate,
#     HashConstantsCode,
#     MarkDefUse, DFSChain, CopyPropagate,
#     Compile.declareVars
# ];

# CompileStrategyFull := [
#     Compile.pullDataDeclsRefs,
#     UnrollCode, FlattenCode, SSA,  CopyPropagate,
#     FoldIf, SSA, CopyPropagate,   # Remove dead IF branches
# #    Compile.scalarize,
#     SSA, CopyPropagate,
#     EliminatePhiSSA, CopyPropagate, # Eliminate Phi functions
#     HashConstantsCode,
#     (c, opts) -> BinSplit(c, opts), CSE, CopyPropagate,  # CSE
#     MarkDefUse, #DFSChain,
#     Compile.declareVars
# ];

# This compile strategy is safe for IFs inside basic blocks. but does not
# fully perform copy propagation. Thus the name 'conservative'. To fully and
# safely optimize, we need to extend the CopyPropagate pass
#
conservativeCompileSSA := [
    Compile.pullDataDeclsRefs, # -- 1
    Compile.fastScalarize,     # -- 2
    UnrollCode,    # -- 3
    FlattenCode,   # -- 4
#    SimpIndicesCode, # -- 5   (YSV: simpIndices was disabled before, not clear why)
    FoldIf,        # -- 6   FoldIf happens before SSA and SSA has to happen before Copyprop
    SSA,           # -- 7
    UntangleChain, # -- 8
    (c, opts) -> CopyPropagate(c, CopyFields(opts, rec(doScalarReplacement:=false))),  # -- 9
    (c, opts) -> HashConsts(c, opts),                                                  # -- 10
    (c, opts) -> When(IsBound(opts.useDeref) and opts.useDeref, DerefNthCode(c), c), # -- 11
    MarkPreds,
    (c, opts) -> BinSplit(c, opts),                                                        # -- 12, 13
    MarkDefUse,
    (c, opts) -> CopyPropagate(c, CopyFields(opts, rec(doScalarReplacement:=false))), # 14
    EliminatePhiSSA,
    Compile.declareVars
];

CompileSSA := [
    Compile.pullDataDeclsRefs,
    Compile.fastScalarize, UnrollCode,
    FlattenCode,
    FoldIf,
    UntangleChain,
    CopyPropagate,
    SSA,
    (c, opts) -> HashConsts(c, opts),
    (c, opts) -> When(IsBound(opts.useDeref) and opts.useDeref, DerefNthCode(c), c),
    MarkPreds,
    (c, opts) -> BinSplit(c, opts),
    ClearDefUse,
    MarkDefUse,
    CopyPropagate,
    EliminatePhiSSA,
    CopyPropagate,
    Compile.declareVars
];


text:
Read( "fibonacci.gi" );

ForAll( [ 1 .. 10 ], i -> fibonacci_rec( i ) = fibonacci_internal( i ) );

ForAll( [ 1 .. 10 ], i -> fibonacci_iterative( i ) = fibonacci_internal( i ) );

iterator := fibonacci_iterator( );

ForAll( [ 1 .. 100 ], i -> iterator( ) = fibonacci_internal( i ) );

max_stars_repo_name: Mynogs/Algorithm-Implementations

max_issues_repo_name: Mynogs/Algorithm-Implementations

max_forks_repo_name: Mynogs/Algorithm-Implementations


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

#F HashIndex and HashEqual Functions
#F ---------------------------------
#F

#F HashLookup( hashTable, key )
#F    returns the data associated with key in hashTable.
#F    returns false if key is not found in hashTable.
#F
HashLookup := function( hashTable, key )
   local index, entry, pos, chash, temp;

   if not IsHashTable( hashTable ) then
      Error( "<hashTable> is not a valid HashTable" );
   fi;

   index := hashTable.indexFun( key, hashTable.size );

   if not ( index > 0 and index <= hashTable.size ) then
      Error( "Did not obtain a valid hash index for key" );
   fi;

   if IsBound( hashTable.entries[index] ) then
      entry := hashTable.entries[index];
      pos := PositionProperty( entry, a -> hashTable.equalFun(a.key, key) );

      if pos = false then
	  return false;
      else
 #      # catch the case of hash-identical but not identical spls
#       elif IsSPL(key) and not IsIdenticalSPL(entry[pos].key, key) then
#             chash := Copy(entry[pos].data);
#             if(IsList(chash)) then
#                for index in [1..Length(chash)] do
#                   chash[index].ruletree :=
# 		  # NOTE:
#                   spiral.formgen.ApplyRuleTreeSPL(entry[pos].data[index].ruletree, key);
#                od;
#             else
# 		# NOTE:
#                chash.ruletree :=
#                spiral.formgen.ApplyRuleTreeSPL(entry[pos].data.ruletree, key);
#             fi;
#             return chash;
#       else
      return entry[pos].data;
      fi;
   else
      return false;
   fi;
end;

MultiHashLookup := function(hashes, key)
    local h, lkup;
    for h in hashes do
        lkup := HashLookup(h, key);
	if lkup<>false then return lkup; fi;
    od;
    return false;
end;

Declare(HashIndexSPLRec);

HashIndexParams := function(param, index)
     local p;
     # hash symbolic Value(type, val) simply as <val>
     if IsValue(param) then param := param.v; fi;

     if IsFFE(param) then
     index := index + 1 + When(param <> GF(2).zero, 0, 1);
     elif IsInt(param) then
	 index := index * (param+1);
     elif IsString(param) then
	 index := index * (Length(param)+1);

     elif IsFloat(param) then
	 index := index + 1;
     elif IsRat(param) then
	 index := index + 1;
     elif IsFloat(param) then
	 index := index + 1;
      ## NOTE: How to handle bools???
     elif IsBool(param) then
	 index := index * When(param, 2,3);

     elif IsCyc(param) then
	 index := index + 1;
     elif IsList(param) then
	 for p in param do
	     index := HashIndexParams(p, index);
	 od;
     elif IsMat(param) then
	 index := index * Product(DimensionsMat(param));
     elif IsPolynomial(param) then
	 index := index * LaurentDegree(param);

      # MP: now we have transforms as parameter and thus need to handle this case
      # by calling the function recursively
     elif IsSPL(param) then
	 index := index + HashIndexSPLRec(param);

     elif IsClass(ObjId(param)) then
         index := index + BagAddr(ObjId(param));
     fi;
     return index;
end;

#F HashIndexSPLRec( <spl> )
#F   Does the actual dirty work for HashIndexSPL recursively
#F   Do not call directly
#F

HashIndexSPLRec := function( spl )
   local type, index, param, params;

   if not IsSPL(spl) then
      Error( "<spl> must be a valid SPL" );
   fi;

   if IsNonTerminal(spl) and IsBound(spl.params) then
      index := BagAddr(ObjId(spl));
      params := When(IsBound(spl.HashId), spl.HashId(), spl.params);
      if IsInt( params ) then
	  index := index * (params+1);
      else
	  params := When(not IsList(params), [params], params);
	  for param in params do
	      index := HashIndexParams(param, index);
	  od;
      fi;
   elif IsBound(spl.children) then
       index := Product( spl.children(), a->HashIndexSPLRec(a) );
   elif IsBound(spl.element) and IsList(spl.element) then
       index := Length(spl.element);
   else
       index := BagAddr(ObjId(spl));
   fi;

   return index;
end;

#F HashIndexSPL( <spl>, <hashSize> )
#F    one possible method of returning a hash index (probably not that good).
#F    takes a SPL and returns a hash index.
#F
HashIndexSPL := function( spl, hashSize )
   if not IsSPL(spl) then
      Error( "<spl> must be a valid SPL" );
   fi;

   if IsBound(spl.hashIndex) then
      return ( (spl.hashIndex mod hashSize) + 1 );
   fi;

   spl.hashIndex := HashIndexSPLRec(spl);
   return ( (spl.hashIndex mod hashSize) + 1 );
end;

#F HashIndexRuleTree( <ruletree>, <hashSize> )
#F    one possible method of returning a hash index (probably not that good).
#F    takes a RuleTree and returns a hash index.
#F

HashIndexRuleTree := function( tree, hashSize )
   local index,
         child;

   index := BagAddr(tree.rule);
   if tree.transposed then
      index := index*3;
   else
      index := index*7;
   fi;
   if IsBound(tree.splOptions) and tree.splOptions="unrolled" then
      index := index*11;
   else
      index := index*13;
   fi;
   index := ((index+1) * (HashIndexSPL(tree.node, hashSize)+1))
            mod hashSize;
   for child in tree.children do
      index := ((index+1) * (HashIndexRuleTree(child, hashSize)+1))
               mod hashSize;
   od;
   return index+1;
end;

#F HashTableSPL() - create hashtable with SPLs as keys
HashTableSPL := function()
   return HashTable( HashIndexSPL, IsHashIdenticalSPL, "HashTableSPL()" );
end;


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

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

#......Ross Jordan Block Sizes........Donna & Ross nilpotents........(in GAP notation)
reps:=[[[2,1],[4,1],[3,1],[1,1]],    # F_4
       [[2,1],[4,1],[7,1],[5,1]],    # F_4(a_1) [[2,1],[4,1],[10,1],[1,1]],
       #[[6,1],[10,1],[1,1],[5,1]],   # F_4(a_2) [[9,1],[1,1],[10,1],[4,1]],
       [[1,1],[5,1],[6,1],[10,1]],   # F_4(a_2) [[9,1],[1,1],[10,1],[4,1]],    I want it ordered
       [[4,1],[3,1],[1,1]],          # C_3      [[1,1],[3,1],[4,1]],
       [[2,1],[4,1],[3,1]],          # B_3      the same
       [[4,1],[6,1],[10,1],[18,1]],  # F_4(a_3) [[4,1],[13,1],[11,1],[12,1]],
       [[4,1],[1,1],[10,1],[20,1]],  # C_3(a_1)^{(2)}
       [[4,1],[1,1],[10,1]],         # C_3(a_1) [[1,1],[10,1],[4,1]],
       [[2,1],[3,1],[1,1],[15,1]],   # sA_2A_1^{(2)}
       [[2,1],[3,1],[1,1]],          # sA_2A_1  [[3,1],[1,1],[2,1]],
       [[4,1],[3,1],[13,1]],         # B_2^{(2)}
       [[4,1],[3,1]],                # B_2      the same
       [[2,1],[4,1],[1,1]],          # A_2sA_1  the same
       [[3,1],[1,1]],                # sA_2     the same
       [[2,1],[4,1]],                # A_2      the same
#       [[2,1],[3,1]],                # A_1sA_1  [[2,1],[1,1]],
#       [[3,1],[10,1]],               # sA_1^{(2)}
#       [[3,1]],                      # sA_1     [[1,1]],
#       [[2,1]]                       # A_1      [[2,1]]
       [[2,1],[1,1]],                # A_1sA_1  [[2,1],[1,1]],
#       [[1,1],[15,1]],               # sA_1^{(2)}  above*reflection[1]*reflection[3]
       [[21,1],[24,1]],              # sA_1^{(2)} Liebeck & Seitz ---> is already in the right position
       [[1,1]],                      # sA_1     [[1,1]],
       [[2,1]]                       # A_1      [[2,1]]
       ];

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

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

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

tori:=[
       [2,2,2,2],   # F_4
       [2,2,0,2],   # F_4(a_1)
       [2,0,0,2],   # F_4(a_2)
       [2,-9,2,2],  # C_3
       [-6,2,2,2],  # B_3
       [0,0,0,2],   # F_4(a_3)
       [2,-5,0,2],  # C_3(a_1)^{(2)}
       [2,-5,0,2],  # C_3(a_1)
       [2,2,2,-5],  # (sA_2A_1)^{(2)}
       [2,2,2,-5],  # sA_2A_1
       [-3,-4,2,2], # (B_2)^{(2)}
       [-3,-4,2,2], # B_2
       [2,2,-3,2],  # A_2sA_1
       [2,0,2,-4],  # sA_2
       [0,2,-2,2],  # A_2
       [2,2,-1,-1], # A_1sA_1
       #[2,0,-1,0],  # (sA_1)^{(2)}
       [2,2,2,2],   # (sA_1)^{(2)} ---> is already in the right position
       [2,0,-1,0],  # sA_1
       [0,2,0,-1]   # A_1
    ];

#blocks:=[];
blocks:=[[[16, 2], [13, 1], [7, 1]],             # F_4
         [[8, 6], [4, 1]],                       # F_4(a_1)
         [[8, 4], [7, 1], [6, 1], [4, 1],[3, 1]],# F_4(a_2)
         [[8, 4], [4, 3], [2, 3], [1, 2]],       # C_3
         [[8, 1], [6, 6], [2, 3], [1, 2]],       # B_3
         [[4, 12], [2, 2]],                      # F_4(a_3)
         [[4, 10], [3, 1], [2, 4], [1, 1]],      # C_3(a_1)^{(2)}
         [[4, 10], [3, 1], [2, 3], [1, 3]],      # C_3(a_1)
         [[4, 9], [3, 3], [2, 3], [1, 1]],       # sA_2A_1^{(2)}
         [[4, 8], [2, 10]],                      # sA_2A_1
         [[4, 10], [3, 1], [2, 3], [1, 3]],      # B_2^{(2)}
         [[4, 10], [3, 1], [1, 9]],              # B_2
         [[4, 6], [3, 4], [2, 8]],               # A_2sA_1
         [[4, 8], [2, 6], [1, 8]],               # sA_2
         [[4, 2], [3, 12], [1, 8]],              # A_2
         [[2, 24], [1, 4]],                      # A_1sA_1
         [[2, 21], [1, 10]],                     # sA_1^{(2)}
         [[2, 16], [1, 20]],                     # sA_1
         [[2, 16], [1, 20]]                      # A_1
         ];

#ords:=[];
ords:=[ 16, 8, 8, 8, 8, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 2, 2, 2 ];

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


text:
Concatenation(ListWithIdenticalEntries(10, "BOB "));
"BOB BOB BOB BOB BOB BOB BOB BOB BOB BOB "


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

Class(FFTXIAOpts, FFTXOpts, rec(
    tags := [],
    operations := rec(Print := s -> Print("<FFTX IA options record>")),
    max_threads := 1024
));

iaOpts := function(arg)
    local opts, optrec, sseopts, smpopts;

    optrec := rec(dataType := T_Real(64), globalUnrolling := 32);
    smpopts := rec(numproc := 4, api := "OpenMP");
    sseopts := rec(svct := true, splitL := false, oddSizes := false, stdTTensor := true, tsplPFA := false);

    if Length(arg) >= 1 then optrec := CopyFields(optrec, arg[1]); fi;
    if Length(arg) >= 2 then smpopts := CopyFields(smpopts, arg[2]); fi;
    if Length(arg) >= 3 then sseopts := CopyFields(sseopts, arg[3]); fi;

    opts := CopyFields(FFTXOpts, IAGlobals.getOpts(optrec, smpopts, sseopts));
    opts.breakdownRules.TFCall := FFTXOpts.breakdownRules.TFCall;

    # FFTX specific breakdown rules
    opts.breakdownRules.Circulant := [Circulant_PRDFT_FDataNT];
    opts.breakdownRules.PRDFT := List([PRDFT1_Base1, PRDFT1_Base2, PRDFT1_CT, PRDFT1_PF, PRDFT_PD, PRDFT_Rader], _noT);
    opts.breakdownRules.IPRDFT := List([ IPRDFT1_Base1, IPRDFT1_Base2, IPRDFT1_CT, IPRDFT_PD, IPRDFT_Rader ], _noT);
    opts.breakdownRules.PRDFT3 := List([ PRDFT3_Base1, PRDFT3_Base2, PRDFT3_CT ], _noT);

    return opts;
end;

Declare(ParseOptsIA);

Class(FFTXIADefaultConf, rec(
    __call__ := self >> self,
    getOpts := (self, t) >> ParseOptsIA(self, t),
    operations := rec(Print := s -> Print("<FFTX IA Default Configuration>")),
    useOMP := true,
    useSIMD := true
));

iaConf := rec(
    defaultName := "defaultIAConf",
    defaultOpts := (arg) >> FFTXIADefaultConf,
    useOMP := true,
    useSIMD := true,
    confHandler := iaOpts
);

fftx.FFTXGlobals.registerConf(iaConf);

getTargetOS := function()
    local tgt;

    if LocalConfig.osinfo.isWindows() then
        tgt := "win-x64-cuda";
    elif LocalConfig.osinfo.isLinux() then
        tgt := "linux-cuda";
    elif LocalConfig.osinfo.isDarwin() then
        tgt := "linux-cuda";    ## may work
    fi;
    return tgt;
end;

#--

# this is a first experimental opts-deriving logic. This needs to be done extensible and properly
ParseOptsIA := function(conf, t)
    local tt, _tt, _conf, _opts;

    _opts := iaOpts();
    return _opts;

#    if IsBound(conf.useCUDADevice) then
#        # detect real MD convolution
#        _tt := Collect(t, RCDiag)::Collect(t, MDPRDFT)::Collect(t, IMDPRDFT)::Collect(t, TTensorI);
#        if Length(_tt) = 4 then
#            _conf := FFTXGlobals.confWarpXCUDADevice();
#            _opts := FFTXGlobals.getOpts(_conf);
#            return _opts;
#        fi;
#
#        # detect batch of DFT/PRDFT/MDDFT/MDPRDFT
#        if ((Length(Collect(t, TTensorInd)) >= 1) or (Length(Collect(t, TTensorI)) >= 1)) and
#            ((Length(Collect(t, DFT)) = 1) or (Length(Collect(t, PRDFT)) = 1) or (Length(Collect(t, IPRDFT)) = 1) or
#              (Length(Collect(t, MDDFT)) >= 1) or (Length(Collect(t, MDPRDFT)) >= 1) or (Length(Collect(t, IMDPRDFT)) >= 1)) then
#            _conf := FFTXGlobals.confBatchFFTCUDADevice();
#            _opts := FFTXGlobals.getOpts(_conf);
#            return _opts;
#        fi;
#
#        # detect 3D DFT
#        _tt := Collect(t, MDDFT)::Collect(t, MDPRDFT)::Collect(t, IMDPRDFT);
#        if Length(_tt) = 1 and Length(_tt[1].params[1]) = 3 then
#            _conf := FFTXGlobals.confFFTCUDADevice();
#            _opts := FFTXGlobals.getOpts(_conf);
#            return _opts;
#        fi;
#
#        # promote with default conf rules
#        tt := _promote1(Copy(t));
#
#        if ObjId(tt) = TFCall then
#            _tt := tt.params[1];
#            # check for convolution
#            if (ObjId(_tt) = MDRConv) or ((ObjId(_tt) = TTensorI) and (ObjId(_tt.params[1]) = MDRConv)) then
#                _conf := FFTXGlobals.confMDRConvCUDADevice();
#                _opts := FFTXGlobals.getOpts(_conf);
#                return _opts;
#            fi;
#            # check for Hockney. This is for N=130
#            if ObjId(_tt) = IOPrunedMDRConv  and _tt.params[1] = [130,130,130] then
#                _conf := FFTXGlobals.confHockneyMlcCUDADevice();
#                _opts := FFTXGlobals.getOpts(_conf);
#                return _opts;
#            fi;
#        fi;
#
#        # check for WarpX
#        _conf := FFTXGlobals.confWarpXCUDADevice();
#        _opts := FFTXGlobals.getOpts(_conf);
#        tt := _opts.preProcess(Copy(t));
#        if ObjId(tt) = TFCall and ObjId(tt.params[1]) = TCompose then
#            _tt := tt.params[1].params[1];
#            # detect promoted WarpX
#            if IsList(_tt) and Length(_tt) = 3 and List(_tt, ObjId) = [ TNoDiagPullinRight, TRC, TNoDiagPullinLeft ] then
#                return _opts;
#            fi;
#        fi;
#        # we are doing nothing special
#        return FFTXGlobals.getOpts(conf);
#    fi;
#    if IsBound(conf.useCUDA) then
#        return FFTXGlobals.getOpts(conf);
#    fi;
#
#    # Here we have to handle GPU configs
    Error("Don't know how to derive opts!\n");
end;

max_stars_repo_name: franzfranchetti/spiral-package-fftx

max_issues_repo_name: franzfranchetti/spiral-package-fftx

max_forks_repo_name: franzfranchetti/spiral-package-fftx


text:
#############################################################################
##
#W  id1600.gi              GAP library of id's             Hans Ulrich Besche
##

ID_GROUP_TREE.next[1600].next[191]:=
rec(
  fp:= [ 2336, 3359, 3566, 4409, 5734, 6716, 7350, 7386, 7764, 12688, 13496,
18471, 18945, 19034, 19184, 20975, 24764, 25529, 25988, 26794, 29528, 29556,
29778, 30192, 31174, 31808, 35666, 37146, 40641, 41354, 41854, 43403, 43492,
45638, 47699, 49222, 50446, 51252, 51491, 51726, 54236, 54650, 55154, 55632,
56266, 56479, 57836, 58509, 61604, 63524, 66312, 67808, 69869, 73661, 73680,
74904, 78694, 79612, 80006, 80937, 82967, 84877, 85694, 89978, 90770, 91762,
92039, 94237, 95962, 96178 ],
  level:= 4,
  next:= [ 6692, rec(
    fp:= [ 88, 51898, 80161, 87396 ],
    next:= [ rec(
      desc:= [ 112011 ],
      fp:= [ 16, 26, 818 ],
      next:= [ rec(
        desc:= [ 302004 ],
        fp:= [ 902, 1801 ],
        next:= [ 6564, rec(
          desc:= [ 209002 ],
          fp:= [ 8, 18 ],
          next:= [ 6665, 6594 ] ) ] ), 6592, rec(
        desc:= [ 108003 ],
        fp:= [ 8, 18, 414 ],
        next:= [ 6558, 6578, 6583 ] ) ] ), rec(
      desc:= [ 113011 ],
      fp:= [ 422, 818, 1214 ],
      next:= [ 6560, 6588, rec(
        desc:= [ 109003 ],
        fp:= [ 4, 14 ],
        next:= [ 6662, 6590 ] ) ] ), rec(
      desc:= [ 110003 ],
      fp:= [ 4, 14, 212 ],
      next:= [ rec(
        desc:= [ 104003, 302003, 302005, 208002 ],
        fp:= [ 2, 12 ],
        next:= [ 6566, 6661 ] ), 6579, 6582 ] ), rec(
      desc:= [ 113011 ],
      fp:= [ 422, 1214 ],
      next:= [ 6584, rec(
        desc:= [ 109003 ],
        fp:= [ 4, 14 ],
        next:= [ 6562, 6591 ] ) ] ) ] ), rec(
    desc:= [ 110003 ],
    fp:= [ 4, 14, 212 ],
    next:= [ 6777, 6776, 6775 ] ), 6679, rec(
    desc:= [ 111003 ],
    fp:= [ 16, 214, 412 ],
    next:= [ 6506, 6511, 6505 ] ), rec(
    fp:= [ 67467, 86989 ],
    next:= [ 5699, 5703 ] ), 6678, rec(
    fp:= [ 28383, 59114 ],
    next:= [ 5677, 5674 ] ), rec(
    fp:= [ 68372, 89029 ],
    next:= [ 6501, 6500 ] ), 6698, rec(
    fp:= [ 22326, 34820, 39910, 49358 ],
    next:= [ 6742, rec(
      desc:= [ 104003 ],
      fp:= [ 4, 14 ],
      next:= [ rec(
        desc:= [ 110003 ],
        fp:= [ 4, 14 ],
        next:= [ 6712, 6717 ] ), 6740 ] ), 6720, 6741 ] ), rec(
    fp:= [ 11506, 97869 ],
    next:= [ 5678, 5681 ] ), rec(
    fp:= [ 30872, 33731 ],
    next:= [ rec(
      desc:= [ 112003 ],
      fp:= [ 2, 12 ],
      next:= [ 5696, 5697 ] ), rec(
      desc:= [ 112003 ],
      fp:= [ 2, 12 ],
      next:= [ 5695, 5694 ] ) ] ), rec(
    fp:= [ 16813, 89614 ],
    next:= [ 6517, 6520 ] ), rec(
    fp:= [ 17432, 76439, 81210, 89426 ],
    next:= [ 6581, 6663, 6664, rec(
      desc:= [ 302014 ],
      fp:= [ 63740, 75240 ],
      next:= [ 6587, rec(
        desc:= [ 107003 ],
        fp:= [ 4, 14 ],
        next:= [ 6565, 6660 ] ) ] ) ] ), rec(
    desc:= [ 110003 ],
    fp:= [ 4, 14, 212 ],
    next:= [ 5684, 5686, 5688 ] ), 6688, rec(
    fp:= [ 5113, 11824, 19609, 48597, 52539, 80636, 83510 ],
    next:= [ rec(
      desc:= [ 111003 ],
      fp:= [ 4, 14, 212 ],
      next:= [ 6546, rec(
        desc:= [ 302004, 302007, 209002 ],
        fp:= [ 2, 12 ],
        next:= [ 6643, 6537 ] ), 6541 ] ), 6542, rec(
      desc:= [ 302005 ],
      fp:= [ 1401, 50901 ],
      next:= [ 6656, rec(
        desc:= [ 117011 ],
        fp:= [ 12, 418 ],
        next:= [ 6551, 6559 ] ) ] ), 6648, rec(
      desc:= [ 110003 ],
      fp:= [ 4, 14, 212 ],
      next:= [ 6552, rec(
        desc:= [ 115011 ],
        fp:= [ 12, 418 ],
        next:= [ 6646, 6585 ] ), 6547 ] ), 6649, 6667 ] ), 6696, 6689, rec(
    desc:= [ 111003 ],
    fp:= [ 4, 14, 212 ],
    next:= [ 6773, 6774, 6772 ] ), rec(
    fp:= [ 84836, 93852 ],
    next:= [ 5676, 5673 ] ), 6674, 6507, rec(
    fp:= [ 13332, 75068 ],
    next:= [ 5700, 5701 ] ), 6675, rec(
    fp:= [ 1266, 7632, 43357, 53685, 70165 ],
    next:= [ 6707, 6739, rec(
      desc:= [ 105003 ],
      fp:= [ 4, 14 ],
      next:= [ rec(
        desc:= [ 111003 ],
        fp:= [ 4, 14 ],
        next:= [ 6709, 6705 ] ), 6737 ] ), 6736, 6738 ] ), rec(
    fp:= [ 4584, 59123 ],
    next:= [ 6699, 6697 ] ), rec(
    fp:= [ 13651, 83336 ],
    next:= [ 5682, 5679 ] ), rec(
    fp:= [ 22006, 43211, 47769, 88448, 91910 ],
    next:= [ 6652, 6651, 6540, rec(
      desc:= [ 116019 ],
      fp:= [ 22, 814 ],
      next:= [ 6642, rec(
        desc:= [ 111003 ],
        fp:= [ 4, 14 ],
        next:= [ 6545, 6549 ] ) ] ), 6640 ] ), 6685, rec(
    fp:= [ 64224, 84969 ],
    next:= [ rec(
      desc:= [ 112003 ],
      fp:= [ 2, 12 ],
      next:= [ 5693, 5692 ] ), rec(
      desc:= [ 112003 ],
      fp:= [ 2, 12 ],
      next:= [ 5690, 5691 ] ) ] ), rec(
    fp:= [ 17115, 54122 ],
    next:= [ 6516, 6515 ] ), 5745, rec(
    fp:= [ 8742, 17698, 37621, 37725, 63931, 68715, 85748 ],
    next:= [ 6644, 6645, 6647, 6666, 6543, 6650, rec(
      desc:= [ 109003 ],
      fp:= [ 8, 18, 414 ],
      next:= [ rec(
        desc:= [ 114011 ],
        fp:= [ 8, 18 ],
        next:= [ 6538, 6550 ] ), 6653, 6655 ] ) ] ), rec(
    fp:= [ 1021, 63186 ],
    next:= [ 6686, 6694 ] ), 6695, 6691, 6744, 5672, rec(
    fp:= [ 15375, 94683 ],
    next:= [ 6673, 6680 ] ), 6508, 6682, rec(
    fp:= [ 22266, 63767 ],
    next:= [ 5698, 5702 ] ), 6677, 6509, rec(
    fp:= [ 32, 31059, 34820, 55974 ],
    next:= [ 6733, 6734, 6735, 6732 ] ), rec(
    fp:= [ 34908, 75785 ],
    next:= [ 6503, 6504 ] ), 6690, rec(
    fp:= [ 10903, 58462, 58539, 89359 ],
    next:= [ 6633, 6629, 6641, 6634 ] ), rec(
    fp:= [ 1754, 3480 ],
    next:= [ 6683, 6684 ] ), 5743, rec(
    fp:= [ 2041, 8592, 13451 ],
    next:= [ 6632, 6654, 6631 ] ), 6743, 6687, 6693, 6672, 6681, 6731,
6510, rec(
    fp:= [ 1797, 27346 ],
    next:= [ 6499, 6502 ] ), 5675, 6630, 5744, 6676, rec(
    fp:= [ 70200, 89024 ],
    next:= [ 6752, rec(
      desc:= [ 109003 ],
      fp:= [ 6, 16, 214, 412 ],
      next:= [ 6751, 6750, 6749, 6748 ] ) ] ), 6635, rec(
    fp:= [ 43650, 55647 ],
    next:= [ 6518, 6519 ] ), rec(
    fp:= [ 86473, 98981 ],
    next:= [ 5680, 5683 ] ), rec(
    desc:= [ 110003 ],
    fp:= [ 4, 14, 212 ],
    next:= [ 5687, 5685, 5689 ] ) ] );

max_stars_repo_name: hulpke/smallgrp

max_issues_repo_name: hulpke/smallgrp

max_forks_repo_name: gap-packages/smallgrp


text:
--/**
-- * <copyright>
-- *
-- * Copyright (c) 2010, 2009 IBM Corporation and others.
-- * All rights reserved.   This program and the accompanying materials
-- * are made available under the terms of the Eclipse Public License v2.0
-- * which accompanies this distribution, and is available at
-- * http://www.eclipse.org/legal/epl-v20.html
-- *
-- * Contributors:
-- *   See (or edit) Notice Declaration below
-- *
-- * </copyright>
-- */
--
-- The Essential OCL KeyWord Lexer
--

%options slr
%options fp=EssentialOCLKWLexer,prefix=Char_
%options noserialize
%options package=org.eclipse.ocl.xtext.essentialocl.parser
%options template=../lpg/KeywordTemplateF.gi
%options export_terminals=("EssentialOCLParsersym.java", "TK_")
%options include_directory="../lpg"

%Import
	KWLexerMapF.gi
%End

%Define

	--
	-- Definition of macros used in the template
	--
	$action_class /.$file_prefix./
	$eof_char /.Char_EOF./
	$copyright_contributions /.*./

%End

%Notice
	/./**
 * Essential OCL Keyword Lexer
 * <copyright>
 *
 * Copyright (c) 2010, 2009 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 *
 * Contributors:
 *   IBM - Initial API and implementation
 *   E.D.Willink - Lexer and Parser refactoring to support extensibility and flexible error handling
 *   E.D.Willink - Bug 285633, 292112
 *   Adolfo Sanchez-Barbudo Herrera (Open Canarias) - LPG v 2.0.17 adoption (242153)
 *   Adolfo Sanchez-Barbudo Herrera (Open Canarias) - Introducing new LPG templates (299396)
$copyright_contributions
 * </copyright>
 *
 *
 */
	./
%End

%Globals
	/../
%End

%Export
	self
	if
	then
	else
	endif
	and
	or
	xor
	not
	implies
	let
	in
	true
	false

	null
	invalid

	--
	-- the remainder of the LPG keywords are defined as such for the
	-- purpose of constructing the CST grammar.  They are not OCL
	-- reserved words
	--
	Set
	Bag
	Sequence
	Collection
	OrderedSet

	String
	Integer
	UnlimitedNatural
	Real
	Boolean
	Tuple
	OclAny
	OclVoid
	OclInvalid
%End

%Start
	KeyWord
%End

%Rules

-- The Goal for the parser is a single Keyword

	KeyWord ::=
		s e l f
		/.$BeginAction
			$setResult($_self);
		  $EndAction
		./

		| i f
		/.$BeginAction
			$setResult($_if);
		  $EndAction
		./

		| t h e n
		/.$BeginAction
			$setResult($_then);
		  $EndAction
		./

		| e l s e
		/.$BeginAction
			$setResult($_else);
		  $EndAction
		./

		| e n d i f
		/.$BeginAction
			$setResult($_endif);
		  $EndAction
		./

		| a n d
		/.$BeginAction
			$setResult($_and);
		  $EndAction
		./

		| o r
		/.$BeginAction
			$setResult($_or);
		  $EndAction
		./

		| x o r
		/.$BeginAction
			$setResult($_xor);
		  $EndAction
		./

		| n o t
		/.$BeginAction
			$setResult($_not);
		  $EndAction
		./

		| i m p l i e s
		/.$BeginAction
			$setResult($_implies);
		  $EndAction
		./

		| l e t
		/.$BeginAction
			$setResult($_let);
		  $EndAction
		./

		| i n
		/.$BeginAction
			$setResult($_in);
		  $EndAction
		./

		| t r u e
		/.$BeginAction
			$setResult($_true);
		  $EndAction
		./

		| f a l s e
		/.$BeginAction
			$setResult($_false);
		  $EndAction
		./

		| S e t
		/.$BeginAction
			$setResult($_Set);
		  $EndAction
		./

		| B a g
		/.$BeginAction
			$setResult($_Bag);
		  $EndAction
		./

		| S e q u e n c e
		/.$BeginAction
			$setResult($_Sequence);
		  $EndAction
		./

		| C o l l e c t i o n
		/.$BeginAction
			$setResult($_Collection);
		  $EndAction
		./

		| O r d e r e d S e t
		/.$BeginAction
			$setResult($_OrderedSet);
		  $EndAction
		./

		| S t r i n g
		/.$BeginAction
			$setResult($_String);
		  $EndAction
		./

		| I n t e g e r
		/.$BeginAction
			$setResult($_Integer);
		  $EndAction
		./

		| U n l i m i t e d N a t u r a l
		/.$BeginAction
			$setResult($_UnlimitedNatural);
		  $EndAction
		./

		| R e a l
		/.$BeginAction
			$setResult($_Real);
		  $EndAction
		./

		| B o o l e a n
		/.$BeginAction
			$setResult($_Boolean);
		  $EndAction
		./

		| T u p l e
		/.$BeginAction
			$setResult($_Tuple);
		  $EndAction
		./

		| O c l A n y
		/.$BeginAction
			$setResult($_OclAny);
		  $EndAction
		./

		| O c l V o i d
		/.$BeginAction
			$setResult($_OclVoid);
		  $EndAction
		./

		| O c l I n v a l i d
		/.$BeginAction
			$setResult($_OclInvalid);
		  $EndAction
		./

		| n u l l
		/.$BeginAction
			$setResult($_null);
		  $EndAction
		./

		| i n v a l i d
		/.$BeginAction
			$setResult($_invalid);
		  $EndAction
		./
%End


text:
LookAndSay := function(s)
  local c, r, cur, ncur, v;
  v := "123";
  r := "";
  cur := 0;
  ncur := 0;
  for c in s do
    if c = cur then
      ncur := ncur + 1;
    else
      if ncur > 0 then
        Add(r, v[ncur]);
        Add(r, cur);
      fi;
      cur := c;
      ncur := 1;
    fi;
  od;
  Add(r, v[ncur]);
  Add(r, cur);
  return r;
end;

LookAndSay("1");     # "11"
LookAndSay(last);    # "21"
LookAndSay(last);    # "1211"
LookAndSay(last);    # "111221"
LookAndSay(last);    # "312211"
LookAndSay(last);    # "13112221"
LookAndSay(last);    # "1113213211"
LookAndSay(last);    # "31131211131221"
LookAndSay(last);    # "13211311123113112211"
LookAndSay(last);    # "11131221133112132113212221"
LookAndSay(last);    # "3113112221232112111312211312113211"
LookAndSay(last);    # "1321132132111213122112311311222113111221131221"
LookAndSay(last);    # "11131221131211131231121113112221121321132132211331222113112211"
LookAndSay(last);    # "311311222113111231131112132112311321322112111312211312111322212311322113212221"


text:
2/3 in Rationals;
# true
2/3 + 3/4;
# 17/12