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

Import(paradigms.vector.rewrite);
Import(paradigms.smp);
Import(paradigms.distributed);
Import(paradigms.multibuffer);

StandardVecRules := MergedRuleSet(RulesSplitComplex, StandardSumsRules, RulesSMP, RulesVec, RulesPropagate, RulesVDiag, RulesKickout, RulesTermGrp, TerminateSymSPL);
HfuncVecRules    := MergedRuleSet(StandardVecRules, RulesHfunc);

PartialVecTermRules := MergedRuleSet(RulesVRC, RulesRC, RulesTermGrp, RulesVRCTermDiag);
FullVecTermRules    := MergedRuleSet(RulesVRC, RulesRC, RulesTermGrp, RulesVRCTerm, RulesTerm, TerminateSymSPL);

ErrorOut := function(sums, opts)
    PrintLine("------------- Begin ------------------");
    PrintLine(sums);
    PrintLine("------------- End ------------------");
    Error("BP: postprocess");
    return(sums);
end;

ErrorOut1 := function(sums, opts)
   Print(".");
   return(sums);
end;

VecLibStrategiesCell := rec(
#    sigmaSpl := [ StandardSumsRules, StandardVecRules ],
   sigmaSpl := [
               #(s, opts) -> ErrorOut(s, opts),
               RemoveNoPull_Dist,
               StandardSumsRules ],

    postProcess := [
    #(s, opts) -> ErrorOut(s, opts),
    MergedRuleSet(PTensorRules, StandardSumsRules, RulesTermGrp, RemoveBuf),
    #(s, opts) -> ErrorOut(s, opts),
    MergedRuleSet(PTensorConvertRules, StandardSumsRules),
    #(s, opts) -> ErrorOut(s, opts),
    StandardSumsRules,
    #(s, opts) -> ErrorOut(s, opts),
    StandardVecRules,
    #(s, opts) -> ErrorOut(s, opts),
        MergedRuleSet(HfuncVecRules, PartialVecTermRules),
    #(s, opts) -> ErrorOut(s, opts),
       MergedRuleSet(HfuncVecRules, PartialVecTermRules),
    #(s, opts) -> ErrorOut(s, opts),
       #(s, opts) -> BlockSums(opts.globalUnrolling, s), #Doing this later (is this a problem?)
       #(s, opts) -> Process_fPrecompute(s, opts), # Doing this in the codegen so we can verify
       RecursStepTerm,
    #(s, opts) -> ErrorOut(s, opts),
       MergedRuleSet(StandardVecRules, FullVecTermRules),
    #(s, opts) -> ErrorOut(s, opts),
       MergedRuleSet(RulesHfunc, RulesFuncSimp, RulesStrengthReduce),
    #(s, opts) -> ErrorOut(s, opts),
        CellVRCTerm,
        RemoveBuf,
      DistMultiBuf,
        #RulesSums, # So composes are flattened before RulesComposeDists runs
       RulesComposeDists,
        #RulesDistMerge, # Not required because for the DMP algorithm, we now do this at a high level
    #(s, opts) -> ErrorOut(s, opts),
      (s, opts) -> applyCellRules(s, opts),
    #(s, opts) -> ErrorOut(s, opts),
        MultiBufDist, #NOTE: Need to do this after RC/VRC has been dealt with, (but before marking BBs). Why not move this to where DistMBuf is?
    #(s, opts) -> ErrorOut(s, opts), #Uncomment line to see state after VRC rules apply.
#      DistMultiBuf, # Why is this here? Why not move it down?
    #(s, opts) -> ErrorOut(s, opts),
        FixBorder,    #Border is used by MultiBufDist, and hence cannot precede it, since FixBorder removes the border
    #(s, opts) -> ErrorOut(s, opts),
        (s, opts) -> BlockSums(opts.globalUnrolling, s),
    #(s, opts) -> ErrorOut(s, opts),
#       (s, opts) -> CellDFTBlockCyclicLayoutHackWrap(s, opts),   # This does nothing if there are no GathRecv/ScatSend that still have functions
    #(s, opts) -> ErrorOut(s, opts),
        (s, opts) -> applyCellInplace(s, opts),
        RulesComposeStreams
    ],

    rc := [],
    preRC := [],
);

VecLibStrategies := rec(
#    sigmaSpl := [ StandardSumsRules, StandardVecRules ],
    sigmaSpl := [ StandardSumsRules ],

    postProcess := [
    #MergedRuleSet(PTensorRules, StandardSumsRules),
    #MergedRuleSet(PTensorConvertRules, StandardSumsRules),
    StandardSumsRules,
    StandardVecRules,
        MergedRuleSet(HfuncVecRules, PartialVecTermRules),
        MultiBufDist,
        MergedRuleSet(HfuncVecRules, PartialVecTermRules),
        (s, opts) -> BlockSums(opts.globalUnrolling, s),
        (s, opts) -> Process_fPrecompute(s, opts), # Doing this in the codegen so we can verify
        RecursStepTerm,
        MergedRuleSet(StandardVecRules, FullVecTermRules),
        MergedRuleSet(RulesHfunc, RulesFuncSimp, RulesStrengthReduce),
        CellVRCTerm,
        (s, opts) -> applyCellRules(s, opts),
        CellDFTBlockCyclicLayoutHack,
        (s, opts) -> applyCellInplace(s, opts),
        RulesComposeDists
    ],

    rc := [],
    preRC := [],
);

VecLibTerminate := [
    MergedRuleSet(StandardVecRules, FullVecTermRules),
    MergedRuleSet(RulesHfunc, RulesFuncSimp, RulesStrengthReduce),
];

#
# NOTE: implement layering
#
#Class(VecRecCodegen, RecCodegen, VectorCodegen);
Class(VecRecCodegen, RecCodegen, VectorCodegen, rec(

#	vRC_Compose := Rule([vRC, @(1, Compose)], e -> Compose(List(@(1).val.children(), vRC))),
#	vRC_SUM := Rule([vRC, @(1, SUM)], e -> SUM(List(@(1).val.children(), vRC))),
#	vRC_SUMAcc := Rule([vRC, @(1, SUMAcc)], e -> SUMAcc(List(@(1).val.children(), vRC))),

#	vRC_Container := Rule([vRC, @(1, [BB,Buf,Inplace,Grp,NoPull,NoPullLeft,NoPullRight, NoDiagPullin, NoDiagPullinLeft, NoDiagPullinRight ])],
#		e -> ObjId(@(1).val)(vRC(@(1).val.child(1)))),

#	vRC_Data := Rule([vRC, @(1, Data)], e -> Data(@(1).val.var, @(1).val.value, vRC(@(1).val.child(1)))),

#	vRC_RStep := Rule([vRC, @(1, RecursStep)], e -> RecursStep(2*@(1).val.yofs, 2*@(1).val.xofs,vRC(@(1).val.child(1)))),

#	vRC_ISum := Rule([vRC, @(1, ISum)], e -> ISum(@(1).val.var, @(1).val.domain, vRC(@(1).val.child(1)))),
#	vRC_ICompose := Rule([vRC, @(1, ICompose)], e -> ICompose(@(1).val.var, @(1).val.domain, vRC(@(1).val.child(1)))),

#	vRC_Grp := Rule([vRC, @(1, Grp)], e -> Grp(vRC(@(1).val.child(1)))),

#	vRC_Scale := Rule([vRC, @(1, Scale)], e ->
#		vRC(Diag(fConst(Rows(@(1).val), @(1).val.scalar))) * vRC(@(1).val.child(1))),

#	vRC_CR := Rule([vRC, @(1, CR)], e -> @(1).val.child(1)),

#	VTensor_VScale := Rule([@(1, VTensor), [VScale, @(2), @(3), @(4)]], e -> VScale(VTensor(@(2).val, @(1).val.vlen), @(3).val, @(4).val*@(1).val.vlen)),
#	VTensor_VTensor := Rule([@(1, VTensor), @(2, VTensor)], e -> VTensor(@(2).val.child(1), @(1).val.vlen*@(2).val.vlen)),

#	vRC_SMP := Rule([@(1, vRC), @(2, [SMPSum, SMPBarrier])],
#		e -> let(s := @(2).val, CopyFields(s, rec(_children := List(s.children(), c->ObjId(@(1).val)(c)), dimensions := @(1).val.dimensions)))),

# should RC and VRC be in some other place?
#	vRC_TCvt := Rule( [@(0, [vRC, RC, VRC]), @(1, TCvt)],
#		e -> let( t := @(1).val, TCvt( 2*t.n(), t.isa_to(), t.isa_from(), t.props()).withTags(t.getTags()).takeAobj(t) )),

));

Class(OpenMP_SSEUnparser, paradigms.smp.OpenMP_UnparseMixin, platforms.sse.SSEUnparser);
Class(OpenMP_SSEUnparser_ParFor, paradigms.smp.OpenMP_UnparseMixin_ParFor, platforms.sse.SSEUnparser);
Class(SMP_SSEUnparser,    paradigms.smp.SMP_UnparseMixin,    platforms.sse.SSEUnparser);
Class(SMP_NEONUnparser,    paradigms.smp.SMP_UnparseMixin,    platforms.neon.NEONUnparser);

Class(OpenMP_AVXUnparser, paradigms.smp.OpenMP_UnparseMixin, platforms.avx.AVXUnparser);
Class(OpenMP_AVXUnparser_ParFor, paradigms.smp.OpenMP_UnparseMixin_ParFor, platforms.avx.AVXUnparser);

SMP_NEONUnparser.preprocess := x -> FixAssign0(x);
SMP_SSEUnparser.preprocess := x -> FixAssign0(x);
OpenMP_SSEUnparser.preprocess := x -> FixAssign0(x);

Declare(_InitVecLibgen);
Declare(_InitVecParLibgenNEON);
Declare(_InitVecLibgenNEON);

InitVecLibgenCell := (isa, use_functions, use_openmp, use_buffering, simdopts) ->
    _InitVecLibgen(
        InitLibgen(CopyFields(LibgenDefaults, SIMDGlobals.getOpts(isa, simdopts),
        rec(generateComplexCode:=false))),
        use_functions, use_openmp, use_buffering, false);

InitVecLibgenNEON := (opt) ->
    _InitVecLibgenNEON(opt);

InitVecParLibgenNEON := (opt, use_functions, use_openmp, use_buffering) ->
    _InitVecParLibgenNEON(
			InitLibgen(CopyFields(LibgenDefaults, _InitVecLibgenNEON(opt), rec(generateComplexCode:=false))),
        use_functions, use_openmp, use_buffering, false);

InitVecParLibgenNEONCx := (opt, use_functions, use_openmp, use_buffering) ->
    _InitVecParLibgenNEON(
			InitLibgen(CopyFields(CplxLibgenDefaults, _InitVecLibgenNEON(opt), rec(generateComplexCode:=true))),
        use_functions, use_openmp, use_buffering, false);

InitVecLibgen := (isa, use_functions, use_openmp, use_buffering) ->
    _InitVecLibgen(
        InitLibgen(CopyFields(LibgenDefaults, SIMDGlobals.getOpts(isa,
                     rec(svct:=true, splitL:=true, oddSizes:=false)), rec(generateComplexCode:=false))),
        use_functions, use_openmp, use_buffering, false);

InitParLibgen := (use_functions, use_openmp, use_buffering) ->
    _InitVecLibgen(InitLibgen(LibgenDefaults), use_functions, use_openmp, use_buffering, false);

InitParLibgenCplx := (use_functions, use_openmp, use_buffering, use_inplace) -> CopyFields(
    _InitVecLibgen(InitLibgen(CplxLibgenDefaults), use_functions, use_openmp, use_buffering, use_inplace),
    rec(unparser := CMacroUnparserProg));

_InitVecLibgenNEON := function(opt)
#	local opts;
#	opts := platforms.neon.benchNEON().half.1d.dft_ic.small.cmplx().getOpts();
#	opts.profile := default_profiles.linux_arm;
	opt.globalUnrolling := 64;
	opt.breakdownRules.TL := [L_cx_real, SIMD_ISA_Bases1, SIMD_ISA_Bases2, IxLxI_kmn_n, IxLxI_kmn_km, L_mn_m_vec, IxLxI_vtensor];
	return opt;
end;

_InitVecParLibgenNEON := function(opt, use_functions, use_openmp, use_buffering, use_inplace)
    local opts, m, clet_size;
#		opt := platforms.neon.benchNEON().half.1d.dft_ic.small.cmplx().getOpts();
		#	opts.profile := default_profiles.linux_arm_pthread;
		opt.globalUnrolling := 64;
		opt.breakdownRules.TL := [L_cx_real, SIMD_ISA_Bases1, SIMD_ISA_Bases2, IxLxI_kmn_n, IxLxI_kmn_km, L_mn_m_vec, IxLxI_vtensor];
    m := When(not use_functions, 64, 32);
    clet_size := m;

    opts := CopyFields(opt, rec(
#        formulaStrategies := Copy(VecLibStrategies),
        breakdownRules := CopyFields(opt.breakdownRules, rec(
            GT := [
                CopyFields(GT_Base, rec(maxSize:=false)),
                GT_NthLoop,
                CopyFields(GT_DFT_CT, rec(
                        minRank := 1,
                        maxRank := When(use_buffering, 1, 0),
                        minSize := When(use_functions, m+1, 1),
                        forTransposition := false,   # What is the right value? beta.anl needs false.
                        codeletSize := When(use_inplace, clet_size, false),
                        inplace := use_inplace)),
                CopyFields(GT_Par, rec(parEntireLoop := false, splitLoop := true)),
                GT_Vec_AxI,
                GT_Vec_IxA, GT_Vec_IxA_L, GT_Vec_L_IxA,
                GT_Vec_SplitL ],
#						DFT := opts.breakdownRules.DFT :: [
#                CopyFields(DFT_GT_CT, rec(
#                        codeletSize := When(use_inplace, clet_size, false),
#                        inplace := use_inplace,
#                        minSize := When(use_functions, m+1, 1)))
#						],
            DFT := [
                DFT_Rader, DFT_GoodThomas, DFT_PD, DFT_Base,
                CopyFields(DFT_CT, rec(maxSize := m)),
                CopyFields(DFT_GT_CT, rec(
                        codeletSize := When(use_inplace, clet_size, false),
                        inplace := use_inplace,
            # YSV: Please don't uncomment without talking to YSV
                        #requiredFirstTag := [AVecReg, AVecRegCx, AParSMP, ParCell],
                        minSize := When(use_functions, m+1, 1)))
            ],
            DFT3     := [ DFT3_Base, DFT3_CT ],
            MDDFT    := [ MDDFT_Base, MDDFT_tSPL_RowCol ],
            # Below is used for MDDFT. MDDFT with inplaceness excludes DFT with inplaceness
            # Thus setting use_inplace=false will disable DFT inplaceness, and enable it in MDDFT
            # NOTE: above is ugly! the only way to fix it is to figure out storage schemes automatically..
            TTensor  := [ CopyFields(AxI_IxB,rec(inplace:=not use_inplace)),
                          CopyFields(IxB_AxI,rec(inplace:=not use_inplace)) ],
            TTensorI := [ TTensorI_toGT ],
            TCompose := [ TCompose_tag ],
#D            TTag     := [TTag_down],
            InfoNt   := [Info_Base]
        )),

        codegen := VecRecCodegen,
        libgen := CopyFields(opt.libgen, rec(terminateStrategy := VecLibTerminate)),
        compileStrategy := IndicesCS,
        useDeref := true
    ));

    if use_buffering then Add(opts.breakdownRules.GT,
            CopyFields(GT_BufReshape, rec(bufIters := [2,4,8,16], u := [2,4]))); fi;

    if not use_functions then
        opts.baseHashes := DropLast(opts.baseHashes, 1);
        Append(opts.formulaStrategies.postProcess, opts.libgen.terminateStrategy);
    fi;

    if use_openmp then
        opts.unparser := OpenMP_SSEUnparser;
    else
        # should not be needed in latest version
        opts.subParams := [var("num_threads", TInt), var("tid", TInt)];
        opts.unparser := SMP_NEONUnparser;
        opts.profile := When(LocalConfig.osinfo.isWindows(), # or LocalConfig.osinfo.isCygwin(),
            LocalConfig.cpuinfo.profile.threads(),
            profiler.default_profiles.linux_x86_threads
        );
    fi;

    return opts;
