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


InstallGlobalFunction(MEMO_MongoDBCache,
function(memo, path)
  local url, cache, type;

  # Full URL including function name
  url := path;
  if not EndsWith(url, "/") then
    Add(url, '/');
  fi;
  Append(url, memo!.funcname);

  # Make cache object
  cache := rec(memo := memo,  # memoised function
               url := url);  # URL of the database
  # Objectify
  type := NewType(DictionariesFamily, MEMO_IsMongoDBCache);
  cache := Objectify(type, cache);

  return cache;
end);

InstallMethod(AddDictionary,
"for a memoisation disk cache and two objects",
[MEMO_IsMongoDBCache, IsObject, IsObject],
function(cache, key, val)
  local memo, h, storedkey, query, namespace, result, args, post_string, url,
        db_response, res;
  memo := cache!.memo;

  # Get hash
  h := memo!.hash(key);

  # Construct MongoDB query as record
  query := rec(hash := h,
               namespace := MEMO_MongoDBNamespace,
               result := memo!.pickle(val));

  # OPTION: storekey
  if memo!.storekey then
    query.key := memo!.pickle(key);
  fi;

  # OPTION: metadata
  if memo!.metadata <> fail then
    query.metadata := memo!.metadata();
  fi;

  # Query the server
  args := List(RecNames(query), rnam -> Concatenation(rnam, "=", query.(rnam)));
  post_string := JoinStringsWithSeparator(args, "&");
  url := cache!.url;
  Info(InfoMemoisation, 3, "Posting to ", url);
  Info(InfoMemoisation, 4, "(including ",
       JoinStringsWithSeparator(RecNames(query), ", "), ")");
  db_response := PostToURL(cache!.url, post_string);
  if db_response.success = false then
    # No valid response from server
    Error("AddDictionary (MongoDB cache): ", db_response.error);
  fi;
  res := JsonStringToGap(db_response.result);

  if res._status = "ERR" then
    # Problem with database
    Error("AddDictionary (MongoDB cache): ", res);
  elif res._status = "OK" then
    # Success
    return;
  fi;
  Error("AddDictionary (MongoDB cache): ",
        "<res>._status should be \"ERR\" or \"OK\"");

  # no return value
end);

InstallMethod(KnowsDictionary,
"for a memoisation disk cache and an object",
[MEMO_IsMongoDBCache, IsObject],
function(cache, key)
  local item;
  item := MEMO_MongoDBQuery(cache, key);
  return item <> fail;
end);

InstallMethod(LookupDictionary,
"for a memoisation disk cache and an object",
[MEMO_IsMongoDBCache, IsObject],
function(cache, key)
  local memo, item, storedkey;
  memo := cache!.memo;

  # Request item from database
  item := MEMO_MongoDBQuery(cache, key);
  if item = fail then
    # We shouldn't normally get here, as we usually check KnowsDictionary first
    Info(InfoMemoisation, 1, "No entry found in database");
    return fail;
  fi;

  # OPTION: storekey
  if memo!.storekey then
    storedkey := memo!.unpickle(item.key);
    # check if key still matches
    if key <> storedkey then
      ErrorNoReturn("Hash collision: <key> does not match <storedkey>");
    fi;
    Info(InfoMemoisation, 3, "Key matches that stored on the server");
  fi;

  # OPTION: unhash
  if memo!.unhash <> fail then
    # unhash and check if key still matches
    storedkey := memo!.unhash(item.hash);
    if storedkey <> key then
      ErrorNoReturn("Hash collision: <key> does not match <storedkey>");
    fi;
  fi;

  return memo!.unpickle(item.result);
end);

InstallMethod(MEMO_ClearCache,
"for a memoisation disk cache",
[MEMO_IsMongoDBCache],
function(cache)
  local db_response;
  db_response := DeleteURL(cache!.url);
  if db_response.success <> true then
    Error("MongoDB cache: failed to clear");
  fi;
  return db_response.success;
end);

InstallGlobalFunction(MEMO_MongoDBQuery,
function(cache, key)
  local memo, query, url, db_response, item;
  memo := cache!.memo;

  # Construct the arguments
  query := rec(namespace := MEMO_MongoDBNamespace);
  query := List(RecNames(query), rnam -> Concatenation("%22",
                                                      rnam,
                                                      "%22=%22",
                                                      query.(rnam),
                                                      "%22"));
  query := JoinStringsWithSeparator(query, ",");
  url := Concatenation(cache!.url, "/", memo!.hash(key),
                       "?where={", query, "}");
  Info(InfoMemoisation, 4, "Querying ", url);
  db_response := DownloadURL(url);
  if db_response.success = false then
    # No valid response from server
    Error("MongoDB cache: ", db_response.error);
  fi;
  item := JsonStringToGap(db_response.result);
  if IsBound(item._status) and item._status = "ERR" then
    if item._error.code = 404 then
      # No result for this hash on server (no problem!)
      return fail;
    else
      # Something else went wrong
      Error("MongoDB cache: ", item._error.code, " ", item._error.message);
    fi;
  fi;

  # Return a single item as a record
  return item;
end);