end;

_InitVecLibgen := function(opts, use_functions, use_openmp, use_buffering, use_inplace)
    local opts, m, clet_size;
    m := When(not use_functions, 64, 32);
    clet_size := m;

    opts := CopyFields(opts, rec(
        formulaStrategies := Copy(VecLibStrategies),
        breakdownRules := CopyFields(opts.breakdownRules, rec(
            GT := [
                CopyFields(GT_Base, rec(maxSize:=false)),
                GT_NthLoop,
                CopyFields(GT_DFT_CT, rec(
                        minRank := 1,
                        maxRank := When(use_buffering, 1, 0),
                        minSize := When(use_functions, m+1, 1),
                        forTransposition := false,   # What is the right value? beta.anl needs false.
                        codeletSize := When(use_inplace, clet_size, false),
                        inplace := use_inplace)),
                CopyFields(GT_Par, rec(parEntireLoop := false, splitLoop := true)),
                GT_Vec_AxI,
                GT_Vec_IxA, GT_Vec_IxA_L, GT_Vec_L_IxA,
                GT_Vec_SplitL ],
            DFT := [
                DFT_Rader, DFT_GoodThomas, DFT_PD, DFT_Base,
                CopyFields(DFT_CT, rec(maxSize := m)),
                CopyFields(DFT_GT_CT, rec(
                        codeletSize := When(use_inplace, clet_size, false),
                        inplace := use_inplace,
            # YSV: Please don't uncomment without talking to YSV
                        #requiredFirstTag := [AVecReg, AVecRegCx, AParSMP, ParCell],
                        minSize := When(use_functions, m+1, 1)))
            ],
            DFT3     := [ DFT3_Base, DFT3_CT ],
            MDDFT    := [ MDDFT_Base, MDDFT_tSPL_RowCol ],
            # Below is used for MDDFT. MDDFT with inplaceness excludes DFT with inplaceness
            # Thus setting use_inplace=false will disable DFT inplaceness, and enable it in MDDFT
            # NOTE: above is ugly! the only way to fix it is to figure out storage schemes automatically..
            TTensor  := [ CopyFields(AxI_IxB,rec(inplace:=not use_inplace)),
                          CopyFields(IxB_AxI,rec(inplace:=not use_inplace)) ],
            TTensorI := [ TTensorI_toGT ],
            TCompose := [ TCompose_tag ],
#D            TTag     := [TTag_down],
            InfoNt   := [Info_Base]
        )),

        codegen := VecRecCodegen,
        libgen := CopyFields(opts.libgen, rec(terminateStrategy := VecLibTerminate)),
        compileStrategy := IndicesCS,
        useDeref := true
    ));

    if use_buffering then Add(opts.breakdownRules.GT,
            CopyFields(GT_BufReshape, rec(bufIters := [2,4,8,16], u := [2,4]))); fi;

    if not use_functions then
        opts.baseHashes := DropLast(opts.baseHashes, 1);
        Append(opts.formulaStrategies.postProcess, opts.libgen.terminateStrategy);
    fi;

    if use_openmp then
        opts.unparser := OpenMP_SSEUnparser;
    else
        # should not be needed in latest version
        opts.subParams := [var("num_threads", TInt), var("tid", TInt)];
        opts.unparser := SMP_SSEUnparser;
        opts.profile := When(LocalConfig.osinfo.isWindows(), # or LocalConfig.osinfo.isCygwin(),
            LocalConfig.cpuinfo.profile.threads(),
            profiler.default_profiles.linux_x86_threads
        );
    fi;

    return opts;
end;

# Example: doParSimdDft(1, 8, false, false, false); # no threads
# Example: doParSimdDft(4, 8, false, false, false); # 4 threads, SPMD
# Example: doParSimdDft(4, 8, false, true, false);  # 4 threads, OpenMP
# Example: doParSimdDft(4, 8, true, true, false);   # 4 threads, OpenMP, codelet reuse
#
doParSimdDft := function(arg)
    local sizes, opts, dpbench, tags, name, isa, p, logn, use_functions, use_openmp,
        use_buffering, interleavedComplex, argrec,simd_opts;

    isa := arg[1];
    p := arg[2];
    logn := arg[3];
    if Length(arg) = 4 and IsRec(arg[4]) then
        argrec := CopyFields(rec(
            use_functions := false,
            use_openmp := true,
            use_buffering := false,
            interleavedComplex := true,
            simd_opts := rec()
        ), arg[4]);
        use_functions := argrec.use_functions;
        use_openmp := argrec.use_openmp;
        use_buffering := argrec.use_buffering;
        interleavedComplex := argrec.interleavedComplex;
        simd_opts := argrec.simd_opts;
    else
        use_functions := arg[4];
        use_openmp := arg[5];
        use_buffering := arg[6];
        interleavedComplex := arg[7];
        simd_opts := rec();
    fi;

    opts := InitVecLibgen(isa, use_functions, use_openmp, use_buffering);
    if use_openmp then opts.language := "c.icl.openmp"; fi;

    if IsList(logn) then sizes := logn;
    else sizes := List([2 * isa.v * p^2 .. logn], d -> 2^d); fi;

    tags := When(p=1, [AVecReg(opts.vector.isa)], [AParSMP(p), AVecReg(opts.vector.isa)]);
    opts.benchTransforms := List(sizes, d -> When(interleavedComplex, InterleavedComplexT, SplitComplexT)(DFT(d)).withTags(tags));
    if p = 1 then
        PrintLine("ISA: ", isa, ", sizes: ", sizes);
        name := isa.name;
    else
        PrintLine("ISA: ", isa, ", threads: ",p,", sizes: ", sizes);
        name := Concat(StringInt(p), "p_", isa.name);
    fi;
    dpbench := DPBench(rec((name) := opts),
                    rec(timeBaseCases := false, verbosity:=0));
    return dpbench;
end;

doParSimdMddft := function(isa, p, logn, use_functions, use_openmp, use_buffering)
    local sizes, opts, dpbench, tags;
    opts := InitVecLibgen(isa, use_functions, use_openmp, use_buffering);

    if IsList(logn) then sizes := logn;
    else sizes := List(Cartesian([4..logn], [4..logn]), d -> [2^d[1], 2^d[2]]); fi;

    tags := When(p=1, [AVecReg(opts.vector.isa)], [AParSMP(p), AVecReg(opts.vector.isa)]);
    PrintLine("ISA: ", isa, ", threads: ",p,", sizes: ", sizes);

    opts.benchTransforms := List(sizes, d -> TRC(MDDFT(d)).withTags(tags));
    dpbench := DPBench(rec((Concat(StringInt(p), "p_", isa.name)) := opts),
                       rec(timeBaseCases := false, verbosity:=0));
    return dpbench;
end;

doParSimdWht := function(isa, p, logn, use_functions, use_openmp, use_buffering)
    local sizes, opts, dpbench, tags;
    opts := InitVecLibgen(isa, use_functions, use_openmp, use_buffering);

    if use_openmp then opts.language := "c.icl.openmp"; else opts.language := "c.icl.opt.core2"; fi;

    if IsList(logn) then sizes := logn;
    else sizes := [Log2Int(isa.v^2 * p^2) .. logn]; fi;

    tags := When(p=1, [AVecReg(opts.vector.isa)], [AParSMP(p), AVecReg(opts.vector.isa)]);
    opts.benchTransforms := List(sizes, d -> WHT(d).withTags(tags));
    PrintLine("ISA: ", isa, ", threads: ",p,", sizes: ", sizes);
    dpbench := DPBench(rec((Concat(StringInt(p), "p_", isa.name)) := opts),
                       rec(timeBaseCases := false, verbosity:=0));
    return dpbench;
end;

Class(cellopts_tmp, rec(
  sc := rec(tsplRader:=false, tsplBluestein:=false, tsplPFA:=false, oddSizes:=false, interleavedComplex := false),
  ic := rec(tsplRader:=false, tsplBluestein:=false, tsplPFA:=false, oddSizes:=false, interleavedComplex := true),
));

#F doMBufIndeParSimdDftCell_old([sizes], isa, p, mbuf_its)
doMBufIndeParSimdDftCell_old := function(arg)
#doMBufIndeParSimdWhtCell := function(arg)
    local sizes, opts, tags, dpbench, mbuf_its, name, isa, logn, use_functions, use_openmp,
        use_buffering, interleavedComplex, argrec, simd_opts, transform, p, extra_its;

    sizes := arg[1];
    isa   := arg[2];
    p     := arg[3];
    mbuf_its := arg[4];

    use_functions := false;
    use_openmp := true;
    use_buffering := false;
    interleavedComplex := true; #NOTE: change this?
    simd_opts := rec();

    opts := InitVecLibgenCell(isa, use_functions, use_openmp, use_buffering, cellopts_tmp.sc);
    opts.compileStrategy := IndicesCS2_FMA;
    opts.spus     := p;
    opts.multibuffer_its := mbuf_its;
    opts.codegen  := MBufCodegen;
    opts.unparser := isa.unparser;
    opts.profile  := isa.backendConfig.profile;

    #opts.globalUnrolling := opts.globalUnrolling*p;
    opts.globalUnrolling := 520;

    opts.measSteadyState := true;

    #opts.breakdownRules.GT := [ GT_Base, GT_NthLoop, GT_DFT_CT, GT_CellDMP_base, GT_CellDMP_gen, GT_Cell, GT_Vec_AxI, GT_Vec_IxA_Push, GT_Vec_IxA_L, GT_Vec_L_IxA, GT_MBufCell_spec ];
    opts.breakdownRules.GT := [ GT_Base, GT_NthLoop, GT_DFT_CT, GT_CellDMP_base, GT_CellDMP_gen, GT_Cell, GT_Vec_AxI, GT_Vec_IxA_Push, GT_Vec_IxA_L, GT_Vec_L_IxA ];
    opts.breakdownRules.TL := Concatenation(opts.breakdownRules.TL, [  TL_CellDMP ]);

    # Determine best packet size here. Packet size is simply size of kernel

    # Determine the correct # of extra loops here
    # For block size of 16384 bytes, we need (16384/(opts.vector.bits/8))/

    # WHT:
    #transform := k -> GT(WHT(LogInt(k,2)), GTPar, GTPar, [mbuf_its*p*((16384/(opts.vector.isa.bits/8))/k)]).withTags(
    #            Concatenation([ParCell(p, k*((16384/(opts.vector.isa.bits/8))/k)), MBufCell(mbuf_its)], opts.tags));

    # DFT_ic:
    transform := k -> GT( TRC(GT(DFT(k,1,false), GTPar, GTPar, [((16384/(opts.vector.isa.bits/8))/(2*k))])), GTPar, GTPar, [mbuf_its*p]).withTags(
                        Concatenation([ParCell(p, 2*k*((16384/(opts.vector.isa.bits/8))/(2*k))), MBufCell(mbuf_its)], opts.tags) );

    # DFT_sc: (untested)
    #transform := k -> GT( SplitComplexT(DFT(k,1,false)), GTPar, GTPar, [mbuf_its*p*((16384/(opts.vector.isa.bits/8))/(2*k))]).withTags(
    #                     Concatenation([ParCell(p, 2*k*((16384/(opts.vector.isa.bits/8))/(2*k))), MBufCell(mbuf_its)], opts.tags) );

    #Error("BP");
    opts.benchTransforms := List(sizes, transform);

    PrintLine("ISA: ", isa, ", Multibuf_its: ",mbuf_its,", sizes: ", sizes);
    name := Concat(StringInt(p), "p_", StringInt(mbuf_its), "mbuf_", isa.name, "_ic");

    dpbench := DPBench(rec((name) := opts),
                    rec(timeBaseCases := false, verbosity:=0));
    return dpbench;
end;

#F doMBufIndeParSimdDftCell([sizes], isa, p, mbuf_its)
#F Does a multibffered, parallel DFT, but not the IxDFT.
doMBufIndeParSimdDftCell_single := function(arg)
#doMBufIndeParSimdWhtCell := function(arg)
    local sizes, opts, tags, dpbench, mbuf_its, name, isa, logn, use_functions, use_openmp,
        use_buffering, interleavedComplex, argrec, simd_opts, transform, p, extra_its;

    sizes := arg[1];
    isa   := arg[2];
    p     := arg[3];
    mbuf_its := arg[4];

    use_functions := false;
    use_openmp := true;
    use_buffering := false;
    interleavedComplex := true; #NOTE: change this?
    simd_opts := rec();

    opts := InitVecLibgenCell(isa, use_functions, use_openmp, use_buffering, cellopts_tmp.sc);
    opts.compileStrategy := IndicesCS2_FMA;
    opts.spus     := p;
    opts.multibuffer_its := mbuf_its;
    opts.codegen  := MBufCodegen;
    opts.unparser := isa.unparser;
    opts.profile  := isa.backendConfig.profile;

    #opts.globalUnrolling := opts.globalUnrolling*p;
    opts.globalUnrolling := 520;

    opts.measSteadyState := true;

    #opts.breakdownRules.GT := [ GT_Base, GT_NthLoop, GT_DFT_CT, GT_CellDMP_base, GT_CellDMP_gen, GT_Cell, GT_Vec_AxI, GT_Vec_IxA_Push, GT_Vec_IxA_L, GT_Vec_L_IxA, GT_MBufCell_spec ];
    opts.breakdownRules.GT := [ GT_Base, GT_NthLoop, GT_DFT_CT, GT_CellDMP_base, GT_CellDMP_gen, GT_Cell, GT_Vec_AxI, GT_Vec_IxA_Push, GT_Vec_IxA_L, GT_Vec_L_IxA];
    opts.breakdownRules.TL := Concatenation(opts.breakdownRules.TL, [  TL_CellDMP ]);

    # Determine best packet size here. Packet size is simply size of kernel

    # Determine the correct # of extra loops here
    # For block size of 16384 bytes, we need (16384/(opts.vector.bits/8))/

    # WHT:
    #transform := k -> GT(WHT(LogInt(k,2)), GTPar, GTPar, [mbuf_its*p*((16384/(opts.vector.isa.bits/8))/k)]).withTags(
    #            Concatenation([ParCell(p, k*((16384/(opts.vector.isa.bits/8))/k)), MBufCell(mbuf_its)], opts.tags));

    # DFT_ic:
    #transform := k -> GT( TRC(DFT(k,1,false)), GTPar, GTPar, [mbuf_its*p]).withTags( Concatenation([ParCell(p, (2*k)), MBufCell(mbuf_its)], opts.tags) );

    # DFT_sc:
    transform := k -> GT( SplitComplexT(DFT(k,1,false)), GTPar, GTPar, [mbuf_its*p]).withTags( Concatenation([ParCell(p, (2*k)), MBufCell(mbuf_its)], opts.tags) );

    opts.benchTransforms := List(sizes, transform);

    PrintLine("ISA: ", isa, ", Multibuf_its: ",mbuf_its,", sizes: ", sizes);
    name := Concat(StringInt(mbuf_its), "mbuf_", StringInt(p), "p_", isa.name, "_sc");

    dpbench := DPBench(rec((name) := opts),
                    rec(timeBaseCases := false, verbosity:=0));
    return dpbench;
end;

#F doMBufIndeParSimdDftCell([sizes], isa, p, mbuf_its)
#F Does just the IxBase (no parallelization, no multibuffering)
doMBufIndeParSimdDftCell_IxDFTbase := function(arg)
#doMBufIndeParSimdWhtCell := function(arg)
    local sizes, opts, tags, dpbench, mbuf_its, name, isa, logn, use_functions, use_openmp,
        use_buffering, interleavedComplex, argrec, simd_opts, transform, p, extra_its;

    sizes := arg[1];
    isa   := arg[2];
    p     := arg[3];
    mbuf_its := arg[4];

    use_functions := false;
    use_openmp := true;
    use_buffering := false;
    interleavedComplex := true; #NOTE: change this?
    simd_opts := rec();

    opts := InitVecLibgenCell(isa, use_functions, use_openmp, use_buffering, cellopts_tmp.sc);
    opts.compileStrategy := IndicesCS2_FMA;
    opts.spus     := p;
    opts.multibuffer_its := mbuf_its;
    opts.codegen  := MBufCodegen;
    opts.unparser := isa.unparser;
    opts.profile  := isa.backendConfig.profile;

    #opts.globalUnrolling := opts.globalUnrolling*p;
    opts.globalUnrolling := 520;

    opts.measSteadyState := true;

    #opts.breakdownRules.GT := [ GT_Base, GT_NthLoop, GT_DFT_CT, GT_CellDMP_base, GT_CellDMP_gen, GT_Cell, GT_Vec_AxI, GT_Vec_IxA_Push, GT_Vec_IxA_L, GT_Vec_L_IxA, GT_MBufCell_spec ];
    opts.breakdownRules.GT := [ GT_Base, GT_NthLoop, GT_DFT_CT, GT_CellDMP_base, GT_CellDMP_gen, GT_Cell, GT_Vec_AxI, GT_Vec_IxA_Push, GT_Vec_IxA_L, GT_Vec_L_IxA ];
    opts.breakdownRules.TL := Concatenation(opts.breakdownRules.TL, [  TL_CellDMP ]);

    # Determine best packet size here. Packet size is simply size of kernel

    # Determine the correct # of extra loops here
    # For block size of 16384 bytes, we need (16384/(opts.vector.bits/8))/

    # WHT:
    #transform := k -> GT(WHT(LogInt(k,2)), GTPar, GTPar, [mbuf_its*p*((16384/(opts.vector.isa.bits/8))/k)]).withTags(
    #            Concatenation([ParCell(p, k*((16384/(opts.vector.isa.bits/8))/k)), MBufCell(mbuf_its)], opts.tags));

    # DFT_ic:
    #transform := k -> TRC(GT(DFT(k,1,false), GTPar, GTPar, [((16384/(opts.vector.isa.bits/8))/(2*k))])).withTags(opts.tags);

    # DFT_sc:
    transform := k -> GT( SplitComplexT(DFT(k,1,false)), GTPar, GTPar, [((16384/(opts.vector.isa.bits/8))/(2*k))] ).withTags(opts.tags);

    opts.benchTransforms := List(sizes, transform);

    PrintLine("ISA: ", isa, ", Multibuf_its: ",mbuf_its,", sizes: ", sizes);
    name := Concat("Ix_", isa.name, "_sc");

    dpbench := DPBench(rec((name) := opts),
                    rec(timeBaseCases := false, verbosity:=0));
    return dpbench;
end;

RecursStep.needInterleavedLeft := self >> self.child(1).needInterleavedLeft();
RecursStep.needInterleavedRight := self >> self.child(1).needInterleavedRight();

RTWrap.needInterleavedRight := self >> false;
RTWrap.needInterleavedLeft := self >> false;

VGath.mkCodelet    := self >> ObjId(self)(self.func.mkCodelet(), self.v);
VGath_sv.mkCodelet := self >> ObjId(self)(self.func.mkCodelet(), self.v, self.sv);
VScat.mkCodelet    := self >> ObjId(self)(self.func.mkCodelet(), self.v);
VScat_sv.mkCodelet := self >> ObjId(self)(self.func.mkCodelet(), self.v, self.sv);
VTensor.mkCodelet  := self >> ObjId(self)(self.child(1).mkCodelet(), self.vlen);

VRC.mkCodelet   := self >> ObjId(self)(self.child(1).mkCodelet(), self.v);
VRCL.mkCodelet  := self >> ObjId(self)(self.child(1).mkCodelet(), self.v);
VRCR.mkCodelet  := self >> ObjId(self)(self.child(1).mkCodelet(), self.v);
VRCLR.mkCodelet := self >> ObjId(self)(self.child(1).mkCodelet(), self.v);

BlockVPerm.mkCodelet := self >> self;
VPerm.mkCodelet      := self >> self;
VPrm_x_I.mkCodelet   := self >> self;

VDiag.mkCodelet     := self >> ObjId(self)(self.element.mkCodelet(), self.v);
VDiag_x_I.mkCodelet := self >> ObjId(self)(self.element.mkCodelet(), self.v);
VRCDiag.mkCodelet   := self >> ObjId(self)(self.element.mkCodelet(), self.v);

VData.signature     := self >> CodeletSignature(self.func);
VData.codeletParams := self >> CodeletParams(self.func);
VData.mkCodelet     := self >> ObjId(self)(MkCodelet(self.func), self.v);
VData.codeletShape  := self >> [ObjId(self), CodeletShape(self.func), self.v];

VDup.signature     := self >> CodeletSignature(self.func);
VDup.codeletParams := self >> CodeletParams(self.func);
VDup.mkCodelet     := self >> ObjId(self)(MkCodelet(self.func), self.v);
VDup.codeletShape  := self >> [ObjId(self), CodeletShape(self.func), self.v];

# NOTE:

# Also children/rChildren is invalid throughout
# children/rChildren inconsistent with constructor

# cutoff_size is eg. 8, then vector length 4 results in VTensor(DFT(2), 4),
# which is size 16, and will be terminated to a Blk(...). How do we prevent this?

# niterate := function(clet_set)
#    s := vrec4(7, 8)[2]; s := prep(s);
#    UniteSet(clet_set, List(clets(s), CodeletName));
#    return clet_set;
# end;
#
# iterate := function(clet_set)
#    s := vrec4(7, 8)[2]; s := prep(s);
#    UniteSet(clet_set, clets(s));
#    return clet_set;
# end;


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

#############################################################################
##
#M  AutomGrp2FR (<G>)
##
InstallMethod(AutomGrp2FR, "for [IsSelfSimilarGroup]", [IsSelfSimilarGroup],
function(G)
  local fam,ngens;
  fam:=UnderlyingSelfSimFamily(G);
  ngens:=Length(fam!.names);
  return SCGroup(FRMachine(fam!.names, List(fam!.recurlist{[1..ngens]},x->x{[1..fam!.deg]}),
                                  List(fam!.recurlist{[1..ngens]},x->x[fam!.deg+1])));
end);

#############################################################################
##
#M  AutomGrp2FR (<G>)
##
InstallMethod(AutomGrp2FR, "for [IsSelfSimGroup]", [IsSelfSimGroup],
function(G)
  local fam,gens_letter_reps,ngens,M;
  gens_letter_reps:=List(GeneratorsOfGroup(G),w->LetterRepAssocWord(w!.word));
  fam:=UnderlyingSelfSimFamily(G);
  ngens:=Length(fam!.names);
  M:=FRMachine(fam!.names, List(fam!.recurlist{[1..ngens]},x->x{[1..fam!.deg]}),
                                  List(fam!.recurlist{[1..ngens]},x->x[fam!.deg+1]));
  return Group(List(gens_letter_reps,w->FRElement(M,w)));
end);

#############################################################################
##
#M  AutomGrp2FR (<G>)
##
InstallMethod(AutomGrp2FR, "for [IsSelfSimilarSemigroup]", [IsSelfSimilarSemigroup],
function(G)
  local fam,ngens;
  fam:=UnderlyingSelfSimFamily(G);
  ngens:=Length(fam!.names);
  return SCSemigroup(FRMachine(fam!.names, List(fam!.recurlist{[1..ngens]},x->x{[1..fam!.deg]}),
                                  List(fam!.recurlist{[1..ngens]},x->x[fam!.deg+1])));
end);

#############################################################################
##
#M  AutomGrp2FR (<G>)
##
InstallMethod(AutomGrp2FR, "for [IsSelfSimSemigroup]", [IsSelfSimSemigroup],
function(G)
  local fam,gens_letter_reps,ngens,M;
  gens_letter_reps:=List(GeneratorsOfSemigroup(G),w->LetterRepAssocWord(w!.word));
  fam:=UnderlyingSelfSimFamily(G);
  ngens:=Length(fam!.names);
  M:=FRMachine(fam!.names, List(fam!.recurlist{[1..ngens]},x->x{[1..fam!.deg]}),
                                  List(fam!.recurlist{[1..ngens]},x->x[fam!.deg+1]));
  return Semigroup(List(gens_letter_reps,w->FRElement(M,w)));
end);

#############################################################################
##
#M  AutomGrp2FR (<G>)
##
InstallMethod(AutomGrp2FR, "for [IsAutomatonGroup]", [IsAutomatonGroup],
function(G)
  local recur_list, fam, names, d, i, s;
  fam := UnderlyingAutomFamily(G);
  names := fam!.names;
  d:=DegreeOfTree(fam);
  recur_list := StructuralCopy(fam!.automatonlist{[1..Length(names)]});

  # if identity is not one of the states
  if Length(fam!.automatonlist)=2*Length(names) then
    for s in recur_list do
      for i in [1..d] do s[i]:=[s[i]]; od;
    od;
  # if identity is one of the states, it should be the last one, so
  # when converting to the recurlist, we replace corresponding entries
  # with empty lists
  else
    for s in recur_list do
      for i in [1..d] do
        if s[i]<=Length(names) then
          s[i]:=[s[i]];
        else
          s[i]:=[];
        fi;
      od;
    od;
  fi;
  return AutomGrp2FR(SelfSimilarGroup(recur_list{[1..Length(names)]},names));
end);

#############################################################################
##
#M  AutomGrp2FR (<G>)
##
InstallMethod(AutomGrp2FR, "for [IsAutomGroup]", [IsAutomGroup],
function(G)
  local recur_list, fam, names, d, i, s, gens_letter_reps, M;
  fam := UnderlyingAutomFamily(G);
  names := fam!.names;
  d:=DegreeOfTree(fam);
  recur_list := StructuralCopy(fam!.automatonlist{[1..Length(names)]});

  # if identity is not one of the states
  if Length(fam!.automatonlist)=2*Length(names) then
    for s in recur_list do
      for i in [1..d] do s[i]:=[s[i]]; od;
    od;
  # if identity is one of the states, it should be the last one, so
  # when converting to the recurlist, we replace corresponding entries
  # with empty lists
  else
    for s in recur_list do
      for i in [1..d] do
        if s[i]<=Length(names) then
          s[i]:=[s[i]];
        else
          s[i]:=[];
        fi;
      od;
    od;
  fi;

  gens_letter_reps:=List(GeneratorsOfGroup(G),w->LetterRepAssocWord(w!.word));

  M:=FRMachine(fam!.names, List(recur_list,x->x{[1..fam!.deg]}),
                                  List(recur_list,x->x[fam!.deg+1]));

  return Group(List(gens_letter_reps,w->FRElement(M,w)));
end);

#############################################################################
##
#M  AutomGrp2FR (<G>)
##
InstallMethod(AutomGrp2FR, "for [IsAutomatonSemigroup]", [IsAutomatonSemigroup],
function(G)
  local recur_list, fam, names, d, i, s;
  fam := UnderlyingAutomFamily(G);
  names := fam!.names;
  d:=DegreeOfTree(fam);
  recur_list := StructuralCopy(fam!.automatonlist{[1..Length(names)]});

  # if identity is not one of the states
  if Length(fam!.automatonlist)=2*Length(names) then
    for s in recur_list do
      for i in [1..d] do s[i]:=[s[i]]; od;
    od;
  # if identity is one of the states, it should be the last one, so
  # when converting to the recurlist, we replace corresponding entries
  # with empty lists
  else
    for s in recur_list do
      for i in [1..d] do
        if s[i]<=Length(names) then
          s[i]:=[s[i]];
        else
          s[i]:=[];
        fi;
      od;
    od;
  fi;
  return AutomGrp2FR(SelfSimilarSemigroup(recur_list{[1..Length(names)]},names));
end);