--
-- The $LANG_NAME$ Keyword Lexer
--
%options package=$PACKAGE_NAME$
%options template=$TEMPLATE$F.gi

%Include
    KWLexerLowerCaseMapF.gi
%End

%Export
    -- List all the keywords the kwlexer will export to the lexer and parser
    boolean
    double
    else
    false
    if
    int
    return
    true
    void
    while
%End

%Terminals
    a    b    c    d    e    f    g    h    i    j    k    l    m
    n    o    p    q    r    s    t    u    v    w    x    y    z
%End

%Start
    -- The Goal for the parser is a single Keyword
    Keyword
%End

%Rules
    Keyword ::= b o o l e a n  /.$setResult($_boolean);./
              | d o u b l e    /.$setResult($_double);./
              | e l s e        /.$setResult($_else);./
              | f a l s e      /.$setResult($_false);./
              | i f            /.$setResult($_if);./
              | i n t          /.$setResult($_int);./
              | v o i d        /.$setResult($_void);./
              | r e t u r n    /.$setResult($_return);./
              | t r u e        /.$setResult($_true);./
              | w h i l e      /.$setResult($_while);./
%End


f:=function (  )
    Print( "f:=", f, ";;\nf();\n" );
    return;
end;;
f();


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

Class(cu_allocate_managed, assign);

cudaMemcpyHostToHost     := "cudaMemcpyHostToHost";
cudaMemcpyHostToDevice   := "cudaMemcpyHostToDevice";
cudaMemcpyDeviceToHost   := "cudaMemcpyDeviceToHost";
cudaMemcpyDeviceToDevice := "cudaMemcpyDeviceToDevice";

Class(cu_check_errors, call);

Class(T_Class, T_Type, rec(

    fields := rec(),

    updateParams := meth(self)
        Constraint(IsString(self.params[1]));
        Constraint(When(Length(self.params)>1, IsRec(self.params[2]), true));
    end,
));

Class(Dim3, T_Class, rec(

    __call__ := (self) >> let(fields := rec(x := TUInt, y := TUInt, z := TUInt), Inherited("Dim3", fields)),

    )
);

var.("fresh_t_obj") := meth(self,id,t,params)
    local v, f;
    if IsType(t) and IsRec(params) and ForAll(RecFields(params), f->f in RecFields(t.params[2])) then
        v := var.fresh_t(id, t);
        for f in Filtered(RecFields(params), e -> IsType(t.params[2].(e))) do
            v.(f) := var.fresh_t(f, t.params[2].(f));
            v.(f).setValue(params.(f));
        od;
        return v;
    else
        Error("<t> must be a type or an integer that represents an interval");
    fi;
end;

Class(T_CudaEvent, T_Type);
Class(cu_event_create, call);
Class(cu_event_record, call);
Class(cu_event_destroy, call);
Class(cu_event_synchronize, call);
Class(cu_device_synchronize, call);
Class(cu_event_elapsed_time, call);

Class(cu_allocate, assign, rec(
   isAssign := true,
   __call__ := (self, loc, type, size) >> WithBases(self,
       rec(operations := CmdOps,
       loc  := toAssignTarget(loc),
       type  := type,
       size := size)
       ),

   rChildren := self >> [self.loc, self.type, self.size],
   rSetChild := rSetChildFields("loc", "type", "size"),

   print := (self,i,si) >> let(name := Cond(IsBound(self.isCompute) and self.isCompute,
                                            gap.colors.DarkYellow(self.__name__),
                                            IsBound(self.isLoad) and self.isLoad,
                                            gap.colors.DarkRed(self.__name__),
                                            IsBound(self.isStore) and self.isStore,
                                            gap.colors.DarkGreen(self.__name__),
                                            self.__name__),
                                Print(name, "(", self.loc, ", ", self.type, ", ", self.size, ")"))
));

Class(cu_memcpy, assign, rec(
   isAssign := true,
   __call__ := (self, loc, exp, size, kind) >> WithBases(self,
       rec(operations := CmdOps,
       loc  := toAssignTarget(loc),
       exp  := toExpArg(exp),
       size := size,
       kind := kind)),

   rChildren := self >> [self.loc, self.exp, self.size, self.kind],
   rSetChild := rSetChildFields("loc", "exp", "size", "kind"),

   print := (self,i,si) >> let(name := Cond(IsBound(self.isCompute) and self.isCompute,
                                            gap.colors.DarkYellow(self.__name__),
                                            IsBound(self.isLoad) and self.isLoad,
                                            gap.colors.DarkRed(self.__name__),
                                            IsBound(self.isStore) and self.isStore,
                                            gap.colors.DarkGreen(self.__name__),
                                            self.__name__),
                                Print(name, "(", self.loc, ", ", self.exp, ", ", self.size, ", ", self.kind, ")"))
));

Class(cu_memcpy_to_sym, assign, rec(
   isAssign := true,
   __call__ := (self, loc, exp, size) >> WithBases(self,
       rec(operations := CmdOps,
       loc  := toAssignTarget(loc),
       exp  := toExpArg(exp),
       size := size)),

   rChildren := self >> [self.loc, self.exp, self.size],
   rSetChild := rSetChildFields("loc", "exp", "size"),

   print := (self,i,si) >> let(name := Cond(IsBound(self.isCompute) and self.isCompute,
                                            gap.colors.DarkYellow(self.__name__),
                                            IsBound(self.isLoad) and self.isLoad,
                                            gap.colors.DarkRed(self.__name__),
                                            IsBound(self.isStore) and self.isStore,
                                            gap.colors.DarkGreen(self.__name__),
                                            self.__name__),
                                Print(name, "(", self.loc, ", ", self.exp, ", ", self.size, ")"))
));

Class(cu_free, call);

Class(cu_call, call, rec(
    __call__ := arg >> let(
        len := Length(arg),
        self := arg[1],
        f := arg[2],
        dim_grid := Checked(IsList(arg[3]) or IsVar(arg[3]), arg[3]),
        dim_block := Checked(IsList(arg[4]) or IsVar(arg[4]), arg[4]),
        args := arg{[5..len]},
        WithBases(self, rec(
            func := f,
            dim_grid := dim_grid,
            dim_block := dim_block,
            operations := CmdOps,
            args := List(args, toExpArg)))
        ),

    rChildren := self >> [self.func, self.dim_grid, self.dim_block]::self.args,

    rSetChild := meth(self, n, newChild)
        local len;
        len := Length(self.args);
        if n > len+3 then Error(); fi;
        if n = 1 then
            self.func := newChild;
        elif n = 2 then
            self.dim_grid := newChild;
        elif n = 3 then
            self.dim_block := newChild;
        elif n > 3 and n <= len+3 then
            self.args[n-3] := newChild;
        fi;
    end,

    print := (self,i,si) >> Print(self.func, "<<<", self.dim_grid, ",", self.dim_block, ">>>", "(", PrintCS(self.args), ")")

));


%Terminals
    DollarSign ::= '$'
    Percent ::= '%'
    _
    a b c d e f g h i j k l m n o p q r s t u v w x y z
    A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
%End

%Headers
    /.
        static readonly int[] tokenKind = new int[128];
        static bool __b_init = init_block();
        static bool init_block()
        {
            tokenKind['$'] = $sym_type.$prefix$DollarSign$suffix$;
            tokenKind['%'] = $sym_type.$prefix$Percent$suffix$;
            tokenKind['_'] = $sym_type.$prefix$_$suffix$;

            tokenKind['a'] = $sym_type.$prefix$a$suffix$;
            tokenKind['b'] = $sym_type.$prefix$b$suffix$;
            tokenKind['c'] = $sym_type.$prefix$c$suffix$;
            tokenKind['d'] = $sym_type.$prefix$d$suffix$;
            tokenKind['e'] = $sym_type.$prefix$e$suffix$;
            tokenKind['f'] = $sym_type.$prefix$f$suffix$;
            tokenKind['g'] = $sym_type.$prefix$g$suffix$;
            tokenKind['h'] = $sym_type.$prefix$h$suffix$;
            tokenKind['i'] = $sym_type.$prefix$i$suffix$;
            tokenKind['j'] = $sym_type.$prefix$j$suffix$;
            tokenKind['k'] = $sym_type.$prefix$k$suffix$;
            tokenKind['l'] = $sym_type.$prefix$l$suffix$;
            tokenKind['m'] = $sym_type.$prefix$m$suffix$;
            tokenKind['n'] = $sym_type.$prefix$n$suffix$;
            tokenKind['o'] = $sym_type.$prefix$o$suffix$;
            tokenKind['p'] = $sym_type.$prefix$p$suffix$;
            tokenKind['q'] = $sym_type.$prefix$q$suffix$;
            tokenKind['r'] = $sym_type.$prefix$r$suffix$;
            tokenKind['s'] = $sym_type.$prefix$s$suffix$;
            tokenKind['t'] = $sym_type.$prefix$t$suffix$;
            tokenKind['u'] = $sym_type.$prefix$u$suffix$;
            tokenKind['v'] = $sym_type.$prefix$v$suffix$;
            tokenKind['w'] = $sym_type.$prefix$w$suffix$;
            tokenKind['x'] = $sym_type.$prefix$x$suffix$;
            tokenKind['y'] = $sym_type.$prefix$y$suffix$;
            tokenKind['z'] = $sym_type.$prefix$z$suffix$;

            tokenKind['A'] = $sym_type.$prefix$A$suffix$;
            tokenKind['B'] = $sym_type.$prefix$B$suffix$;
            tokenKind['C'] = $sym_type.$prefix$C$suffix$;
            tokenKind['D'] = $sym_type.$prefix$D$suffix$;
            tokenKind['E'] = $sym_type.$prefix$E$suffix$;
            tokenKind['F'] = $sym_type.$prefix$F$suffix$;
            tokenKind['G'] = $sym_type.$prefix$G$suffix$;
            tokenKind['H'] = $sym_type.$prefix$H$suffix$;
            tokenKind['I'] = $sym_type.$prefix$I$suffix$;
            tokenKind['J'] = $sym_type.$prefix$J$suffix$;
            tokenKind['K'] = $sym_type.$prefix$K$suffix$;
            tokenKind['L'] = $sym_type.$prefix$L$suffix$;
            tokenKind['M'] = $sym_type.$prefix$M$suffix$;
            tokenKind['N'] = $sym_type.$prefix$N$suffix$;
            tokenKind['O'] = $sym_type.$prefix$O$suffix$;
            tokenKind['P'] = $sym_type.$prefix$P$suffix$;
            tokenKind['Q'] = $sym_type.$prefix$Q$suffix$;
            tokenKind['R'] = $sym_type.$prefix$R$suffix$;
            tokenKind['S'] = $sym_type.$prefix$S$suffix$;
            tokenKind['T'] = $sym_type.$prefix$T$suffix$;
            tokenKind['U'] = $sym_type.$prefix$U$suffix$;
            tokenKind['V'] = $sym_type.$prefix$V$suffix$;
            tokenKind['W'] = $sym_type.$prefix$W$suffix$;
            tokenKind['X'] = $sym_type.$prefix$X$suffix$;
            tokenKind['Y'] = $sym_type.$prefix$Y$suffix$;
            tokenKind['Z'] = $sym_type.$prefix$Z$suffix$;
            return true;
        }

         public   static int getKind(char c)
        {
            return (((c & 0xFFFFFF80) == 0) /* 0 <= c < 128? */ ? tokenKind[c] : 0);
        }
    ./