#############################################################################
##
#M  AutomGrp2FR (<G>)
##
InstallMethod(AutomGrp2FR, "for [IsAutomSemigroup]", [IsAutomSemigroup],
function(G)
  local recur_list, fam, names, d, i, s, gens_letter_reps, M;
  fam := UnderlyingAutomFamily(G);
  names := fam!.names;
  d:=DegreeOfTree(fam);
  recur_list := StructuralCopy(fam!.automatonlist{[1..Length(names)]});

  # if identity is not one of the states
  if Length(fam!.automatonlist)=2*Length(names) then
    for s in recur_list do
      for i in [1..d] do s[i]:=[s[i]]; od;
    od;
  # if identity is one of the states, it should be the last one, so
  # when converting to the recurlist, we replace corresponding entries
  # with empty lists
  else
    for s in recur_list do
      for i in [1..d] do
        if s[i]<=Length(names) then
          s[i]:=[s[i]];
        else
          s[i]:=[];
        fi;
      od;
    od;
  fi;

  gens_letter_reps:=List(GeneratorsOfSemigroup(G),w->LetterRepAssocWord(w!.word));

  M:=FRMachine(fam!.names, List(recur_list,x->x{[1..fam!.deg]}),
                                  List(recur_list,x->x[fam!.deg+1]));

  return Semigroup(List(gens_letter_reps,w->FRElement(M,w)));
end);

#############################################################################
##
#M  AutomGrp2FR (<G>)
##
InstallMethod(AutomGrp2FR, "for [IsAutom or IsSelfSim]", [IsAutom],
function(a)
  local fam,G;
  fam:=FamilyObj(a);
  if fam!.isgroup then
    G:=AutomGrp2FR(Group(a));
  else
    G:=AutomGrp2FR(Semigroup(a));
  fi;
  return G.1;
end);

#############################################################################
##
#M  AutomGrp2FR (<G>)
##
InstallMethod(AutomGrp2FR, "for [IsAutom or IsSelfSim]", [IsSelfSim],
function(a)
  local fam,G;
  fam:=FamilyObj(a);
  if fam!.isgroup then
    G:=AutomGrp2FR(Group(a));
  else
    G:=AutomGrp2FR(Semigroup(a));
  fi;
  return G.1;
end);

#############################################################################
##
#M  FR2AutomGrp (<G>)
##
InstallMethod(FR2AutomGrp, "for [IsFRGroup and HasUnderlyingFRMachine]", [IsFRGroup and HasUnderlyingFRMachine],
function(G)
  local M, names, recurlist, i;
  M := UnderlyingFRMachine(G);
  names := List(GeneratorsOfGroup(M!.free),x->String(x));
  recurlist := List(M!.transitions,x->List(x,w->LetterRepAssocWord(w)));
  for i in [1..Length(recurlist)] do
    Add(recurlist[i],PermList(M!.output[i]));
  od;
  return SelfSimilarGroup(recurlist,names,false);
end);

#############################################################################
##
#M  FR2AutomGrp (<G>)
##
InstallMethod(FR2AutomGrp, "for [IsFRGroup]", [IsFRGroup],
function(G)
  local UG;
  UG := FR2AutomGrp(SCGroup(UnderlyingFRMachine(Representative(G))));
  return Group(List(GeneratorsOfGroup(G),x->SelfSim(AssocWordByLetterRep(FamilyObj(One(UnderlyingFreeGroup(UG))),
                                                    LetterRepAssocWord(InitialState(x))), UnderlyingSelfSimFamily(UG))));
end);

#############################################################################
##
#M  FR2AutomGrp (<G>)
##
InstallMethod(FR2AutomGrp, "for [IsFRSemigroup and HasUnderlyingFRMachine]", [IsFRSemigroup and HasUnderlyingFRMachine],
function(G)
  local M, names, recurlist, i;
  M := UnderlyingFRMachine(G);
  names := List(GeneratorsOfSemigroup(M!.free),x->String(x));
  recurlist := List(M!.transitions,x->List(x,w->LetterRepAssocWord(w)));
  for i in [1..Length(recurlist)] do
    Add(recurlist[i],Transformation(M!.output[i]));
  od;
  return SelfSimilarSemigroup(recurlist,names,false);
end);

#############################################################################
##
#M  FR2AutomGrp (<G>)
InstallMethod(FR2AutomGrp, "for [IsFRSemigroup]", [IsFRSemigroup],
function(G)
  local UG;
  UG := FR2AutomGrp(SCSemigroup(UnderlyingFRMachine(Representative(G))));
  return Semigroup(List(GeneratorsOfSemigroup(G),x->SelfSim(AssocWordByLetterRep(FamilyObj(One(UnderlyingFreeGroup(UG))),
                                                           LetterRepAssocWord(InitialState(x))), UnderlyingSelfSimFamily(UG))));
end);

#############################################################################
##
#M  FR2AutomGrp (<G>)
##
InstallMethod(FR2AutomGrp, "for [IsFRMonoid and HasUnderlyingFRMachine]", [IsFRMonoid and HasUnderlyingFRMachine],
function(G)
  local M, names, recurlist, i;
  M := UnderlyingFRMachine(G);
  names := List(GeneratorsOfMonoid(M!.free),x->String(x));
  recurlist := List(M!.transitions,x->List(x,w->LetterRepAssocWord(w)));
  for i in [1..Length(recurlist)] do
    Add(recurlist[i],Transformation(M!.output[i]));
  od;
  return Monoid(GeneratorsOfSemigroup(SelfSimilarSemigroup(recurlist,names,false)));
end);

#############################################################################
##
#M  FR2AutomGrp (<G>)
InstallMethod(FR2AutomGrp, "for [IsFRMonoid]", [IsFRMonoid],
function(G)
  local UG;
  UG := FR2AutomGrp(SCSemigroup(UnderlyingFRMachine(Representative(G))));
  return Monoid(List(GeneratorsOfMonoid(G),x->SelfSim(AssocWordByLetterRep(FamilyObj(One(UnderlyingFreeGroup(UG))),
                                                           LetterRepAssocWord(InitialState(x))), UnderlyingSelfSimFamily(UG))));
end);

#############################################################################
##
#M  FR2AutomGrp (<G>)
##
InstallMethod(FR2AutomGrp, "for [IsFRElement]", [IsFRElement],
function(a)
  local G, M;
  M:=UnderlyingFRMachine(a);
  # Group case
  if ForAll(M!.output,l->Set(l)=[1..Length(l)]) then
    G:=FR2AutomGrp(SCGroup(M));
  # Semigroup case
  else
    G:=FR2AutomGrp(SCSemigroup(M));
  fi;
  return SelfSim(AssocWordByLetterRep(FamilyObj(One(UnderlyingFreeGroup(G))),
                 LetterRepAssocWord(InitialState(a))), UnderlyingSelfSimFamily(G));
end);

#E

ext: gi

max_stars_repo_name: gap-packages/automgrp

max_issues_repo_name: gap-packages/automgrp

max_forks_repo_name: gap-packages/automgrp


text:
#############################################################################
##
#W    pcpgrp3.gi                                                Karel Dekimpe
#W                                                               Bettina Eick
##
##    This file contains the 3-dimensional almost crystallographic groups
##    as pcp groups. There are 17 types groups.
##