%End


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

# Configuration of system dependent parameters
# ============================================

#F Global code generation options record
#F
SpiralDefaults := rec();

SPL_DEFAULTS := SpiralDefaults;


%Terminals
    DollarSign ::= '$'
    Percent ::= '%'
    _
    a b c d e f g h i j k l m n o p q r s t u v w x y z
%End

%Headers
    /.
        //
        // Each upper case letter is mapped into its corresponding
        // lower case counterpart. For example, if an 'A' appears
        // in the input, it is mapped into $sym_type::$prefix$a$suffix$ just
        // like 'a'.
        //
         inline static int tokenKind[128] = {};

        static bool static_init()
        {
            tokenKind['$'] = $sym_type::$prefix$DollarSign$suffix$;
            tokenKind['%'] = $sym_type::$prefix$Percent$suffix$;
            tokenKind['_'] = $sym_type::$prefix$_$suffix$;

            tokenKind['a'] = $sym_type::$prefix$a$suffix$;
            tokenKind['b'] = $sym_type::$prefix$b$suffix$;
            tokenKind['c'] = $sym_type::$prefix$c$suffix$;
            tokenKind['d'] = $sym_type::$prefix$d$suffix$;
            tokenKind['e'] = $sym_type::$prefix$e$suffix$;
            tokenKind['f'] = $sym_type::$prefix$f$suffix$;
            tokenKind['g'] = $sym_type::$prefix$g$suffix$;
            tokenKind['h'] = $sym_type::$prefix$h$suffix$;
            tokenKind['i'] = $sym_type::$prefix$i$suffix$;
            tokenKind['j'] = $sym_type::$prefix$j$suffix$;
            tokenKind['k'] = $sym_type::$prefix$k$suffix$;
            tokenKind['l'] = $sym_type::$prefix$l$suffix$;
            tokenKind['m'] = $sym_type::$prefix$m$suffix$;
            tokenKind['n'] = $sym_type::$prefix$n$suffix$;
            tokenKind['o'] = $sym_type::$prefix$o$suffix$;
            tokenKind['p'] = $sym_type::$prefix$p$suffix$;
            tokenKind['q'] = $sym_type::$prefix$q$suffix$;
            tokenKind['r'] = $sym_type::$prefix$r$suffix$;
            tokenKind['s'] = $sym_type::$prefix$s$suffix$;
            tokenKind['t'] = $sym_type::$prefix$t$suffix$;
            tokenKind['u'] = $sym_type::$prefix$u$suffix$;
            tokenKind['v'] = $sym_type::$prefix$v$suffix$;
            tokenKind['w'] = $sym_type::$prefix$w$suffix$;
            tokenKind['x'] = $sym_type::$prefix$x$suffix$;
            tokenKind['y'] = $sym_type::$prefix$y$suffix$;
            tokenKind['z'] = $sym_type::$prefix$z$suffix$;

            tokenKind['A'] = $sym_type::$prefix$a$suffix$;
            tokenKind['B'] = $sym_type::$prefix$b$suffix$;
            tokenKind['C'] = $sym_type::$prefix$c$suffix$;
            tokenKind['D'] = $sym_type::$prefix$d$suffix$;
            tokenKind['E'] = $sym_type::$prefix$e$suffix$;
            tokenKind['F'] = $sym_type::$prefix$f$suffix$;
            tokenKind['G'] = $sym_type::$prefix$g$suffix$;
            tokenKind['H'] = $sym_type::$prefix$h$suffix$;
            tokenKind['I'] = $sym_type::$prefix$i$suffix$;
            tokenKind['J'] = $sym_type::$prefix$j$suffix$;
            tokenKind['K'] = $sym_type::$prefix$k$suffix$;
            tokenKind['L'] = $sym_type::$prefix$l$suffix$;
            tokenKind['M'] = $sym_type::$prefix$m$suffix$;
            tokenKind['N'] = $sym_type::$prefix$n$suffix$;
            tokenKind['O'] = $sym_type::$prefix$o$suffix$;
            tokenKind['P'] = $sym_type::$prefix$p$suffix$;
            tokenKind['Q'] = $sym_type::$prefix$q$suffix$;
            tokenKind['R'] = $sym_type::$prefix$r$suffix$;
            tokenKind['S'] = $sym_type::$prefix$s$suffix$;
            tokenKind['T'] = $sym_type::$prefix$t$suffix$;
            tokenKind['U'] = $sym_type::$prefix$u$suffix$;
            tokenKind['V'] = $sym_type::$prefix$v$suffix$;
            tokenKind['W'] = $sym_type::$prefix$w$suffix$;
            tokenKind['X'] = $sym_type::$prefix$x$suffix$;
            tokenKind['Y'] = $sym_type::$prefix$y$suffix$;
            tokenKind['Z'] = $sym_type::$prefix$z$suffix$;
            return true;
        };
        inline static bool ddddd = static_init();
         int getKind(wchar_t c)
        {
            return (c < 128 ? tokenKind[c] : 0);
        }
    ./
%End


#
# AoC: Advent of Code solutions in GAP
#
# Implementations
#
AoC.Year2019 := rec();

InstallMethod( Puzzle,
    [IsString, IsString, IsFunction, IsFunction],
    function( Year, Day, PartOne, PartTwo )
        local puzzle;
        puzzle := rec(
            Input := function( )
                return InputTextFile(
                    Filename(
                        DirectoriesPackageLibrary( "AoC", StringFormatted( "input/{1}", Year ) )[1],
                        StringFormatted( "day{1}.txt", Day )
                    )
                );
            end,
            PartOne := PartOne,
            PartTwo := PartTwo
        );
        Objectify( TYPE_PUZZLE, puzzle );
        return puzzle;
    end );
InstallMethod( Input,
  "of a puzzle",
  [ IsPuzzle ],
  puzzle -> puzzle!.Input() );
InstallMethod( PartOne,
  "of a puzzle",
  [ IsPuzzle ],
  puzzle -> puzzle!.PartOne );
InstallMethod( PartTwo,
  "of a puzzle",
  [ IsPuzzle ],
  puzzle -> puzzle!.PartTwo );


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

Declare(_StandardMeasureVerify);

SetMakeOptsPThreads:=function(opts)
   opts.profile.makeopts.LDFLAGS:=Concat(When(IsBound(opts.profile.makeopts.LDFLAGS),opts.profile.makeopts.LDFLAGS,"")," -lpthread -L/lib/tls -lm");
   opts.profile.makeopts.ADDSRC:=Concat("../../../lib/smp2.c ",When(IsBound(opts.profile.makeopts.ADDSRC),opts.profile.makeopts.ADDSRC,""));
   opts.profile.makeopts.TIMER:="../common/time_threads.c";
   opts.profile.makeopts.TIMER_OPTS:="";
end;

SetMakeOptsSSE:=function(opts)
   opts.profile.makeopts.CFLAGS:=Concat(opts.profile.makeopts.CFLAGS," -msse3 -vec-report=0");
end;

SetMakeOptsOpenMP:=function(opts)
   opts.profile.makeopts.CFLAGS:=Concat(opts.profile.makeopts.CFLAGS," -openmp -openmp-report0");
end;

SetMakeOptsAffinity:=function(opts)
   opts.profile.makeopts.CFLAGS:=Concat(opts.profile.makeopts.CFLAGS," -DUSE_SCHED_AFFINITY");
end;

SetMakeOptsLibgen:=function(opts)
   opts.profile.makeopts.CFLAGS:=Concat(opts.profile.makeopts.CFLAGS," -fno-alias -fno-fnalias -fno-inline-functions");
end;

SetMakeOptsAssembly:=function(opts)
   opts.profile.makeopts.CFLAGS:=Concat(opts.profile.makeopts.CFLAGS," -save-temps");
end;

_default_makeopts := rec(
    GAP := "gap.c",
    STUB := "stub.h",
);

#
## default options for sim-outorder
#
# simple scalar uses a parametrized architecture. these params
# were inferred from details in the following paper:
#
# "Efficient Resource Sharing in Concurrent Error Detecting
# Superscalar Microarchitectures"
#
# Jared C. Smolens, Jangwoo Kim, James C. Hoe, and Babak Falsafi
#
# 37th Annual IEEE/ACM International Symposium on Microarchitecture
#