ACPcpGroupDim3Nr01 := function (k1)
local FTL;
  FTL := FromTheLeftCollector( 3 );
  SetConjugate( FTL, 2, 1, [2,1,  3,k1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr02 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 4 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [2,0,  3,0,  4,k4] );
  SetConjugate( FTL, 2, 1, [2,-1,  3,0,  4,k2] );
  SetConjugate( FTL, 3, 1, [2,0,  3,-1,  4,k3] );
  SetConjugate( FTL, 4, 1, [2,0,  3,0,  4,1] );
  SetConjugate( FTL, 3, 2, [3,1,  4,k1] );
  SetConjugate( FTL, 4, 2, [3,0,  4,1] );
  SetConjugate( FTL, 4, 3, [4,1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr03 := function (k1, k2)
local FTL;
  FTL := FromTheLeftCollector( 4 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [2,0,  3,0,  4,0] );
  SetConjugate( FTL, 2, 1, [2,1,  3,0,  4,-k2] );
  SetConjugate( FTL, 3, 1, [2,0,  3,-1,  4,0] );
  SetConjugate( FTL, 4, 1, [2,0,  3,0,  4,-1] );
  SetConjugate( FTL, 3, 2, [3,1,  4,k1] );
  SetConjugate( FTL, 4, 2, [3,0,  4,1] );
  SetConjugate( FTL, 4, 3, [4,1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr04 := function (k1, k2)
local FTL;
  FTL := FromTheLeftCollector( 4 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [2,1,  3,0,  4,k2] );
  SetConjugate( FTL, 2, 1, [2,1,  3,0,  4,2*k2] );
  SetConjugate( FTL, 3, 1, [2,0,  3,-1,  4,-k1] );
  SetConjugate( FTL, 4, 1, [2,0,  3,0,  4,-1] );
  SetConjugate( FTL, 3, 2, [3,1,  4,2*k1] );
  SetConjugate( FTL, 4, 2, [3,0,  4,1] );
  SetConjugate( FTL, 4, 3, [4,1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr05 := function (k1, k2)
local FTL;
  FTL := FromTheLeftCollector( 4 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [2,0,  3,0,  4,0] );
  SetConjugate( FTL, 2, 1, [2,0,  3,1,  4,-k2] );
  SetConjugate( FTL, 3, 1, [2,1,  3,0,  4,-k2] );
  SetConjugate( FTL, 4, 1, [2,0,  3,0,  4,-1] );
  SetConjugate( FTL, 3, 2, [3,1,  4,k1] );
  SetConjugate( FTL, 4, 2, [3,0,  4,1] );
  SetConjugate( FTL, 4, 3, [4,1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr06 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 5 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [3,0,  4,0,  5,0] );
  SetConjugate( FTL, 2, 1, [2, 1 ,  3,0,  4,0,  5,-k4] );
  SetConjugate( FTL, 3, 1, [3,1,  4,0,  5,-k2] );
  SetConjugate( FTL, 4, 1, [3,0,  4,-1,  5,0] );
  SetConjugate( FTL, 5, 1, [3,0,  4,0,  5,-1] );
  SetRelativeOrder( FTL, 2, 2 );
  SetPower( FTL, 2, [3,0,  4,0,  5,k4] );
  SetConjugate( FTL, 3, 2, [3,-1,  4,0,  5,k2] );
  SetConjugate( FTL, 4, 2, [3,0,  4,-1,  5,k3] );
  SetConjugate( FTL, 5, 2, [3,0,  4,0,  5,1] );
  SetConjugate( FTL, 4, 3, [4,1,  5,k1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr07 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 5 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [3,0,  4,0,  5,0] );
  SetConjugate( FTL, 2, 1, [2, 1 ,  3,0,  4,-1,  5,-2*k3 - k4] );
  SetConjugate( FTL, 3, 1, [3,1,  4,0,  5,k1 - k2] );
  SetConjugate( FTL, 4, 1, [3,0,  4,-1,  5,0] );
  SetConjugate( FTL, 5, 1, [3,0,  4,0,  5,-1] );
  SetRelativeOrder( FTL, 2, 2 );
  SetPower( FTL, 2, [3,0,  4,0,  5,k3] );
  SetConjugate( FTL, 3, 2, [3,-1,  4,0,  5,k2] );
  SetConjugate( FTL, 4, 2, [3,0,  4,-1,  5,-2*(k3 + k4)] );
  SetConjugate( FTL, 5, 2, [3,0,  4,0,  5,1] );
  SetConjugate( FTL, 4, 3, [4,1,  5,2*k1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr08 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 5 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [3,1,  4,0,  5,-k3] );
  SetConjugate( FTL, 2, 1, [2, 1 ,  3,1,  4,-1,  5,-2*k1 + k2 - 2*k3 - k4] );
  SetConjugate( FTL, 3, 1, [3,1,  4,0,  5,-2*k3] );
  SetConjugate( FTL, 4, 1, [3,0,  4,-1,  5,-k1] );
  SetConjugate( FTL, 5, 1, [3,0,  4,0,  5,-1] );
  SetRelativeOrder( FTL, 2, 2 );
  SetPower( FTL, 2, [3,0,  4,0,  5,k4] );
  SetConjugate( FTL, 3, 2, [3,-1,  4,0,  5,k1 + 2*k3] );
  SetConjugate( FTL, 4, 2, [3,0,  4,-1,  5,-k1 + 2*k2 - 2*k3] );
  SetConjugate( FTL, 5, 2, [3,0,  4,0,  5,1] );
  SetConjugate( FTL, 4, 3, [4,1,  5,2*k1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr09 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 5 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [3,0,  4,0,  5,0] );
  SetConjugate( FTL, 2, 1, [2, 1 ,  3,0,  4,0,  5,-k4] );
  SetConjugate( FTL, 3, 1, [3,0,  4,1,  5,-k3] );
  SetConjugate( FTL, 4, 1, [3,1,  4,0,  5,-k3] );
  SetConjugate( FTL, 5, 1, [3,0,  4,0,  5,-1] );
  SetRelativeOrder( FTL, 2, 2 );
  SetPower( FTL, 2, [3,0,  4,0,  5,k4] );
  SetConjugate( FTL, 3, 2, [3,-1,  4,0,  5,k2] );
  SetConjugate( FTL, 4, 2, [3,0,  4,-1,  5,-k2 + 2*k3] );
  SetConjugate( FTL, 5, 2, [3,0,  4,0,  5,1] );
  SetConjugate( FTL, 4, 3, [4,1,  5,k1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr10 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 4 );
  SetRelativeOrder( FTL, 1, 4 );
  SetPower( FTL, 1, [2,0,  3,0,  4,k4] );
  SetConjugate( FTL, 2, 1, [2,0,  3,-1,  4,k3] );
  SetConjugate( FTL, 3, 1, [2,1,  3,0,  4,-k2] );
  SetConjugate( FTL, 4, 1, [2,0,  3,0,  4,1] );
  SetConjugate( FTL, 3, 2, [3,1,  4,k1] );
  SetConjugate( FTL, 4, 2, [3,0,  4,1] );
  SetConjugate( FTL, 4, 3, [4,1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr11 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 5 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [3,0,  4,0,  5,0] );
  SetConjugate( FTL, 2, 1, [2, 3 ,  3,0,  4,0,  5,-k4] );
  SetConjugate( FTL, 3, 1, [3,1,  4,0,  5,-k2 - k3] );
  SetConjugate( FTL, 4, 1, [3,0,  4,-1,  5,0] );
  SetConjugate( FTL, 5, 1, [3,0,  4,0,  5,-1] );
  SetRelativeOrder( FTL, 2, 4 );
  SetPower( FTL, 2, [3,0,  4,0,  5,k4] );
  SetConjugate( FTL, 3, 2, [3,0,  4,-1,  5,k3] );
  SetConjugate( FTL, 4, 2, [3,1,  4,0,  5,-k2] );
  SetConjugate( FTL, 5, 2, [3,0,  4,0,  5,1] );
  SetConjugate( FTL, 4, 3, [4,1,  5,k1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr12 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 5 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [3,1,  4,0,  5,k3] );
  SetConjugate( FTL, 2, 1, [2, 3 ,  3,0,  4,-1,  5,-k2 - k3 - k4] );
  SetConjugate( FTL, 3, 1, [3,1,  4,0,  5,2*k3] );
  SetConjugate( FTL, 4, 1, [3,0,  4,-1,  5,-k1] );
  SetConjugate( FTL, 5, 1, [3,0,  4,0,  5,-1] );
  SetRelativeOrder( FTL, 2, 4 );
  SetPower( FTL, 2, [3,0,  4,0,  5,k4] );
  SetConjugate( FTL, 3, 2, [3,0,  4,-1,  5,k1 - k2 - 2*k3] );
  SetConjugate( FTL, 4, 2, [3,1,  4,0,  5,-k2] );
  SetConjugate( FTL, 5, 2, [3,0,  4,0,  5,1] );
  SetConjugate( FTL, 4, 3, [4,1,  5,2*k1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr13 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 4 );
  SetRelativeOrder( FTL, 1, 3 );
  SetPower( FTL, 1, [2,0,  3,0,  4,k4] );
  SetConjugate( FTL, 2, 1, [2,-1,  3,-1,  4,k2 + k3] );
  SetConjugate( FTL, 3, 1, [2,1,  3,0,  4,-k2] );
  SetConjugate( FTL, 4, 1, [2,0,  3,0,  4,1] );
  SetConjugate( FTL, 3, 2, [3,1,  4,k1] );
  SetConjugate( FTL, 4, 2, [3,0,  4,1] );
  SetConjugate( FTL, 4, 3, [4,1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr14 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 5 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [3,0,  4,0,  5,0] );
  SetConjugate( FTL, 2, 1, [2, 2 ,  3,0,  4,0,  5,-k4] );
  SetConjugate( FTL, 3, 1, [3,0,  4,-1,  5,-k2] );
  SetConjugate( FTL, 4, 1, [3,-1,  4,0,  5,k2] );
  SetConjugate( FTL, 5, 1, [3,0,  4,0,  5,-1] );
  SetRelativeOrder( FTL, 2, 3 );
  SetPower( FTL, 2, [3,0,  4,0,  5,k4] );
  SetConjugate( FTL, 3, 2, [3,-1,  4,-1,  5,k2 + k3] );
  SetConjugate( FTL, 4, 2, [3,1,  4,0,  5,-k2] );
  SetConjugate( FTL, 5, 2, [3,0,  4,0,  5,1] );
  SetConjugate( FTL, 4, 3, [4,1,  5,k1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr15 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 5 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [3,0,  4,0,  5,0] );
  SetConjugate( FTL, 2, 1, [2, 2 ,  3,0,  4,0,  5,-k4] );
  SetConjugate( FTL, 3, 1, [3,0,  4,1,  5,-k2] );
  SetConjugate( FTL, 4, 1, [3,1,  4,0,  5,-k2] );
  SetConjugate( FTL, 5, 1, [3,0,  4,0,  5,-1] );
  SetRelativeOrder( FTL, 2, 3 );
  SetPower( FTL, 2, [3,0,  4,0,  5,k4] );
  SetConjugate( FTL, 3, 2, [3,-1,  4,-1,  5,k1 + 3*k2 - k3] );
  SetConjugate( FTL, 4, 2, [3,1,  4,0,  5,-k1 - 3*k2 + 2*k3] );
  SetConjugate( FTL, 5, 2, [3,0,  4,0,  5,1] );
  SetConjugate( FTL, 4, 3, [4,1,  5,k1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr16 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 4 );
  SetRelativeOrder( FTL, 1, 6 );
  SetPower( FTL, 1, [2,0,  3,0,  4,k4] );
  SetConjugate( FTL, 2, 1, [2,0,  3,-1,  4,k3] );
  SetConjugate( FTL, 3, 1, [2,1,  3,1,  4,k1 - k2 - k3] );
  SetConjugate( FTL, 4, 1, [2,0,  3,0,  4,1] );
  SetConjugate( FTL, 3, 2, [3,1,  4,k1] );
  SetConjugate( FTL, 4, 2, [3,0,  4,1] );
  SetConjugate( FTL, 4, 3, [4,1] );
return PcpGroupByCollector(FTL);
end;

ACPcpGroupDim3Nr17 := function (k1, k2, k3 , k4)
local FTL;
  FTL := FromTheLeftCollector( 5 );
  SetRelativeOrder( FTL, 1, 2 );
  SetPower( FTL, 1, [3,0,  4,0,  5,0] );
  SetConjugate( FTL, 2, 1, [2, 5 ,  3,0,  4,0,  5,-k4] );
  SetConjugate( FTL, 3, 1, [3,0,  4,1,  5,-k3] );
  SetConjugate( FTL, 4, 1, [3,1,  4,0,  5,-k3] );
  SetConjugate( FTL, 5, 1, [3,0,  4,0,  5,-1] );
  SetRelativeOrder( FTL, 2, 6 );
  SetPower( FTL, 2, [3,0,  4,0,  5,k4] );
  SetConjugate( FTL, 3, 2, [3,0,  4,-1,  5,k3] );
  SetConjugate( FTL, 4, 2, [3,1,  4,1,  5,k1 - k2 - k3] );
  SetConjugate( FTL, 5, 2, [3,0,  4,0,  5,1] );
  SetConjugate( FTL, 4, 3, [4,1,  5,k1] );
return PcpGroupByCollector(FTL);
end;

#############################################################################
##
## some small helpers
##
ACPcpDim3Funcs := [ ACPcpGroupDim3Nr01, ACPcpGroupDim3Nr02,
ACPcpGroupDim3Nr03,
ACPcpGroupDim3Nr04, ACPcpGroupDim3Nr05, ACPcpGroupDim3Nr06,
ACPcpGroupDim3Nr07, ACPcpGroupDim3Nr08, ACPcpGroupDim3Nr09,
ACPcpGroupDim3Nr10, ACPcpGroupDim3Nr11, ACPcpGroupDim3Nr12,
ACPcpGroupDim3Nr13, ACPcpGroupDim3Nr14, ACPcpGroupDim3Nr15,
ACPcpGroupDim3Nr16, ACPcpGroupDim3Nr17 ];
MakeReadOnlyGlobal( "ACPcpDim3Funcs" );

max_stars_repo_name: alex-konovalov/aclib

max_issues_repo_name: alex-konovalov/aclib

max_forks_repo_name: alex-konovalov/aclib


text:
# Copyright (c) 2018-2020, Carnegie Mellon University
# See LICENSE for details
#
# Wrapper object for qCirc that explores global reordering of qubits by
# permuting the input adjacency matrix
#

##
#F GenStages( <arch>, <circ>) Object
##
#F Given a circuit object and an architecture topology matrix,
#F for every reordering of the physical qubits
#F generate a top-level circuit with that topology
GenStages := function(arch, circ)
    local child, idcs, n, i, ar, c;
    n := Length(arch);
    idcs := [0..n-1];
    idcs := Arrangements(idcs, Length(idcs));
    child := [];
    for i in idcs do
        ar := PruneArch(arch, i);
        c := circ.recursive_def(ar);
        Add(child, [c, ar]);
    od;
    return child;
end;

##
#F BestCircuitReorder( <arch>, <t>, <opts>) Object
##
#F Finds the Best Circuit, but also explores all global reorderings of the
#F Adjacency matrix, skipping duplicate reorderings.
#F TODO: This is non-optimal because the hashtable is discarded across
#F DP calls, ideally we can feed the hashtable from the previous search into the next DP search
#F For research code that is bottlenecked by other reasons, this is acceptable for now
BestCircuitReorder := function(arch, t, opts)
    local gs, child, g, child_cnots, idx, min, c, same, a, arches_checked, i, j, row, bit, already;
    gs := GenStages(arch, t);
    child := [];
    arches_checked := [];
    for g in gs do
        Print("\n Compiling arch: \n");
        Print(g[2]);
        Print("\n\n");
        already := 0;
        for a in arches_checked do
            i := 1;
            same := 1;
            for row in a do
                j := 1;
                for bit in row do
                    if bit <> g[2][i][j] then
                        same := 0;
                    fi;
                    j := j + 1;
                od;
                i := i + 1;
            od;
            if same = 1 then
                already := 1;
            fi;
        od;
        if already = 1 then
            Print("\n already checked architecture! \n");
        fi;
        if already <> 1 then
            Add(child, BestCircuit(g[1], opts));
            Add(arches_checked, g[2]);
        fi;
    od;
    child_cnots := Map(child, i -> Length(Collect(i, @(1,CNOT))));
    min := Minimum(child_cnots);
    idx := 1;
    for c in child_cnots do
        if c = min then
            return child[idx];
        fi;
        idx := idx + 1;
    od;
    return null;
end;

max_stars_repo_name: spiral-software/spiral-package-quantum

max_issues_repo_name: spiral-software/spiral-package-quantum

max_forks_repo_name: spiral-software/spiral-package-quantum


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

# misc functions for hamlet stuff

# takes the permutation in SPL, returns the bit matrix
# extends some of the Peter's stuff, -BA
SPLtoBitMatrix := function(perm)
	local m,bit;

	m := MatSPL(perm);

	bit := BitMatrixToInts(PermMatrixToBits(m));

	return bit;

end;

SPLtoLocalMemConf := function(perm,w)
	local opts,path,t,filename,cmdString;

	opts := InitStreamUnrollHw();

	path := Concat("/tmp/spiral/", String(GetPid()), "/");
	filename := "perm";
    MakeDir(path);

	BRAMPerm.print := (self,i,is) >> Print(self.name, "Mem(", BitMatrixToInts(self._children[1]), ", ", BitMatrixToInts(self._children[2]), ", ", self.streamSize, ")");

	t := streamGen(perm.withTags([AStream(w)]),opts);

	PrintTo(ConcatenationString(path, filename, ".spl"), HDLPrint(1024, t.dims(), -1, t));

    if (_splhdl_path = "") then
		Error("Error: path to SPLHDL compiler not set: paradigms.stream._splhdl_path is not bound.");
    fi;

    cmdString := ConcatenationString(_splhdl_path, " ", path, filename, ".spl");

	Exec(cmdString);

	BRAMPerm.print := (self,i,is) >> Print(self.name, "(", BitMatrixToInts(self._children[1]), ", ", BitMatrixToInts(self._children[2]), ", ", self.streamSize, ")");

	return path;
end;


text:
method vfBtnList vfBtnList.mLoad <alias=vfBtnList_mLoad>( )
{
//	this->vForm.mCreateWin()
	ustr empus
	str  emps
	ustr ustmp
	uint comp
	comp as this
		comp.AutoLang=1
		comp.Border=$fbrdNone
		comp.Bottom=0
		comp.Caption=empus
		comp.Enabled=1
		comp.FormStyle=$fsChild
		comp.Height=226
		comp.HelpTopic=empus
		comp.Hint=empus
		comp.HorzAlign=$alhClient
		comp.IconName=empus
		comp.Left=0
		comp.Name="fBtnList"
		comp.Right=0
		comp.StartPos=$spDesigned
		comp.Style=emps
		comp.TabOrder=0
		comp.Tag=0
		comp.Top=0
		comp.TopMost=1
		comp.VertAlign=$alvClient
		comp.Visible=1
		comp.Width=378
		comp.WindowState=$wsNormal
		comp.OnCreate.Set( this, fBtnList_Create )
		comp as this.sbMain
		comp.Owner = this
			comp.AutoLang=1
			comp.AutoScroll=1
			comp.Border=$brdNone
			comp.Bottom=27
			comp.Enabled=1
			comp.Height=181
			comp.HelpTopic=empus
			comp.Hint=empus
			comp.HorzAlign=$alhClient
			comp.HorzRange=46
			comp.Left=0
			comp.Name="sbMain"
			comp.Right=0
			comp.Style=emps
			comp.TabOrder=0
			comp.Tag=0
			comp.Top=0
			comp.VertAlign=$alvClient
			comp.VertRange=52
			comp.Visible=1
			comp.Width=360
			comp.OnMouse.Set( this, fBtnList_Mouse )
			comp as this.tbItems
			comp.Owner = this.sbMain
				comp.AutoLang=0
				comp.AutoSize=0
				comp.Bottom=0
				comp.Enabled=1
				comp.Height=26
				comp.HelpTopic=empus
				comp.Hint=empus
				comp.HorzAlign=$alhLeftRight
				comp.ImageList=ustmp.fromutf8("project")
				comp.Left=46
				comp.Name="tbItems"
				comp.Right=-2
				comp.ShowCaption=$tscRight
				comp.ShowDivider=0
				comp.Style=emps
				comp.TabOrder=0
				comp.Tag=0
				comp.Top=0
				comp.VertAlign=$alvTop
				comp.Vertical=1
				comp.Visible=1
				comp.Width=316
				comp.Wrapable=1
				comp.OnMouse.Set( this, fBtnList_ItemMouse )
			comp as this.tbPaste
			comp.Owner = this.sbMain
				comp.AutoLang=1
				comp.AutoSize=0
				comp.Bottom=0
				comp.Enabled=1
				comp.Height=25
				comp.HelpTopic=empus
				comp.Hint=empus
				comp.HorzAlign=$alhLeft
				comp.ImageList=ustmp.fromutf8("project")
				comp.Left=23
				comp.Name="tbPaste"
				comp.Right=0
				comp.ShowCaption=$tscNone
				comp.ShowDivider=0
				comp.Style=emps
				comp.TabOrder=1
				comp.Tag=0
				comp.Top=0
				comp.VertAlign=$alvTop
				comp.Vertical=1
				comp.Visible=1
				comp.Width=23
				comp.Wrapable=1
			comp as this.tbCopy
			comp.Owner = this.sbMain
				comp.AutoLang=1
				comp.AutoSize=0
				comp.Bottom=0
				comp.Enabled=1
				comp.Height=52
				comp.HelpTopic=empus
				comp.Hint=empus
				comp.HorzAlign=$alhLeft
				comp.ImageList=ustmp.fromutf8("project")
				comp.Left=0
				comp.Name="tbCopy"
				comp.Right=0
				comp.ShowCaption=$tscNone
				comp.ShowDivider=0
				comp.Style=emps
				comp.TabOrder=2
				comp.Tag=0
				comp.Top=0
				comp.VertAlign=$alvTop
				comp.Vertical=1
				comp.Visible=1
				comp.Width=23
				comp.Wrapable=1
	comp as this
		comp.ClientHeight=181
		comp.ClientWidth=360

	return this
}

method vfBtnList vfBtnList.init( )
{
   this.pTypeId = vfBtnList
   return this
}
func init_vfBtnList <entry>()
{
   regcomp( vfBtnList, "vfBtnList", vForm, $vForm_last,
      %{ %{$mLoad,     vfBtnList_mLoad}},
      0->collection )

}

max_stars_repo_name: novostrim/macroclip

max_issues_repo_name: novostrim/macroclip

max_forks_repo_name: novostrim/macroclip


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

DCTDiag  := n -> Diag(diagDirsum(fConst(1,1/Sqrt(n)), fConst(n-1, Sqrt(2/n))));
DCTDiag2 := n -> Diag(diagDirsum(fConst(1,1/n), fConst(n-1, 2/n)));

#ForwardDCT := DCT2;

Class(InverseDCT, DTTBase, rec(
    terminate := self >> DCT3(self.params[1]).terminate() * DCTDiag2(self.params[1]),
#    transpose := self >> DCTDiag2(self.params[1]) * DCT2(self.params[1]),
));

RulesFor(InverseDCT, rec(
     InverseDCT_toDCT3 := rec(
	 isApplicable     := P -> true,
	 allChildren      := P -> [[ DCT3(P[1]) ]],
	 rule := (P, C) -> C[1] * DCTDiag2(P[1])
     )
));


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

_vlist := (l,t)->List(Flat(l), x->t.value(x));

Class(fNearestNeighbor, FuncClass, rec(
#    def := (N,n,base,stride) -> Checked(IntFloat(base + (n-1)*stride) < N, rec(N:=N, n:=n)),
    def := (N,n,base,stride) -> rec(N:=N, n:=n),
    domain := self >> self.params[2],
    range := self >> self.params[1],
    lambda := self >> let(h := var.fresh_t("h", TInt), Lambda(h, floor(self.params[3]+h*self.params[4]+0.5))),
    # NOTE. FF: that is a temporary solution to make this function work inside Lambda. need to talk to YSV how to do it right.
    t := TFunc
));

Class(fZeroPadMiddle, FuncClass, rec(
    def := (N,n) -> Checked(IsEvenInt(n), rec(N:=N, n:=n)),
    domain := self >> self.params[2],
    range := self >> self.params[1],
    lambda := self >> let(N:=self.params[1], n:=self.params[2], fStack(fAdd(N, n/2, 0), fAdd(N, n/2, N-n/2)))
));

Class(Downsample, TaggedNonTerminal, rec(
    abbrevs := [ (dsfunc) -> [dsfunc] ],
    dims      := self >> [self.params[1].domain(), self.params[1].range()],
    terminate := self >> Gath(self.params[1]),
    isReal    := True,
    normalizedArithCost := self >> 0,
    TType := T_Complex(T_Real(64)),
    doNotMeasure := true,
    HashId := self >> let(h := [ ObjId(self.params[1]), self.params[1].range() ],
        When(IsBound(self.tags), Concatenation(h, self.tags), h)),

));

Class(Upsample, TaggedNonTerminal, rec(
    abbrevs := [ (usfunc) -> [usfunc] ],
    dims      := self >> [self.params[1].range(), self.params[1].domain()],
    terminate := self >> Scat(self.params[1]),
    isReal    := True,
    normalizedArithCost := self >> 0,
    TType := T_Complex(T_Real(64))
));

NewRulesFor(Upsample, rec(
    Upsample_base := rec(
        applicable := (self, nt) >> not nt.hasTags(),
        apply := (nt, C, cnt) -> Mat(MatSPL(Scat(nt.params[1])))
    )
));

NewRulesFor(Downsample, rec(
    Downsample_base := rec(
        applicable := (self, nt) >> not nt.hasTags(),
        apply := (nt, C, cnt) -> NeedInterleavedComplex(ScatGath(fId(nt.params[1].domain()), nt.params[1]))
    )
));


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

FunctionEquality:=(a,b)-> (a=b) or (a.n=b.n and a.N=b.N and let(func1:=a.lambda(), func2:=b.lambda(),
	ForAll([1..a.N],e->func1.at(e).ev()=func2.at(e).ev())));

#######################################################
########  Redesigning the ruleset
#######################################################

Class(OLScatFuseRules, RuleSet);
RewriteRules(OLScatFuseRules, rec(
        Scat_ScatAcc := ARule(Compose,[@(1,[Scat, ScatAcc]),@(2,ScatAcc)],
            e->[ScatAcc(fCompose(@(1).val.func,@(2).val.func))]),

        ScatAcc_Scat := ARule(Compose,[@(1,ScatAcc),@(2,[Scat, ScatAcc])],
            e->[ScatAcc(fCompose(@(1).val.func,@(2).val.func))]),

        ICScatAcc_Scat := ARule(Compose,[@(1,ICScatAcc),@(2,[Scat, ICScatAcc])],
            e->[ICScatAcc(fCompose(@(1).val.func,@(2).val.func))]),

        Prm_ScatQuestionMark  := ARule(Compose, [ @(1, Prm), @(2, ScatQuestionMark) ],
            e -> [ ScatQuestionMark(fCompose(@(1).val.inverse, @(2).val.func)) ]),

        Prm_ICScatAcc := ARule(Compose, [ @(1, Prm), @(2, ICScatAcc) ],
            e -> [ ICScatAcc(fCompose(@(1).val.inverse, @(2).val.func)) ]),

        ScatQuestionMark_ScatQuestionMark :=
        ARule(Compose, [ @(1, ScatQuestionMark), @(2, ScatQuestionMark) ],
            e -> [ ScatQuestionMark(fCompose(@(1).val.func, @(2).val.func)) ]),

        Kill_NoPullId :=
        ARule(Compose,[[NoPull,[Prm,@,fId,fId]]],e->[]),

        Kill_ComposeIdL :=
        ARule(Compose,[@(1),[Prm,@,fId,fId]],e->[@(1).val]),

        Kill_ComposeIdR :=
        ARule(Compose,[[Prm,@,fId,fId],@(1).val],e->[@(1).val]),

#        Rule([@(1,Compose,e->Length(e.rChildren())>1),...,[Prm,@,fId,fId],...],e->Error("kill id")),

));

Class(OLVectorScatFuseRules, RuleSet);
RewriteRules(OLVectorScatFuseRules, rec(
        VScat_VConstruct :=
        ARule(Compose, [ @(1, VScat), @(2, [VScatAcc,VScatQuestionMark], e->@(1).val.v=e.v) ],
            e -> [ ObjId(@(2).val)(fCompose(@(1).val.func, @(2).val.func), @(1).val.v) ]),

        VScatQuestionMark_VConstruct :=
        ARule(Compose, [ @(1, VScatQuestionMark), @(2, [VScat,VScatAcc,VScatQuestionMark], e->@(1).val.v=e.v) ],
            e -> [ ObjId(@(1).val)(fCompose(@(1).val.func, @(2).val.func), @(1).val.v) ]),

        VScatAcc_VConstruct :=
        ARule(Compose, [ @(1, VScatAcc), @(2, [VScat,VScatAcc], e->@(1).val.v=e.v) ],
            e -> [ VScatAcc(fCompose(@(1).val.func, @(2).val.func), @(1).val.v) ]),

        ##The following rules are copies of the ones in the subvector system
        ScatQuestionMark_VConstruct := ARule(Compose, [ @(1, ScatQuestionMark), @(2, [VScat,VScatQuestionMark]) ],
            e -> [ VScat_svQuestionMark(@(1).val.func, getV(@(2).val), 1), @(2).val ]),

        ScatAcc_VConstruct := ARule(Compose, [ @(1, ScatAcc), @(2, [VScat,VScatAcc, VScatQuestionMark]) ],
            e -> [ VScat_svAcc(@(1).val.func, getV(@(2).val), 1), @(2).val ]),

    VScat_svQuestionMark__VScat := ARule(Compose, [@(1,[VScat_svQuestionMark,VScat_svAcc]), @(2,VScat)],
    e -> let(func := @(1).val.func,  vfunc := @(2).val.func,
             v    := getV(@(1).val), sv    := @(1).val.sv,
        [ ObjId(@(1).val)(fCompose(func, fTensor(vfunc, fId(v/sv))), v, sv) ])),

    GathScat_svQuestionMark_fTensor := Rule([@(1, [VGath_sv, VScat_svQuestionMark,VScat_svAcc]),
                            [@(2,fTensor), ..., [fId, @(3).cond(e -> Gcd(@(1).val.v/
@(1).val.sv, e) = @(1).val.v)]]],
     e -> let(v := @(1).val.v, sv := @(1).val.sv,
          n := @(3).val,   gcd := Gcd(v / sv, n),
          ObjId(@(1).val)(fTensor(DropLast(@(2).val.children(), 1), fId(n/gcd)), v, sv*gcd))),

    GathScat_sv_H := Rule([@(1, [VGath_sv, VScat_sv, VScat_svAcc]), [H, @(2),@(3).cond(e -> Gcd(@(1).val.v/
@(1).val.sv, e) = @(1).val.v),@(4),@(5)]],
            e->let(v := @(1).val.v, sv := @(1).val.sv,
          n := @(3).val,   gcd := Gcd(v / sv, n),
          mydiv:=When(IsSymbolic(@(4).val),idiv(@(4).val,gcd),@(4).val/gcd),
          ObjId(@(1).val)(H(@(2).val/gcd,n/gcd,mydiv,@(5).val), v, sv*gcd))),

        VScat_svQuestionMark__VScatQuestionMark := ARule(Compose, [@(1,[VScat_svQuestionMark,VScat_svAcc]), @(2,VScatQuestionMark)],
            e -> let(func := @(1).val.func,  vfunc := @(2).val.func,
                v    := getV(@(1).val), sv    := @(1).val.sv,
                [ ObjId(@(1).val)(fCompose(func, fTensor(vfunc, fId(v/sv))), v, sv) ])),

        VScat_svAcc__VScatAcc := ARule(Compose, [@(1,[VScat_svQuestionMark,VScat_svAcc]), @(2,VScatAcc)],
            e -> let(func := @(1).val.func,  vfunc := @(2).val.func,
                v    := getV(@(1).val), sv    := @(1).val.sv,
                [ VScat_svAcc(fCompose(func, fTensor(vfunc, fId(v/sv))), v, sv) ])),

        VScat_svQuestionMark_to_VScatQuestionMark :=
        Rule(@(1,VScat_svQuestionMark,e->getV(e)=e.sv), e->VScatQuestionMark(@(1).val.func, @(1).val.v)),

        VScat_svAcc_to_VScatAcc :=
        Rule(@(1,VScat_svAcc,e->getV(e)=e.sv), e->VScatAcc(@(1).val.func, @(1).val.v)),

));

Class(OLVectorPropagateRules, RuleSet);
RewriteRules(OLVectorPropagateRules, rec(
        VTensorOL_ScatAcc :=
        Rule([@(1,VTensor_OL),[@(2,Compose),@(3,ScatAcc),...]],
            e->VScatAcc(@(3).val.func,@(1).val.vlen)*
            VTensor_OL(Compose(Drop(@(2).val._children,1)),@(1).val.vlen)),

        VTensorOL_ScatQuestionMark :=
        Rule([@(1,VTensor_OL),[@(2,Compose),@(3,ScatQuestionMark),...]],
            e->VScatQuestionMark(@(3).val.func,@(1).val.vlen)*
            VTensor_OL(Compose(Drop(@(2).val._children,1)),@(1).val.vlen)),

        VTensorOL_Cross :=
        Rule([@(1,VTensor_OL),[@(2,Compose),...,@(3,Cross)]],
            e->VTensor_OL(Compose(DropLast(@2.val._children,1)),@(1).val.vlen)*
            Cross(List(@(3).val._children,t->VTensor(t,@(1).val.vlen)))),

        VTensorOL_ISum := Rule([@(1, VTensor_OL), @(2, [ISum])],
            e -> let(s := @(2).val, CopyFields(s,
                    rec(_children := List(s.children(), c->VTensor_OL(c, @(1).val.vlen)))))),

        VTensor_I :=
        Rule([@(1,VTensor),@(2,I)],
            e->VTensor(Prm(fId(@(2).val.dimensions[1])),@(1).val.vlen)),

));

OLVectorPropagateRuleset := MergedRuleSet(OLVectorPropagateRules, StandardSumsRules, OLScatFuseRules,
    OLVectorScatFuseRules, RulesVec, RulesPropagate);

Class(OLPushScatQuestionMarkInRules, RuleSet);
RewriteRules(OLPushScatQuestionMarkInRules, rec(
        ScatQuestionMark_NoPullRight := ARule(Compose, [@(1, [ScatQuestionMark,VScatQuestionMark]),@(2,NoPullRight)],
            e -> [ NoPullRight(@(1).val * @(2).val.rChildren()[1])]),

        ScatQuestionMark_Isum := ARule(Compose, [@(1, [ScatQuestionMark,VScatQuestionMark]), @(2,ISum)],
            e-> [ ISum(@(2).val.var,@(2).val.domain,Compose(@(1).val,@(2).val._children))]),
));

OLPushScatQuestionMarkInRuleset := MergedRuleSet(OLPushScatQuestionMarkInRules, StandardSumsRules, OLScatFuseRules,
    OLVectorScatFuseRules, RulesVec, RulesPropagate);

Class(OLAlreadyInitializedScatQuestionMarkRules, RuleSet);
RewriteRules(OLAlreadyInitializedScatQuestionMarkRules, rec(
        AlreadyInitializedScatQuestionMark := Rule(@(1, [ScatQuestionMark]),
            e-> ScatAcc(@(1).val.func)),

        AlreadyInitializedVScatQuestionMark := Rule(@(1, [VScatQuestionMark]),
            e-> VScatAcc(@(1).val.func,@(1).val.v)),
));

Class(OLScatQuestionMarkToScat, RuleSet);
RewriteRules(OLScatQuestionMarkToScat, rec(
        ScatQuestionMarkToScat := Rule(@(1, [ScatQuestionMark]),
            e-> Scat(@(1).val.func)),

        VScatQuestionMarkToScat := Rule(@(1, [VScatQuestionMark]),
            e-> VScat(@(1).val.func,@(1).val.v)),
));

Class(OLQuickAndDirtyHackForCodelet, RuleSet);
RewriteRules(OLQuickAndDirtyHackForCodelet, rec(
        ScatAcc_NoPullRight := ARule(Compose, [@(1, [ScatAcc,VScatAcc]),@(2,NoPullRight)],
            e -> [ NoPullRight(@(1).val * @(2).val.rChildren()[1])]),

        ScatAcc_Isum := ARule(Compose, [@(1, [ScatAcc, VScatAcc]), @(2,ISum)
],
            e-> [ ISum(@(2).val.var,@(2).val.domain,Compose(@(1).val,@(2).val._children))]),
));

OLAlreadyInitializedScatQuestionMarkRuleset := MergedRuleSet(OLAlreadyInitializedScatQuestionMarkRules,
    StandardSumsRules, OLScatFuseRules, OLVectorScatFuseRules, RulesVec, RulesPropagate);

Class(OLSplitScatQuestionMarkRules, RuleSet);
RewriteRules(OLSplitScatQuestionMarkRules, rec(

        SplitScatQuestionMark := Rule(@(1, [ScatQuestionMark]),
            e-> ScatInit(@(1).val.func , [],ScatAcc(@(1).val.func))),

        PullScatInitOutOfCompose := Rule([@(1,Compose),@(2,ScatInit),...],
            e-> ScatInit(@(2).val.func,@(2).val.cond,
                Compose(@(2).val._children,Drop(@(1).val._children,1)))),

        PullScatInitOutOfISum := Rule([@(1,ISum),@(2,ScatInit)],
            e->When(@(1).val.var in @(2).val.func.free(),
                ScatInitProbe(@(2).val.func,@(2).val.cond,
                    ISum(@(1).val.var,@(1).val.domain,
                        ScatInitFixed(@(2).val.func,@(2).val.cond,@(2).val._children))),
                ScatInit(@(2).val.func,@(2).val.cond,
                    ISum(@(1).val.var,@(1).val.domain,@(2).val._children)))),

        PullScatInitProbeOutOfCompose := Rule([@(1,Compose),@(2,ScatInitProbe),...],
            e-> ScatInitProbe(@(2).val.func,@(2).val.cond,
                Compose(@(2).val._children,Drop(@(1).val._children,1)))),

        PullScatInitProbeOutOfISum := Rule([@(1,ISum),@(2,ScatInitProbe)],
            e->When(@(1).val.var in @(2).val.func.free(),
                ScatInitProbe(@(2).val.func,@(2).val.cond,
                    ISum(@(1).val.var,@(1).val.domain,@(2).val._children)),
                ScatInitProbe(@(2).val.func,Concatenation([@(1).val.var],@(2).val.cond),
                    ISum(@(1).val.var,@(1).val.domain,@(2).val._children)))),

));
OLSplitScatQuestionMarkRuleset := MergedRuleSet(OLSplitScatQuestionMarkRules, StandardSumsRules, OLScatFuseRules,
    OLVectorScatFuseRules, RulesVec, RulesPropagate);

Class(OLScatProbeMergeRules, RuleSet);
RewriteRules(OLScatProbeMergeRules, rec(

        PushScatInitProbeInsideSum := Rule([@(1,ScatInitProbe),@(2,ISum),@],
            e->ISum(@(2).val.var,@(2).val.domain,
                ScatInitProbe(@(1).val.func,@(1).val.cond,Compose(@(2).val._children)))),

        PushScatInitProbeInsideCompose := Rule([@(1,ScatInitProbe),@(2,Compose),@],
            e-> Compose([ScatInitProbe(@(1).val.func,@(1).val.cond,@(2).val._children[1]),
                    Drop(@(2).val._children,1)])),

        ScatInit_Cross := ARule(Compose, [@(1,[ScatInit]), @(2, [Cross])],
            e-> [ ScatInit(@(1).val.func,@(1).val.cond,Compose(@(1).val._children,@(2).val))]),

        ScatInitProbe_ScatInitFixed := Rule([@(1,ScatInitProbe),@(2,ScatInitFixed),@],
            e->When(@(1).val.func=@(2).val.func,
                ScatInit(@(1).val.func,@(1).val.cond,@(2).val._children),
                Error("ScatInitProbe doesn t remerge with ScatInitFixed, how did that happen?"))),

        PushScatInitInsideCompose := Rule([@(1,ScatInit),@(2,Compose),@],
            e-> Compose([ScatInit(@(1).val.func,@(1).val.cond,@(2).val._children[1]),
                    Drop(@(2).val._children,1)])),

        ScatInit_ScatAcc :=
        Rule([@(1,ScatInit,e->ObjId(e._children)=ScatAcc and e.func=e._children.func and e.cond=[]),@(2,ScatAcc),@],
            e-> Scat(@(1).val.func)),
));

OLScatProbeMergeRuleset := MergedRuleSet(OLScatProbeMergeRules, StandardSumsRules, OLScatFuseRules,
    OLVectorScatFuseRules, RulesVec, RulesPropagate);

Class(OLScatAccPeelRules, RuleSet);
RewriteRules(OLScatAccPeelRules, rec(
    ScatInitPeel := Rule(
        [@(1,ScatInit, e->e.cond=[]),
            [@(2,ISum), [@(3,Compose),
                    @(4,ScatAcc,e->FunctionEquality(e.func,@(1).val.func)), ... ]],@],

	e-> let(v := @(2).val.var, newv := Ind(v.range-1),
#x            Buf(Scat(fId(@(1).val.dims()[1]))) *
            SUM(SubstVars(
                    Scat(@(4).val.func) * Compose(Drop(Copy(@(3).val.children()),1)),
                    rec((v.id) := V(0))),
                Cond(@(2).val.domain=2,
                    SubstVars(@(2).val.child(1),rec((v.id) := V(1))),
                    SubstVars(
                        ISum(newv, @(2).val.domain-1, @(2).val.child(1)),
                        rec((v.id) := newv+1)))
            )))
));

OLScatAccPeelRuleset := MergedRuleSet(OLScatAccPeelRules,StandardSumsRules, OLScatFuseRules,
    OLVectorScatFuseRules, RulesVec, RulesPropagate);

Class(OLCrossPullInRules, RuleSet);
RewriteRules(OLCrossPullInRules, rec(
        PullInSMPSumRight := ARule( Compose, [ @(1, [Prm, Scat]), @(2, [SMPSum]) ],
            e -> [ CopyFields(@(2).val, rec(
             _children :=  List(@(2).val._children, c -> @(1).val * c),
             dimensions := [Rows(@(1).val), Cols(@(2).val)] )) ]),

        SMPSum_Cross  := ARule(Compose, [ @(1, [SMPSum]), @(2, [Cross]) ],
            e -> [ ObjId(@(1).val)(@(1).val.p,@(1).val.var, @(1).val.domain, @(1).val.child(1) * @(2).val) ]),

        ISum_Cross  := ARule(Compose, [ @(1, [ISum, ISumAcc]), @(2, [Cross]) ],
            e -> [ ObjId(@(1).val)(@(1).val.var, @(1).val.domain, @(1).val.child(1) * @(2).val) ]),

        SUM_Cross  := ARule(Compose, [ @(1, [SUM]), @(2, [Cross]) ],
            e -> [ ObjId(@(1).val)(List(@(1).val._children,x->x*@(2).val)) ]),

        ScatAcc_RecursStep := ARule(Compose, [ @(1, [ScatAcc]), @(2, [RecursStep]) ],
            e -> [ RecursStep(@2.val.yofs, @2.val.xofs,
                     @(1).val* @2.val.child(1)).attrs(@(1).val) ]),

        RecursStep_Cross := ARule(Compose, [ @(1, [RecursStep]), @(2, [Cross]) ],
            e -> [ RecursStep(@1.val.yofs, @1.val.xofs,
                    @1.val.child(1) * @(2).val).attrs(@(1).val) ]),

        Cross_Cross := ARule(Compose, [ @(1, [Cross, CrossBlockTop]), @(2, [Cross]) ],
            e -> [ ObjId(@(1).val)(@(1).val.child(1) * @(2).val.child(1),
                    @(1).val.child(2) * @(2).val.child(2)).attrs(@(1).val) ]),

        CrossBlockTopPass := Rule(@(1,CrossBlockTop,e->not((ObjId(e.rChildren()[2])=Prm) and
                ObjId(e.rChildren()[2].rChildren()[2])=fId and
                ObjId(e.rChildren()[2].rChildren()[3])=fId)),
        e->Cross(Prm(fId(@(1).val.rChildren()[1].dims()[1])),@(1).val.rChildren()[2])*
            CrossBlockTop(@(1).val.rChildren()[1],
                Prm(fId(@(1).val.rChildren()[2].dims()[2])))
        )
));

OLCrossPullInRuleset := MergedRuleSet(OLCrossPullInRules, StandardSumsRules, OLScatFuseRules,
    OLVectorScatFuseRules, RulesVec, RulesPropagate);

Class(OLAfterCrossPullInRuleset, RuleSet);
RewriteRules(OLAfterCrossPullInRuleset, rec(
        Cross_CompatibleISum  := Rule([Cross, @(1, ISum), @(2, ISum,e->e.domain=@(1).val.domain) ],
            function(e)
              local varmap, var;
              varmap:=tab();
              var:=@(2).val.var;
              varmap.(var.id):=@(1).val.var;
              return ISum(@(1).val.var, @(1).val.domain,
                  Cross(@(1).val.rChildren()[1],SubstVars(@(2).val.rChildren()[1],varmap)));
            end)
));

Class(OLTearCrossRules, RuleSet);
RewriteRules(OLTearCrossRules, rec(
#        FullTearVGath_dup:=ARule(Cross,[@(1,VGath_dup)],e->[VGath(@(1).val.func,@(1).val.v)*VGath_dup(fId(@(1).val.func.range()),@(1).val.v)]),

#HACK, of course
        HalfTearVGath_dup:=Rule([@(1,VGath_dup),[@(2,fTensor),@(3,fBase),fBase,...]],e->
            let(func:=fTensor(Drop(Copy(@(2).val.rChildren()),1)),
            VGath(func,@(1).val.v)*
            VGath_dup(fTensor(@(3).val,fId(func.range())),@(1).val.v))),

        TearCross:=ARule(Compose,[@(1,Cross,e->e.hasRightScat() and e.hasLeftGath())],
            e->[Copy(@(1).val.splitCross()[1]),Copy(@(1).val.splitCross()[2])]),

        PushCrossOutOfIsum :=
        Rule([@(1,ISum), @(2,Compose,e->ObjId(Last(e.rChildren()))=Cross and
                    Last(e.rChildren()).hasRightScat() and not Last(e.rChildren()).hasLeftGath())],
            e->Compose(ISum(@(1).val.var, @(1).val.domain,
                    Compose(DropLast(@(2).val.rChildren(),1))),Last(@(2).val.rChildren()))),

#        PushCrossOutOfSMPSum :=
#        Rule([@(1,SMPSum), @(2), @(3,Compose,e->ObjId(Last(e.rChildren()))=Cross and
#                    Last(e.rChildren()).hasRightScat() and not Last(e.rChildren()).hasLeftGath())],
#
#            e->Compose(SMPSum(@(2).val,@(1).val.var, @(1).val.domain,
#                    Compose(DropLast(@(3).val.rChildren(),1))),Last(@(3).val.rChildren()))),

        CrossConditionalMerge :=
        ARule(Compose, [ @(1, Cross), @(2, Cross, e->e.hasRightScat() and
                    @(1).val.hasRightScat() and not(@(1).val.hasLeftGath()) and not(e.hasLeftGath())) ],
            e -> [ ObjId(@(1).val)(@(1).val.child(1) * @(2).val.child(1),@(1).val.child(2) *
                    @(2).val.child(2)).attrs(@(1).val) ]),
));

OLTearCrossRuleset := MergedRuleSet(OLTearCrossRules, StandardSumsRules, OLScatFuseRules,
    OLVectorScatFuseRules, RulesVec);

Class(OLSingleComposeRuleset, RuleSet);
RewriteRules(OLSingleComposeRuleset, rec(
        SingleCompose := Rule([@(1,Compose),@(2)],e->@(2).val),
));

Class(OLDropVGath_dupRuleset, RuleSet);
RewriteRules(OLDropVGath_dupRuleset, rec(
        DropVGath_dup := Rule(@(1,VGath_dup),e->VGath(@(1).val.func,@(1).val.v))
));

Class(OLDropSMPSumRuleset, RuleSet);
RewriteRules(OLDropSMPSumRuleset, rec(
        DropSMPSum := Rule(@(1,SMPSum),e->ISum(@(1).val.var,@(1).val.domain,@1.val.rChildren()[2]))
));

#########  HACCCK !!!
Class(OLMagicVUnrollDupRuleset, RuleSet);
RewriteRules(OLMagicVUnrollDupRuleset, rec(
#        MagicVGath_dup := Rule(@(1,VGath_dup,e->e.func.domain()>128 and e.func.domain() mod (32*4)=0),
#            e->let(itid:=Ind(4),idx:=Ind(@(1).val.func.domain()/(32*4)),
#                SMPSum(4,itid,4,
#                    ISum(idx,@(1).val.func.domain()/(32*4),
#                        VScat(fTensor(fBase(itid),fBase(idx),fId(32)), 2)*
#                        VGath_dup(fTensor(fBase(itid),fBase(idx),fId(32)), 2))),

        MagicVGath_dup := Rule(@(1,VGath_dup,e->e.func.domain()>32),
            e->@(1).val.toloop(32))
));

#Class(OLLiftBarrierRuleset, RuleSet);
#RewriteRules(OLLiftBarrierRuleset, rec(
#        EliminateBarrier := ARule(Compose, [@(1,RewriteBarrier),@(2)],
#            e->[@(2).val]),
#));

OLDefaultStrategy := [TerminateSymSPL,RulesTerm];

#################################################################
Class(OLRulesCode, RuleSet);
RewriteRules(OLRulesCode, rec(

        #Cross must not be BBized without neighbours if it has some identities
        CrossBB_in := Rule([BB,[@(1,Cross),...,[Prm,@,fId,fId],...]],
            e->Cross(List(@(1).val.rChildren(),x->BB(x)))
        ),

        DoubleBB := Rule([BB,@(1,BB)],e->@(1).val),

        #BB should go out if the inner are not Ids (simplified to perms in this case)
        Cross_BB_out := Rule([Cross, @(1, BB,e->not(ObjId(e.rChildren()[1])=Prm)), @(2, BB,e->not(ObjId(e.rChildren()[1])=Prm)) ],
            e -> BB(Cross(@(1).val.child(1),@(2).val.child(1)))),

        Drop_NoPull := Rule(@(1,NoPull),e->@(1).val.rChildren()[1]),

));

Class(OLRulesBufferFinalize, RuleSet);
RewriteRules(OLRulesBufferFinalize, rec(
    FinalizeNoPull := Rule(@(1, [NoPull, NoPullLeft, NoPullRight]), i->@1.val.child(1)),
    DropCrossBlockTop := Rule(@(1,CrossBlockTop), e->Cross(@1.val.rChildren()))
));

#RewriteRules(RulesStrengthReduce, rec(
# vdupmultsR:= Rule([mul, @(1), @(2).cond(e->ObjId(e.t)=TVect and ObjId(@(1).val.t)<>TVect)], e->mul(vdup(@(1).val, @(2).val.t.size), @(2).val)),
#
#vdupmultsL:= Rule([mul, @(1), @(2).cond(e->ObjId(@(1).val.t)=TVect and ObjId(e.t)<>TVect)], e->mul(@(1).val,vdup(@(2).val, @(1).val.t.size))),
#
#));

Class(OLPushScatAccRules,RuleSet);

#ScatAcc that are pushed inside sums MUST merge or else things get added twice or more...
RewriteRules(OLPushScatAccRules, rec(
        PushScatAccInsideISum := ARule(Compose, [@(1, ScatAcc), @(2,ISum)],
            e-> [ ISum(@(2).val.var,@(2).val.domain,Compose(@(1).val,@(2).val._children))]),

        PushScatAccInsideSUM := ARule(Compose, [@(1, ScatAcc), @(2,SUM)],
            e-> let(childs:=List(@(2).val._children,e->@(1).val*e), [ SUM(childs)])),

        PushICScatAccInsideISum := ARule(Compose, [@(1, ICScatAcc), @(2,ISum)],
            e-> [ ISum(@(2).val.var,@(2).val.domain,Compose(@(1).val,@(2).val._children))]),

        PushICScatAccInsideSUM := ARule(Compose, [@(1, ICScatAcc), @(2,SUM)],
            e-> let(childs:=List(@(2).val._children,e->@(1).val*e), [ SUM(childs)])),

));
OLPushScatAccRuleset := MergedRuleSet(OLPushScatAccRules, StandardSumsRules, OLScatFuseRules,
    OLVectorScatFuseRules, RulesVec, RulesPropagate);


text:
%Headers
    --
    -- Additional methods for the action class not provided in the template
    --
    /.
        //
        // The Lexer contains an array of characters as the input stream to be parsed.
        // There are methods to retrieve and classify characters.
        // The lexparser "token" is implemented simply as the index of the next character in the array.
        // The Lexer extends the abstract class LpgLexStream with an implementation of the abstract
        // method getKind.  The template defines the Lexer class and the lexer() method.
        // A driver creates the action class, "Lexer", passing an Option object to the constructor.
        //
        $kw_lexer_class *kwLexer= nullptr;
        bool printTokens =false;
          static const int ECLIPSE_TAB_VALUE = 4;

        int*  getKeywordKinds() { return kwLexer->getKeywordKinds(); }

        /**
         * @deprecated function replaced by {@link #reset(char [] content, const std::wstring& filename)}
         */
         void initialize(shared_ptr_wstring content, const std::wstring& filename)
        {
            reset(content, filename);
        }

         void makeToken(int left_token, int right_token, int kind)
        {
            lexStream->makeToken(left_token, right_token, kind);
        }

         void makeToken(int kind)
        {
            int startOffset = getLeftSpan(),
                endOffset = getRightSpan();
            lexStream->makeToken(startOffset, endOffset, kind);
            if (printTokens) printValue(startOffset, endOffset);
        }

         void makeComment(int kind)
        {
            int startOffset = getLeftSpan(),
                endOffset = getRightSpan();
            lexStream->getIPrsStream()->makeAdjunct(startOffset, endOffset, kind);
        }

         void skipToken()
        {
            if (printTokens) printValue(getLeftSpan(), getRightSpan());
        }

         void checkForKeyWord()
        {
            int startOffset = getLeftSpan(),
                endOffset = getRightSpan(),
                kwKind = kwLexer->lexer(startOffset, endOffset);
            lexStream->makeToken(startOffset, endOffset, kwKind);
            if (printTokens) printValue(startOffset, endOffset);
        }

        //
        // This flavor of checkForKeyWord is necessary when the default kind
        // (which is returned when the keyword filter doesn't match) is something
        // other than _IDENTIFIER.
        //
         void checkForKeyWord(int defaultKind)
        {
            int startOffset = getLeftSpan(),
                endOffset = getRightSpan(),
                kwKind = kwLexer->lexer(startOffset, endOffset);
            if (kwKind == $_IDENTIFIER)
                kwKind = defaultKind;
            lexStream->makeToken(startOffset, endOffset, kwKind);
            if (printTokens) printValue(startOffset, endOffset);
        }

         void printValue(int startOffset, int endOffset)
        {
           auto  input = lexStream->getInputChars().data();
            std::wstring s(input + startOffset,input + startOffset+  endOffset - startOffset + 1);
            std::wcout << (s) << std::endl ;
        }

        //
        //
        //
         struct $super_stream_class :public LpgLexStream
        {
         inline static int tokenKind[] =
        {
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 000    0x00
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 001    0x01
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 002    0x02
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 003    0x03
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 004    0x04
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 005    0x05
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 006    0x06
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 007    0x07
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 008    0x08
            $sym_type::$prefix$HT$suffix$,              // 009    0x09
            $sym_type::$prefix$LF$suffix$,              // 010    0x0A
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 011    0x0B
            $sym_type::$prefix$FF$suffix$,              // 012    0x0C
            $sym_type::$prefix$CR$suffix$,              // 013    0x0D
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 014    0x0E
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 015    0x0F
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 016    0x10
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 017    0x11
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 018    0x12
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 019    0x13
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 020    0x14
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 021    0x15
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 022    0x16
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 023    0x17
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 024    0x18
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 025    0x19
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 026    0x1A
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 027    0x1B
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 028    0x1C
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 029    0x1D
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 030    0x1E
            $sym_type::$prefix$CtlCharNotWS$suffix$,    // 031    0x1F
            $sym_type::$prefix$Space$suffix$,           // 032    0x20
            $sym_type::$prefix$Exclamation$suffix$,     // 033    0x21
            $sym_type::$prefix$DoubleQuote$suffix$,     // 034    0x22
            $sym_type::$prefix$Sharp$suffix$,           // 035    0x23
            $sym_type::$prefix$DollarSign$suffix$,      // 036    0x24
            $sym_type::$prefix$Percent$suffix$,         // 037    0x25
            $sym_type::$prefix$Ampersand$suffix$,       // 038    0x26
            $sym_type::$prefix$SingleQuote$suffix$,     // 039    0x27
            $sym_type::$prefix$LeftParen$suffix$,       // 040    0x28
            $sym_type::$prefix$RightParen$suffix$,      // 041    0x29
            $sym_type::$prefix$Star$suffix$,            // 042    0x2A
            $sym_type::$prefix$Plus$suffix$,            // 043    0x2B
            $sym_type::$prefix$Comma$suffix$,           // 044    0x2C
            $sym_type::$prefix$Minus$suffix$,           // 045    0x2D
            $sym_type::$prefix$Dot$suffix$,             // 046    0x2E
            $sym_type::$prefix$Slash$suffix$,           // 047    0x2F
            $sym_type::$prefix$0$suffix$,               // 048    0x30
            $sym_type::$prefix$1$suffix$,               // 049    0x31
            $sym_type::$prefix$2$suffix$,               // 050    0x32
            $sym_type::$prefix$3$suffix$,               // 051    0x33
            $sym_type::$prefix$4$suffix$,               // 052    0x34
            $sym_type::$prefix$5$suffix$,               // 053    0x35
            $sym_type::$prefix$6$suffix$,               // 054    0x36
            $sym_type::$prefix$7$suffix$,               // 055    0x37
            $sym_type::$prefix$8$suffix$,               // 056    0x38
            $sym_type::$prefix$9$suffix$,               // 057    0x39
            $sym_type::$prefix$Colon$suffix$,           // 058    0x3A
            $sym_type::$prefix$SemiColon$suffix$,       // 059    0x3B
            $sym_type::$prefix$LessThan$suffix$,        // 060    0x3C
            $sym_type::$prefix$Equal$suffix$,           // 061    0x3D
            $sym_type::$prefix$GreaterThan$suffix$,     // 062    0x3E
            $sym_type::$prefix$QuestionMark$suffix$,    // 063    0x3F
            $sym_type::$prefix$AtSign$suffix$,          // 064    0x40
            $sym_type::$prefix$A$suffix$,               // 065    0x41
            $sym_type::$prefix$B$suffix$,               // 066    0x42
            $sym_type::$prefix$C$suffix$,               // 067    0x43
            $sym_type::$prefix$D$suffix$,               // 068    0x44
            $sym_type::$prefix$E$suffix$,               // 069    0x45
            $sym_type::$prefix$F$suffix$,               // 070    0x46
            $sym_type::$prefix$G$suffix$,               // 071    0x47
            $sym_type::$prefix$H$suffix$,               // 072    0x48
            $sym_type::$prefix$I$suffix$,               // 073    0x49
            $sym_type::$prefix$J$suffix$,               // 074    0x4A
            $sym_type::$prefix$K$suffix$,               // 075    0x4B
            $sym_type::$prefix$L$suffix$,               // 076    0x4C
            $sym_type::$prefix$M$suffix$,               // 077    0x4D
            $sym_type::$prefix$N$suffix$,               // 078    0x4E
            $sym_type::$prefix$O$suffix$,               // 079    0x4F
            $sym_type::$prefix$P$suffix$,               // 080    0x50
            $sym_type::$prefix$Q$suffix$,               // 081    0x51
            $sym_type::$prefix$R$suffix$,               // 082    0x52
            $sym_type::$prefix$S$suffix$,               // 083    0x53
            $sym_type::$prefix$T$suffix$,               // 084    0x54
            $sym_type::$prefix$U$suffix$,               // 085    0x55
            $sym_type::$prefix$V$suffix$,               // 086    0x56
            $sym_type::$prefix$W$suffix$,               // 087    0x57
            $sym_type::$prefix$X$suffix$,               // 088    0x58
            $sym_type::$prefix$Y$suffix$,               // 089    0x59
            $sym_type::$prefix$Z$suffix$,               // 090    0x5A
            $sym_type::$prefix$LeftBracket$suffix$,     // 091    0x5B
            $sym_type::$prefix$BackSlash$suffix$,       // 092    0x5C
            $sym_type::$prefix$RightBracket$suffix$,    // 093    0x5D
            $sym_type::$prefix$Caret$suffix$,           // 094    0x5E
            $sym_type::$prefix$_$suffix$,               // 095    0x5F
            $sym_type::$prefix$BackQuote$suffix$,       // 096    0x60
            $sym_type::$prefix$a$suffix$,               // 097    0x61
            $sym_type::$prefix$b$suffix$,               // 098    0x62
            $sym_type::$prefix$c$suffix$,               // 099    0x63
            $sym_type::$prefix$d$suffix$,               // 100    0x64
            $sym_type::$prefix$e$suffix$,               // 101    0x65
            $sym_type::$prefix$f$suffix$,               // 102    0x66
            $sym_type::$prefix$g$suffix$,               // 103    0x67
            $sym_type::$prefix$h$suffix$,               // 104    0x68
            $sym_type::$prefix$i$suffix$,               // 105    0x69
            $sym_type::$prefix$j$suffix$,               // 106    0x6A
            $sym_type::$prefix$k$suffix$,               // 107    0x6B
            $sym_type::$prefix$l$suffix$,               // 108    0x6C
            $sym_type::$prefix$m$suffix$,               // 109    0x6D
            $sym_type::$prefix$n$suffix$,               // 110    0x6E
            $sym_type::$prefix$o$suffix$,               // 111    0x6F
            $sym_type::$prefix$p$suffix$,               // 112    0x70
            $sym_type::$prefix$q$suffix$,               // 113    0x71
            $sym_type::$prefix$r$suffix$,               // 114    0x72
            $sym_type::$prefix$s$suffix$,               // 115    0x73
            $sym_type::$prefix$t$suffix$,               // 116    0x74
            $sym_type::$prefix$u$suffix$,               // 117    0x75
            $sym_type::$prefix$v$suffix$,               // 118    0x76
            $sym_type::$prefix$w$suffix$,               // 119    0x77
            $sym_type::$prefix$x$suffix$,               // 120    0x78
            $sym_type::$prefix$y$suffix$,               // 121    0x79
            $sym_type::$prefix$z$suffix$,               // 122    0x7A
            $sym_type::$prefix$LeftBrace$suffix$,       // 123    0x7B
            $sym_type::$prefix$VerticalBar$suffix$,     // 124    0x7C
            $sym_type::$prefix$RightBrace$suffix$,      // 125    0x7D
            $sym_type::$prefix$Tilde$suffix$,           // 126    0x7E

            $sym_type::$prefix$AfterASCII$suffix$,      // for all chars in range 128..65534
            $sym_type::$prefix$EOF$suffix$              // for '\uffff' or 65535

        };

          int getKind(int i)  // Classify character at ith location
        {
            int c = (i >= getStreamLength() ? 0xffff : getCharValue(i));
            return (c < 128 // ASCII Character
                      ? tokenKind[c]
                      : c == 0xffff
                           ? $sym_type::$prefix$EOF$suffix$
                           : $sym_type::$prefix$AfterASCII$suffix$);
        }

        std::vector<std::wstring> orderedExportedSymbols() { return $exp_type::orderedTerminalSymbols; }

         $super_stream_class(const std::wstring& filename, int tab):LpgLexStream(filename, tab)
        {

        }

         $super_stream_class(shared_ptr_wstring input_chars, const std::wstring& filename, int tab):LpgLexStream(input_chars, filename, tab)
        {

        }

         $super_stream_class(shared_ptr_wstring input_chars, const std::wstring& filename):LpgLexStream(input_chars, filename, 1)
        {

        }
        };
    ./
%End

max_stars_repo_name: kuafuwang/LPG-VScode

max_issues_repo_name: kuafuwang/LPG-VScode

max_forks_repo_name: kuafuwang/LPG-VScode


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

##########################################################################################
#   tSPL Pease DFTDR rule
NewRulesFor(DFTDR, rec(
    # Pease tSPL DFT_(k)*DR(k,r) -> \Prod(L(I tensor F)Tc)
    DFTDR_tSPL_Pease   := rec (
        forTransposition := true,
        minSize := 2,

	# set to true to wrap TC in fPrecompute (i.e., for software)
	# leave as false to allow TC to be simplified by other rules
	# (i.e., for hardware)
	precompute := false,

        applicable := (self, t) >> let(
            tags := t.getTags(), n := t.params[1], k := t.params[2], radix := t.params[3],
            stream := Cond(Length(tags)=1 and IsBound(tags[1].bs), tags[1].bs, -1),
               Length(tags) >= 1    and
               (IsSymbolic(n)    or ((n >= self.minSize) and IsIntPower(n, radix)))
	       and IsInt(LogInt(n, radix) / self.unroll_its)
#           and k = 1
        ),

        freedoms := t -> [[ t.params[3] ]], # radix is the only degree of freedom

	# Used in HW to have multiple stages inside the TICompose.  For "normal"
	# operation, keep this at 1.
	unroll_its := 1,

        child := (self, t, fr) >> let(
            n     := t.params[1],
            radix := fr[1],
            e     := t.params[2],
            tags  := t.getTags(),
            j     := Ind(LogInt(n, radix)/self.unroll_its),
            stage := TTensorI(DFT(radix, e), n/radix, AVec, APar),
            fPre  := Cond(self.precompute, fPrecompute, x->x),
	    twid  := i >> TDiag(fPre(TC(n, radix, self.unroll_its*j+i, e))),
	    full_stage := List([1..self.unroll_its], t->(TCompose([stage, twid(t-1)]))),

            [ TICompose(j, j.range, TCompose(full_stage)).withTags(tags) ]
        ),

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

# it is a transpose, Spiral will transpose automatically
DRDFT_tSPL_Pease := DFTDR_tSPL_Pease;