default_profiles := rec(
    no_compile := rec(
        name := "no-compile",
        meas := (a,b) -> 1000,
        verify := (a,b) -> false,
        makeopts := rec( CFLAGS := "" )
    ),

    # LINUX profiles
    ###############

    linux_power_gcc:=
    rec(
        name := "linux-power-gcc",
        makeopts := rec(
            CFLAGS := "",
            CC := "gcc" ),
        outdir := "/tmp/spiral/power",
        meas := (a, b) -> _StandardMeasureVerify(a, b, ""),
        verify := (a, b) -> _StandardMeasureVerify(a, b, "verify") ),

    arm_icount := rec(
        name := "arm-icount",
        makeopts := rec(
            CFLAGS := "-O2 -std=c99 -fomit-frame-pointer",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_arm := rec(
        name := "linux-arm",
        makeopts := rec(
            CFLAGS := "-O2 -std=c99 -fomit-frame-pointer",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_x86_icc := rec(
        name := "linux-x86",
        makeopts := rec(
            CC := "icc",
            CFLAGS := "-O3 -w -std=c99 -fomit-frame-pointer -vec-report0",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_avx_icc := rec(
        name := "linux-x86",
        makeopts := rec(
            CC := "icc",
            CFLAGS := "-O3 -w -mavx -std=c99 -fomit-frame-pointer -vec-report0",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_arm_pthread := rec(
        name := "linux-arm",
        makeopts := rec(
            CFLAGS := "-O3 -std=c99 -fomit-frame-pointer",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, "pthread"),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verifyPThread")
    ),

    linux_x86_icc_openmp := rec(
        name := "linux-x86",
        makeopts := rec(
            CC := "icc",
            CFLAGS := "-O -openmp -w -std=c99 -fomit-frame-pointer",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_x86_gcc := rec(
        name := "linux-x86",
        makeopts := rec(
            CC := "gcc",
            CFLAGS := "-O2 -Wall -fomit-frame-pointer -march=native -std=c99",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_x86_cuda := rec(
        name := "linux-cuda",
        makeopts := rec(
            CC := "nvcc",
            CFLAGS := "-O2 ",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_x86_threads := rec(
        name := "linux-x86",
        makeopts := rec(
            CC := "icc",
            CFLAGS := "-O -Wall -w -std=c99 -msse2",
            LDFLAGS := "-lpthread -L/lib/tls -lm",
            ADDSRC := "../../../lib/smp2.c",
            TIMER := "../common/time_threads.c",
            TIMER_OPTS := ""
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_x86_icc_threads := rec(
        name := "linux-x86",
        makeopts := rec(
            CC := "icc",
            CFLAGS := "-O -Wall -std=c99 -openmp",
            LDFLAGS := "-lguide -lpthread -L/lib/tls -lm",
            ADDSRC := "../../../lib/smp2.c",
            TIMER := "../common/time_threads.c",
            TIMER_OPTS := ""
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_x86_perfmon2 := rec(
        name := "linux-x86-perfmon2",
        makeopts := rec(
            CC := "gcc",
            CFLAGS := "-O3 -fomit-frame-pointer -std=c99",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_x86_newtimer := rec(
        name := "linux-x86-newtimer",
        makeopts := rec(
            CC := "gcc",
            CFLAGS := "-O3 -fomit-frame-pointer -std=c99",
            HOST := "localhost"
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_lrb_icc := rec(
      name := "linux-lrb-icc",
        makeopts := rec(
            CC := "icc",
            CFLAGS := "-std=c99 -w -vec-report0 -O",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

     linux_lrb_icc_openmp := rec(
        name := "linux-lrb-icc",
        makeopts := rec(
            CC := "icc",
            CFLAGS := "-O2 -openmp -w -std=c99 -fomit-frame-pointer",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_lrb_icc_threads := rec(
        name := "linux-lrb-icc",
        makeopts := rec(
            CC := "icc",
            CFLAGS := "-O2 -Wall -std=c99 -openmp",
            LDFLAGS := "-lguide -lpthread -L/lib/tls -lm",
            ADDSRC := "../../../lib/smp2.c",
            TIMER := "../common/time_threads.c",
            TIMER_OPTS := ""
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_x86_vtune := rec(
        name := "linux-x86-vtune",
        makeopts := rec(
            CC := "gcc",
            CFLAGS := "-O2 -g -fomit-frame-pointer -std=c99",
            RUNS := 1000,
            EVENT := "L1D_REPL",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_arm_gcc := rec(
        name := "linux-arm-gcc",
        makeopts := rec(
            CC := "gcc",
            CFLAGS := "-O2 -std=c99 -fomit-frame-pointer",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    # LINUX embedded profiles
    #########################

    linux_xscale_gcc := rec(
        name := "linux-xscale-gcc",
        makeopts := rec(
            CC := "arm-xscale-linux-gnu-gcc",
            CFLAGS := "-O2",
            COMPILER_DIR := "",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    ti_dsk_tms320c6713 := rec(
        name := "ti-dsk-tms320c6713",
        makeopts := rec(
            CFLAGS := "-O3 -std=c99",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    # MS WINDOWS profiles
    ##############

    win_x86_vcc := rec(
        name := "win-x86-vcc",
        makeopts := rec(
            CC := "cl",
            CFLAGS := "/O2",
        ),
        outdir := "/temp/vcc32",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_x64_vcc := rec(
        name := "win-x64-vcc",
        makeopts := rec(
            CC := "cl",
            CFLAGS := "/O3",
        ),
        premake := () -> "vcvarsall.bat amd64 > nul",
        outdir := "/temp/vcc64",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_x86_icc := rec(
        name := "win-x86-icc",
        makeopts := rec(
            CC := "icl",
            CFLAGS := "/O3", # /G7 /QxSSSE3",
        ),
        premake := () -> spiral.IntelC.ia32().premake,
        outdir := "/temp/icc32",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_x86_icc_openmp := rec(
        name := "win-x86-icc-openmp",
        makeopts := rec(
            CC := "icl",
            CFLAGS := "/O3 /Qopenmp", # /G7 /QxSSSE3",
#            LDFLAGS := "/NODEFAULTLIB:libc /NODEFAULTLIB:libm /NODEFAULTLIB:libirc /DEFAULTLIB:libcmt /DEFAULTLIB:libmmt /DEFAULTLIB:libircmt"
        ),
        premake := () -> spiral.IntelC.ia32().premake,
        outdir := "/temp/icc32omp",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_x86_icc_threads := rec(
        name := "win-x86-icc-threads",
        makeopts := rec(
            CC := "icl",
            CFLAGS := "/O3", # /G7 /QxSSSE3",
        ),
        premake := () -> spiral.IntelC.ia32().premake,
        outdir := "/temp/threads32",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_x64_icc := rec(
        name := "win-x64-icc",
        makeopts := rec(
            CC := "icl",
            CFLAGS := "/O3 /G7 /QxSSSE3",
        ),
        premake := () -> spiral.IntelC.em64t().premake,
        outdir := "/temp/icc64",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_x64_icc_openmp := rec(
        name := "win-x64-icc-openmp",
        makeopts := rec(
            CC := "icl",
            CFLAGS := "/O3 /G7 /QxSSSE3 /Qopenmp",
#            LDFLAGS := "/NODEFAULTLIB:libc /NODEFAULTLIB:libm /NODEFAULTLIB:libirc /DEFAULTLIB:libcmt /DEFAULTLIB:libmmt /DEFAULTLIB:libircmt"
        ),
        premake := () -> spiral.IntelC.em64t().premake,
        outdir := "/temp/icc64omp",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_x64_icc_threads := rec(
        name := "win-x64-icc-threads",
        makeopts := rec(
            CC := "icl",
            CFLAGS := "/O3 /G7 /QxSSSE3",
        ),
        premake := () -> spiral.IntelC.em64t().premake,
        outdir := "/temp/threads64",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_x86_gcc := rec(
        name := "win-x86-gcc",
        target := rec(name := "win-x86-gcc"),
        makeopts := rec(
            CC := "gcc",
            CFLAGS := "-O3 -march=native -std=c99 -Wno-implicit -Wno-aggressive-loop-optimizations",
        ),
        outdir := "/temp",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_x86_llvm := rec(
        name := "win-x86-llvm",
        target := rec(name := "win-x86-llvm"),
        makeopts := rec(
            CC := "clang",
            CFLAGS := "-O2 -march=native -std=c99 -Wall",
        ),
        outdir := "/temp",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_x86_nvcc_gpu := rec(
        name := "win-x86-nvcc-gpu",
        makeopts := rec(
            CC := "nvcc",
            CFLAGS := "",
            GAP:= "gap.cu"
        ),
#   premake := () -> spiral.IntelC.ia32().premake,
        outdir := "/temp/nvcc",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
  ),

    win_x64_cuda := rec (
	name := "win-x64-cuda",
	target := rec(name := "win-x64-cuda"),
	makeopts := rec (
	    CC := "nvcc",
	    CFLAGS := "",
	),
        outdir := "/temp",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    # altivec profiles
    # ------------------------------------------------------------------------------
    linux_altivec_gcc := rec(
        name := "linux-altivec-gcc",
        makeopts := rec(
            CFLAGS := ""
        ),
        stubopts := rec(
        ),
        outdir := "/tmp/spiral/altivec",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify"),
        verifyfftw := (a,b) -> _StandardMeasureVerify(a,b, "fftwverify")
    ),

    # CELL processor profiles
    ##############

    linux_cellSPU_gcc := rec(
        name := "linux-cellSPU-gcc",
        makeopts := rec(
            CFLAGS := ""
        ),
        stubopts := rec(
        ),
        outdir := "/tmp/spiral/cellSPU",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_cellSPU_gcc_MMM := rec(
        name := "linux-cellSPU-gcc-MMM",
        makeopts := rec(
            CFLAGS := ""
        ),
        stubopts := rec(
        ),
        outdir := "/tmp/spiral/cellSPU",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    linux_cellmultiSPU_gcc := rec(
        name := "linux-cellmultiSPU-gcc",
        makeopts := rec(
            CFLAGS := "",
            GAP_PPE := "gap_ppe.c",
            GAP_PPE_TWIDDLES_DECL := "twiddles-declare.h",
            GAP_PPE_TWIDDLES_SET  := "twiddles-set.h",
        ),
        stubopts := rec(
            SPUS := 1,
            MULTIBUFFER_ITERATIONS := 1,
        ),
        outdir := "/tmp/spiral/cellmultiSPU",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify"),
        verifyfftw := (a,b) -> _StandardMeasureVerify(a,b, "fftwverify"),
        verifyquick  := (a,b) -> _StandardMeasureVerify(a,b, "quickverify")
    ),

    linux_cellmultiSPU_speadk := rec(
        name := "linux-cellmultiSPU-speadk",
        makeopts := rec(
            CFLAGS := ""
        ),
        stubopts := rec(
            SPUS := 1,
            MULTIBUFFER_ITERATIONS := 1,
        ),
        outdir := "/tmp/spiral/cellmultiSPUspeadk",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify"),
        verifyfftw := (a,b) -> _StandardMeasureVerify(a,b, "fftwverify")
    ),

    linux_cellPPU_gcc := rec(
        name := "linux-cellPPU-gcc",
        makeopts := rec(
            CFLAGS := ""
        ),
        stubopts := rec(
        ),
        outdir := "/tmp/spiral/cellPPU",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    win_remote := rec(
        name := "win-remote",
        host := "host.domain",
        userid := "user",
        passwd := "password",
        outdir := "S:/temp/win-remote",
        hostdir := ".",
        exec := "makecmd.cmd",
        remote_cmd := "remote.cmd",
#        meas := (a,b) -> _RemoteMeasureVerify(a,b, ""),
#        verify := (a,b) -> _RemoteMeasureVerify(a,b, "verify"),
#        runmake := (a,b) -> _RemoteRunMake(a,b),
        remote := true,
        library := "vanilla",
        remote_library := "vanilla",
        appendSymbol := "$*",
#        download := opts -> _RemoteDownload(opts),
#        clean := opts -> _RemoteClean(opts),
        libdir := "profiler",
        dload_cmd := "download.cmd",
        clean_cmd := "clean.cmd"
    ),

    # SIMPLESCALAR 3.0 profiles
    ##############

    ssnix_simple_gcc_cachemiss := rec(
        name := "ssnix-simple-gcc",
        makeopts := rec(
            SSDIR := "~/ss",
            CFLAGS := "-O2 -I~/ss/include",
            SSBIN := "sim-cache",
            SSPARAM := "dl1.misses"
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b,""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    ssnix_simple_gcc_instr := rec(
        name := "ssnix-simple-gcc",
        makeopts := rec(
            SSDIR := "~/ss",
            CFLAGS := "-O2 -I~/ss/include",
            SSBIN := "sim-outorder",
            SSPARAM := "sim_cycle"
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b,""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    ssnix_simple_gcc_cachemissnew := rec(
        name := "ssnix-simple-gcc-new",
        makeopts := rec(
            SSDIR := "~/ss",
            CFLAGS := "-O2 -I~/ss/include",
            SSBIN := "sim-cache",
            SSPARAM := "dl1.misses",
            TIMER_OPTS := "-n 1"
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b,""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    # FPGA Hardware generator profile
    ####################

    fpga_splhdl := rec(
        name := "fpga-splhdl",
        outdir := "/tmp/spiral",
        threshold := 2048,
        makeopts := rec(
            OUTNAME := "gap.v",
            GAP := "gap.spl",
            VLOGLIB := "/Users/pam/eclwork/SPLHDL/support",
            NCV := "ncverilog",
            SPLHDL := "splhdl",
            GETRES := "getRes",
            IVERILOG := "iverilog",
            VVP := "vvp",
            DATATYPE := "fix 16", #other possibility 'float'.
            TWIDTYPE := ""
        ),
        meas := (a,b) -> _StandardMeasureVerify(a,b,""),
    ),

    # INTEL MAC profiles
    #######

    darwin_x86_gcc := rec(
        name := "darwin-x86",
        makeopts := rec(
            CC := "gcc",
            CFLAGS := "-O2 -fomit-frame-pointer -msse2 -std=c99",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    darwin_x86_icc := rec(
        name := "darwin-x86",
        makeopts := rec(
            CC := "icc",
            CFLAGS := "-O3 -fomit-frame-pointer -std=c99",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    # PTLSim backend
    ############

    linux_ptlsim_icc := rec(
        name := "linux-ptlsim",
        makeopts := rec(
            CC := "icc",
            CFLAGS := "-O3 -fomit-frame-pointer -std=c99",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify"),
    ),

    # Basilio's code analyzer.
     linux_x86_anl := rec(
         name := "linux-x86-anl",
         makeopts := rec(
             CC := "gcc",
             CFLAGS := "-O2 -msse2 -w -std=c99 -fomit-frame-pointer",
         ),
         outdir := "/tmp/spiral",
         meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
         verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
     ),

    # DPA simulator profile
    linux_dpa_sim := rec(
        name := "linux-dpa-simulator",
        makeopts := rec(
            DPA_DIR := "${HOME}/DPA",
            DPA_SPEC := "lmvec_memvec_vecint",
            CC := "gcc",
            CFLAGS := "",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

    # DPA emulator (modelsim) profile
    linux_dpa_emu := rec(
        name := "linux-dpa-modelsim",
        makeopts := rec(
            DPA_DIR := "${HOME}/DPA",
            DPA_SPEC := "lmvec_memvec_vecint",
            XCC_DIR := "/opt/sparc-elf-4.4.2",
            MODELSIM_DIR := "/opt/modelsim",
            CC := "sparc-elf-gcc",
            CFLAGS := "",
        ),
        outdir := "/tmp/spiral",
        meas := (a,b) -> _StandardMeasureVerify(a,b, ""),
        verify := (a,b) -> _StandardMeasureVerify(a,b, "verify")
    ),

);


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