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

SIMD_ISA_DB.hashAdd(TL(4, 2, 1, 1).withTags([ AVecReg(MACRO_2xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(4, 2, 1, 1).withTags([ AVecReg(MACRO_2xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(4, 2, 1, 1).withTags([ AVecReg(MACRO_2xf) ]) ),
      measured := 2,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(1, 1, 1, 2).withTags([ AVecReg(MACRO_2xf) ]), [ rec(
      ruletree := IxLxI_vtensor( TL(1, 1, 1, 2).withTags([ AVecReg(MACRO_2xf) ]) ),
      origtree := IxLxI_vtensor( TL(1, 1, 1, 2).withTags([ AVecReg(MACRO_2xf) ]) ),
      measured := 0,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 1, 2).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(4, 2, 1, 2).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(4, 2, 1, 2).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 2,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 2, 1).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(4, 2, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(4, 2, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 6.2000000000000002,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 1, 4).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := IxLxI_vtensor( TL(4, 2, 1, 4).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := IxLxI_vtensor( TL(4, 2, 1, 4).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 0,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 4, 1).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(4, 2, 4, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(4, 2, 4, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 12.4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 8, 1, 1).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := IxLxI_kmn_km( TL(16, 8, 1, 1).withTags([ AVecReg(MACRO_4xf) ]),
          SIMD_ISA_Bases1( TL(8, 4, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 1, 4).withTags([ AVecReg(MACRO_4xf) ]) ) ),
      origtree := IxLxI_kmn_km( TL(16, 8, 1, 1).withTags([ AVecReg(MACRO_4xf) ]),
          SIMD_ISA_Bases1( TL(8, 4, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 1, 4).withTags([ AVecReg(MACRO_4xf) ]) ) ),
      measured := 4,
      globalUnrolling := 10000 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 4, 1, 1).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(8, 4, 1, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(8, 4, 1, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 2,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 4, 1, 2).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases2( TL(8, 4, 1, 2).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := SIMD_ISA_Bases2( TL(8, 4, 1, 2).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 4, 2, 1).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(8, 4, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(8, 4, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 2, 1, 2).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases2( TL(8, 2, 1, 2).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := SIMD_ISA_Bases2( TL(8, 2, 1, 2).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 2, 2, 1).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(8, 2, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(8, 2, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 4, 1, 1).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(16, 4, 1, 1).withTags([ AVecReg(MACRO_4xf) ]),
          SIMD_ISA_Bases2( TL(8, 4, 1, 2).withTags([ AVecReg(MACRO_4xf) ]) ),
          SIMD_ISA_Bases1( TL(8, 4, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ) ),
      origtree := IxLxI_kmn_n( TL(16, 4, 1, 1).withTags([ AVecReg(MACRO_4xf) ]),
          SIMD_ISA_Bases2( TL(8, 4, 1, 2).withTags([ AVecReg(MACRO_4xf) ]) ),
          SIMD_ISA_Bases1( TL(8, 4, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ) ),
      measured := 8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 2, 2).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(4, 2, 2, 2).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(4, 2, 2, 2).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 2, 1, 1).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(16, 2, 1, 1).withTags([ AVecReg(MACRO_4xf) ]),
          IxLxI_vtensor( TL(4, 2, 1, 4).withTags([ AVecReg(MACRO_4xf) ]) ),
          SIMD_ISA_Bases1( TL(8, 2, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ) ),
      origtree := IxLxI_kmn_n( TL(16, 2, 1, 1).withTags([ AVecReg(MACRO_4xf) ]),
          IxLxI_vtensor( TL(4, 2, 1, 4).withTags([ AVecReg(MACRO_4xf) ]) ),
          SIMD_ISA_Bases1( TL(8, 2, 2, 1).withTags([ AVecReg(MACRO_4xf) ]) ) ),
      measured := 4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 2, 1, 1).withTags([ AVecReg(MACRO_4xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(8, 2, 1, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(8, 2, 1, 1).withTags([ AVecReg(MACRO_4xf) ]) ),
      measured := 2,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(16, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_vtensor( TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
          SIMD_ISA_Bases1( TL(8, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_n( TL(16, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_vtensor( TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
          SIMD_ISA_Bases1( TL(8, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 8, 2, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_km( TL(16, 8, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_km( TL(16, 8, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(64, 8, 1, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(64, 8, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_km( TL(32, 8, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_IxLxI_up( TL(16, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
                IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
                SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
              IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
                IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
                SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
            IxLxI_kmn_n( TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
          SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_n( TL(64, 8, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_km( TL(32, 8, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_IxLxI_up( TL(16, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
                IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
                SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
              IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
                IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
                SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
            IxLxI_kmn_n( TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
          SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 24,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(4, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(4, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 0.90000000000000002,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 0,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 8, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(4, 2, 8, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(4, 2, 8, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 7.2000000000000002,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(8, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(8, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(64, 32, 1, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_km( TL(64, 32, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      origtree := IxLxI_kmn_km( TL(64, 32, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      measured := 8,
      globalUnrolling := 10000 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 8, 1, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(16, 8, 1, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(16, 8, 1, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 2,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(64, 4, 1, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(64, 4, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          IxLxI_kmn_km( TL(32, 4, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
            SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ) ),
      origtree := IxLxI_kmn_n( TL(64, 4, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          IxLxI_kmn_km( TL(32, 4, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
            SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ) ),
      measured := 16,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(32, 8, 1, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_km( TL(32, 8, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_IxLxI_up( TL(16, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
            IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
          IxLxI_kmn_n( TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      origtree := IxLxI_kmn_km( TL(32, 8, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_IxLxI_up( TL(16, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
            IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
          IxLxI_kmn_n( TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      measured := 16,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(32, 8, 2, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(32, 8, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_km( TL(16, 8, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            SIMD_ISA_Bases1( TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_n( TL(32, 8, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_km( TL(16, 8, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            SIMD_ISA_Bases1( TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 16,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(64, 2, 1, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(64, 2, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_n( TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_n( TL(64, 2, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_n( TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 8, 1, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_km( TL(16, 8, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(8, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_km( TL(16, 8, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(8, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 4,
      globalUnrolling := 10000 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 0,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(32, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(32, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_n( TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_n( TL(32, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_n( TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(32, 2, 2, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(32, 2, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
          SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_n( TL(32, 2, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
          SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_n( TL(8, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 0,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 4, 1, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(8, 4, 1, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(8, 4, 1, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 2,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_vtensor( TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := IxLxI_vtensor( TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 0,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(8, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(8, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 2,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 4, 4, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_IxLxI_up( TL(16, 4, 4, 1).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
          SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_IxLxI_up( TL(16, 4, 4, 1).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
          SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 16,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(32, 16, 1, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_km( TL(32, 16, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      origtree := IxLxI_kmn_km( TL(32, 16, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      measured := 8,
      globalUnrolling := 10000 ) ]);
SIMD_ISA_DB.hashAdd(TL(32, 16, 2, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_km( TL(32, 16, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_km( TL(32, 16, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 8,
      globalUnrolling := 10000 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 4, 1, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_IxLxI_up( TL(16, 4, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(16, 2, 1, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
          SIMD_ISA_Bases1( TL(16, 2, 1, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_IxLxI_up( TL(16, 4, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(16, 2, 1, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
          SIMD_ISA_Bases1( TL(16, 2, 1, 1).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
          SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      origtree := IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
          SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
      measured := 8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(8, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(8, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(8, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 4,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(4, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(4, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 1.8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(32, 4, 1, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(32, 4, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          IxLxI_IxLxI_up( TL(16, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
            IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ) ),
      origtree := IxLxI_kmn_n( TL(32, 4, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          IxLxI_IxLxI_up( TL(16, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
            IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ) ),
      measured := 16,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(32, 4, 2, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_km( TL(32, 4, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      origtree := IxLxI_kmn_km( TL(32, 4, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
          SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
          IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      measured := 16,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 2, 1, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(16, 2, 1, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(16, 2, 1, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 2,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(64, 16, 1, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_kmn_n( TL(64, 16, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_km( TL(32, 16, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
            SIMD_ISA_Bases1( TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
          IxLxI_kmn_km( TL(32, 16, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
            SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      origtree := IxLxI_kmn_n( TL(64, 16, 1, 1).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_km( TL(32, 16, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
            SIMD_ISA_Bases1( TL(8, 4, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_kmn_km( TL(8, 4, 1, 8).withTags([ AVecReg(MACRO_8xf) ]),
              IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
              IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
          IxLxI_kmn_km( TL(32, 16, 2, 1).withTags([ AVecReg(MACRO_8xf) ]),
            SIMD_ISA_Bases1( TL(16, 8, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      measured := 16,
      globalUnrolling := 10000 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_IxLxI_up( TL(16, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      origtree := IxLxI_IxLxI_up( TL(16, 4, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          IxLxI_kmn_n( TL(16, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 2, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            SIMD_ISA_Bases1( TL(8, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      measured := 16,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(4, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(4, 2, 4, 2).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 3.6000000000000001,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := IxLxI_vtensor( TL(4, 2, 1, 16).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 0,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
      origtree := SIMD_ISA_Bases1( TL(16, 2, 4, 1).withTags([ AVecReg(MACRO_8xf) ]) ),
      measured := 8,
      globalUnrolling := 8 ) ]);
SIMD_ISA_DB.hashAdd(TL(16, 4, 1, 2).withTags([ AVecReg(MACRO_8xf) ]), [ rec(
      ruletree := IxLxI_IxLxI_up( TL(16, 4, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_n( TL(16, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            SIMD_ISA_Bases1( TL(8, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          IxLxI_kmn_n( TL(16, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            SIMD_ISA_Bases1( TL(8, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      origtree := IxLxI_IxLxI_up( TL(16, 4, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
          IxLxI_kmn_n( TL(16, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            SIMD_ISA_Bases1( TL(8, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ),
          IxLxI_kmn_n( TL(16, 2, 1, 2).withTags([ AVecReg(MACRO_8xf) ]),
            IxLxI_vtensor( TL(4, 2, 1, 8).withTags([ AVecReg(MACRO_8xf) ]) ),
            SIMD_ISA_Bases1( TL(8, 2, 2, 2).withTags([ AVecReg(MACRO_8xf) ]) ) ) ),
      measured := 8,
      globalUnrolling := 8 ) ]);


#############################################################################
####
##
#W  anupqhead.gi               ANUPQ package                    Werner Nickel
#W                                                                Greg Gamble
##
##  `Head' file for the GAP interface to the ANU pq binary by Eamonn O'Brien.
##
#Y  Copyright (C) 2006  Lehrstuhl D fuer Mathematik,  RWTH Aachen,  Germany
##

#############################################################################
##
#V  ANUPQData . . record used by various functions of the ANUPQ package
##
##  The fields of ANUPQData are:
##
##    "binary"  . . the path of the pq binary
##    "tmpdir"  . . the path of the temporary directory for pq i/o files
##    "io"  . . . . list of data records for PqStart IO Streams
##    "outfile" . . the path of the pq output file
##    "SPimages"  . the path of the pq GAP_library file
##    "version" . . the version of the current pq binary
##
InstallValue( ANUPQData,
  rec( binary := Filename( DirectoriesPackagePrograms( "anupq" ), "pq" ),
       tmpdir := DirectoryTemporary(),
       ni := rec(), # record for non-interactive functions
       io := []     # list of records for PqStart IO Streams,
                    #  of which, there are initially none
       )
);
ANUPQData.outfile  := Filename( ANUPQData.tmpdir, "PQ_OUTPUT" );
ANUPQData.SPimages := Filename( ANUPQData.tmpdir, "GAP_library" );

# Fire up the pq binary to get its version
ANUPQData.version := "";
Process( DirectoryCurrent(), ANUPQData.binary, InputTextNone(),
         OutputTextString( ANUPQData.version, false ), [ "-v" ] );
ANUPQData.version :=
    ANUPQData.version{[PositionSublist( ANUPQData.version, "Version" ) + 8 ..
                       Length(ANUPQData.version) - 1] };

#############################################################################
##
#I  InfoClass
##
# Set the default level of InfoANUPQ
SetInfoLevel( InfoANUPQ, 1 );

#############################################################################
##
#V  ANUPQWarnOfOtherOptions . if true user is warned of non-ANUPQ-f'n options
##
ANUPQWarnOfOtherOptions := false;

#############################################################################
##
##  Ensure no zombie `pq' processes from interactive (`PqStart') sessions are
##  left lying around when user quits GAP.
##
InstallAtExit( PqQuitAll );

#E  anupqhead.gi . . . . . . . . . . . . . . . . . . . . . . . . .  ends here


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

#-----------------------------------------------------------------------------
# Cell SMP Paradigm (preferred)
#-----------------------------------------------------------------------------

#F SymSPL = Symmetric SPL. In this particular case, we know about the symmetry
#F of this SymSPL, so we can remove it.
Class(KillSymSPL, RuleSet);
RewriteRules(KillSymSPL, rec(
     kill_symspl := Rule([@(1,SymSPL), ISum], e -> @(1).val.child(1)),
));

NewRulesFor(GT, rec(
#F For cases where p doesn't divide n nicely. (eg: Parallelize (I6 x A) across 4 processors
    GT_Cell_nonmultiple := rec(
        maxSize       := false,
        minSize       := false,

        requiredFirstTag := ParCell,

        applicable := (self, t) >> let(
                rank   := Length(t.params[4]),
                procs  := t.firstTag().params[1],
                rank = 1
                  and let(its := t.params[4][1],
                    PatternMatch(t, [GT, @(1), @(2,XChain), @(3,XChain), ...], empty_cx())
                    # Only do IxA for now
                    and t.params[2] = GTPar and t.params[3] = GTPar
                    and IsPosInt(idiv(its, procs).v)
                    and IsPosInt(imod(its, procs).v)
                  )
        ),
        children := (self, t) >> let(
            spl     := t.params[1],
            g       := t.params[2],
            s       := t.params[3],
            its     := t.params[4][1],
            procs   := t.firstTag().params[1],
            pksize  := t.firstTag().params[2],
            remits  := imod(its, procs).v,
            parits  := its - remits,
            origtags:= t.getTags(),
            partag  := t.firstTag(),
            tags    := Drop(t.getTags(), 1),
            When(remits > 1,
                [ [ GT(spl, g, s, [parits]).withTags(origtags), GT(spl, g, s, [remits]).setTags(Concatenation(ParCell(remits, pksize), tags)) ] ],
                [ [ GT(spl, g, s, [parits]).withTags(origtags), spl.withTags(tags) ] ]
            )
        ),

        apply := (self, t, C, Nonterms) >> let(
                 DirectSum(C[1], C[2])
        ) #apply
    ), #GT_Cell_nonmultiple

    GT_Cell := rec(
        maxSize       := false,
        minSize       := false,

        requiredFirstTag := ParCell,

        applicable := (self, t) >> let(
                rank   := Length(t.params[4]),
                procs  := t.firstTag().params[1],
                pkSize := t.firstTag().params[2],
                rank = 1
                  and let(its := t.params[4][1],
                    PatternMatch(t, [GT, @(1), @(2,XChain), @(3,XChain), ...], empty_cx())
                    # The (IxA) construct doesn't need the pkSize constraint that
                    # the (AxI) construct (and (IxA)L, L(IxA)) does:
                    and When(t.params[2] = GTPar and t.params[3] = GTPar,
                                IsPosInt(its/(procs)),
                                IsPosInt(its/(procs*pkSize))
                        )
                  )
        ),

        children := (self, t) >> let(
            spl    := t.params[1],
            g      := t.params[2],
            s      := t.params[3],
            its    := t.params[4][1],
            procs  := t.firstTag().params[1],
            remits := its/procs,
            tags   := Drop(t.getTags(), 1), # Eat up tag at topmost level. NOTE: Nested parallelism not considered for now
            When(remits > 1,
            [ [ GT(spl, g, s, [its / procs]).withTags(tags), ] ],
            [ [ spl.withTags(tags) ] ])
        ),

        apply := (self, t, C, Nonterms) >> let(
            spl     := t.params[1],
            g       := t.params[2],
            s       := t.params[3],
            its     := t.params[4][1],
            gg      := When(g=GTVec, XChain([0,1,2]), XChain([1,2,0])),
            ss      := When(s=GTVec, XChain([0,1,2]), XChain([1,2,0])),
            procs   := t.firstTag().params[1],
            pkSize  := t.firstTag().params[2],
            i       := var("spuid", TInt, procs),
            #z := Error("Breakpoint\n"),

            # NOTE: There is a degree of freedom in allocating packet sizes for
            # ScatDist and GathDist. Here's how we handle things:

            # When we do on-chip exchanges, we "combine" ScatSends with
            # GathDists (and vice versa). So we want the packet size of the
            # ScatSends and GathDists (and vice versa) to match across multiple
            # factors of the transform (like the DFT). So we issue a ScatDist
            # here with a matching packet size.

            # However, when we do off-chip stuff, we compose ScatDists with
            # ScatMems instead. Here, we want the highest packet size possible
            # for our ScatDists and GathDists.

            # We need to find a universal solution that works neatly for
            # everything. Perhaps issue the largest available pkSize for
            # ScatDist here, and then step it down if needed in the rewrite
            # rules (since we know it's a ScatDist and that this is possible).

            cscatter:= When(s = GTPar,
                        #ScatDist(ss.part(1, i, Rows(spl), [procs, its/(procs*pkSize)]).range(), pkSize, procs, i),
                        ScatDist(procs, its*Rows(spl)/procs, procs, i),
                        ScatSend(ss.part(1, i, Rows(spl), [procs, its/(procs*pkSize)]),         pkSize, procs, i)),
            cgather := When(g = GTPar,
                        #GathDist(gg.part(1, i, Cols(spl), [procs, its/(procs*pkSize)]).range(), pkSize, procs, i),
                        GathDist(procs, its*Rows(spl)/procs, procs, i),
                        GathRecv(gg.part(1, i, Cols(spl), [procs, its/(procs*pkSize)]),         pkSize, procs, i)),

            DistSum(procs, i, procs, cscatter * C[1] * cgather)
        ) #apply
    ), #GT_Cell

    GT_Cell_auto := rec(
        minPkSize := 4, # Hardcoded for real single precision (4 real elements == 16 bytes)
        requiredFirstTag := ParCell_auto,

        applicable := (self, t) >> let(
                rank   := Length(t.params[4]),
                procs  := t.firstTag().params[1],
                rank = 1
                  and let(its := t.params[4][1],
                    PatternMatch(t, [GT, @(1), @(2,XChain), @(3,XChain), ...], empty_cx())
                    # The (IxA) construct doesn't need the pkSize constraint that
                    # the (AxI) construct (and (IxA)L, L(IxA)) does:
                    and When(t.params[2] = GTPar and t.params[3] = GTPar,
                                IsPosInt(its/(procs)),
                                IsPosInt(its/(procs * self.minPkSize)) #Need a packet size of at least 4
                        )
                  )
        ),

        children := (self, t) >> let(
            spl    := t.params[1],
            g      := t.params[2],
            s      := t.params[3],
            n      := t.params[4][1],
            p      := t.firstTag().params[1],
            r      := n/p,
            tags   := Drop(t.getTags(), 1), # Eat up tag at topmost level. NOTE: Nested parallelism not considered for now
            When(r > 1,
            [ [ GT(spl, g, s, [n/p]).withTags(tags) ] ],
            [ [ spl.withTags(tags) ] ])
        ),

        apply := (self, t, C, Nonterms) >> let(
#           z := Error("Breakpoint\n"),
            spl     := t.params[1],
            g       := t.params[2],
            s       := t.params[3],
            n       := t.params[4][1],
            gg      := When(g.params[1]=[0,1], XChain([0,1,2]), XChain([1,2,0])),
            ss      := When(s.params[1]=[0,1], XChain([0,1,2]), XChain([1,2,0])),
            p       := t.firstTag().params[1],
            pkSize  := n/p,       # Automatically use up the remaining stuff for pkSize
                       # NOTE: pkSize is WRONG! Will be different for Par and Vec!
            i       := var("spuid", TInt, p),
            cscatter:= When(s.params[1]=[1,0],
                        ScatDist(ss.part(1, i, Rows(spl), [p, n/(p*pkSize)]).range(), pkSize, p, i),
                        ScatSend(ss.part(1, i, Rows(spl), [p, n/(p*pkSize)]),         pkSize, p, i)),
            cgather := When(g.params[1]=[1,0],
                        GathDist(gg.part(1, i, Cols(spl), [p, n/(p*pkSize)]).range(), pkSize, p, i),
                        GathRecv(gg.part(1, i, Cols(spl), [p, n/(p*pkSize)]),         pkSize, p, i)),

            DistSum(p, i, p, cscatter * C[1] * cgather)
        ) #apply
    ), #GT_Cell
));

#-----------------------------------------------------------------------------
# Cell DMP Paradigm
#-----------------------------------------------------------------------------
NewRulesFor(GT, rec(
    # Converts (In x A) to A DistSum (parallel loop)
    GT_CellDMP_base_old := rec(
        maxSize       := false,
        minSize       := false,

        requiredFirstTag := ParCellDMP_old,

        applicable := (self, t) >> let(
                rank   := Length(t.params[4]),
                procs  := t.firstTag().params[1],
                rank = 1
                  and let(its := t.params[4][1],
                    PatternMatch(t, [GT, @(1), @(2,XChain), @(3,XChain), ...], empty_cx())
                    and IsPosInt(its/(procs)))
                  and (t.params[2]=GTPar and t.params[3]=GTPar)

        ),

        children := (self, t) >> let(
            spl    := t.params[1],
            g      := t.params[2],
            s      := t.params[3],
            its    := t.params[4][1],
            procs  := t.firstTag().params[1],
            tags  := Drop(t.getTags(), 1),  # Children don't need tags since we parallelize only at the topmost level
            [ [ GT(spl, g, s, [its / procs]).withTags(tags), InfoNt(procs) ] ]
        ),

        apply := (self, t, C, Nonterms) >> let(
            spl     := t.params[1],
            g       := t.params[2],
            s       := t.params[3],
            its     := t.params[4][1],
            gg      := When(g.params[1]=[0,1], XChain([0,1,2]), XChain([1,2,0])),
            ss      := When(s.params[1]=[0,1], XChain([0,1,2]), XChain([1,2,0])),
            procs   := t.firstTag().params[1],
            i       := var("spuid", TInt, procs),
            cscatter:= ScatDist(ss.part(1, i, Rows(spl), [procs, its/(procs)]).range(), 1, procs, i),
            cgather := GathDist(gg.part(1, i, Cols(spl), [procs, its/(procs)]).range(), 1, procs, i),

            DistSum(procs, i, procs, cscatter * C[1] * cgather)

        ) #apply
    ), #GT_CellDMP_Base

    # Converts all other TTensor constructs (IxA)L, L(IxA), (AxI) into L's and (IxA)s
    # The L's are factorized to PTensor * Comm_Cell * PTensor
    GT_CellDMP_gen_old := rec(
        maxSize    := false,
        minSize    := false,
        forTransposition := false,

        requiredFirstTag := ParCellDMP_old,

        applicable := (self, t) >> let(
                rank   := Length(t.params[4]),
                procs  := t.firstTag().params[1],
                rank = 1
                  and let(its := t.params[4][1],
                    PatternMatch(t, [GT, @(1), @(2,XChain), @(3,XChain), ...], empty_cx())
                    and IsPosInt(its/(procs))
                    )
                  and not(t.params[2]=GTPar and t.params[3]=GTPar)
             ),

        children := (self, t) >> let(
            spl    := t.params[1],
            g      := t.params[2],
            s      := t.params[3],
            its    := t.params[4][1],
            procs  := t.firstTag().params[1],
            tags   := t.getTags(),
            m      := Rows(spl),
            n      := its,

            Cond(g = GTVec and s = GTPar, # L(IxA) case
                  [ [
                      TCompose([
                         GT(spl, GTPar, GTPar, t.params[4]),
                         TL(m*n, n)
                      ]).withTags(tags),
                  ] ],

                  g = GTPar and s = GTVec, # (IxA)L case
                  [ [
                      TCompose([
                         TL(m*n, m),
                         GT(spl, GTPar, GTPar, t.params[4]),
                      ]).withTags(tags),
                  ] ],

                  g = GTVec and s = GTVec, # AxI case
                  [ [
                      TCompose([
                         TL(m*n, m),
                         GT(spl, GTPar, GTPar, t.params[4]),
                         TL(m*n, n)
                      ]).withTags(tags),
                  ] ]
            )
        ),

        apply := (self, t, C, Nonterms) >> C[1]
    ) #GT_CellDMP_gen

));

NewRulesFor(TL, rec(
   TL_CellDMP_old := rec(
       maxSize       := false,
       minSize       := false,
       forTransposition := false,

       requiredFirstTag := ParCellDMP_old,

       applicable := (self, t) >> let(
           mn := t.params[1],
           m  := t.params[2],
           p  := t.firstTag().params[1],
           n  := mn/m,
           p2 := p*p,
           IsPosInt(m/p) and IsPosInt(mn/p2) and IsPosInt(n) and (n>p)
       ),

       # Note: TLs with a ParCellDMP tag should never have either of the Ix set (Should always be I1 x L x I1)
       children := (self, t) >> let(
           mn := t.params[1],
           m  := t.params[2],
           p  := t.firstTag().params[1],
           n  := mn/m,
           p2 := p*p,

           # Eat up the parallel (ParCellDMP) tag.
           tags  := Drop(t.getTags(), 1),

           [ [ TL(mn/p, m/p).withTags(tags), TL(n, p, 1, m/p).withTags(tags), InfoNt(m,n,p) ] ]
       ),

       apply := (self, t, C, Nonterms) >> let(
           m   := Nonterms[3].params[1],
           n   := Nonterms[3].params[2],
           p   := Nonterms[3].params[3],
           p2  := p*p,
           mn  := m*n,

           PTensor(C[1], p) * Comm_Cell(p, mn/p2) * PTensor(C[2], p)
       )
   ) #TL_CellDMP
));

setStandAlone := function(tags)
   local i, retval;
   retval := Copy(tags);

   for i in retval do
      i.isEdge := true;
   od;
   return(retval);
end;

#-----------------------------------------------------------------------------#-----------------------------------------------------------------------------#----------------------------------------------------------------------------- #-----------------------------------------------------------------------------
#-----------------------------------------------------------------------------
# Cell DMP Paradigm - new
#-----------------------------------------------------------------------------
NewRulesFor(GT, rec(
    GT_StickyL := rec(
        requiredFirstTag := StickyL,
        applicable := (self, t) >> true,
        children   := (self, t) >> [[ t.withoutFirstTag() ]],
        apply := (self, t, C, Nonterms) >> C[1]
    ),

    # Converts (In x A) to A DistSum (parallel loop)
    GT_CellDMP_base := rec(
        maxSize       := false,
        minSize       := false,

        requiredFirstTag := ParCellDMP,

        applicable := (self, t) >> let(
                rank   := Length(t.params[4]),
                procs  := t.firstTag().params[1],
                rank = 1
                  and let(its := t.params[4][1],
                    PatternMatch(t, [GT, @(1), @(2,XChain), @(3,XChain), ...], empty_cx())
                    and IsPosInt(its/(procs)))
                  and (t.params[2]=GTPar and t.params[3]=GTPar)

        ),

        children := (self, t) >> let(
            spl    := t.params[1],
            g      := t.params[2],
            s      := t.params[3],
            its    := t.params[4][1],
            procs  := t.firstTag().params[1],
            tags  := Drop(t.getTags(), 1),  # No nested parallelism for now
            [ [ GT(spl, g, s, [its / procs]).withTags(tags), InfoNt(procs) ] ]
        ),

        apply := (self, t, C, Nonterms) >> let(
            spl     := t.params[1],
            g       := t.params[2],
            s       := t.params[3],
            its     := t.params[4][1],
            gg      := When(g.params[1]=[0,1], XChain([0,1,2]), XChain([1,2,0])),
            ss      := When(s.params[1]=[0,1], XChain([0,1,2]), XChain([1,2,0])),
            procs   := t.firstTag().params[1],
            i       := var("spuid", TInt, procs),
            cscatter:= ScatDist(ss.part(1, i, Rows(spl), [procs, its/(procs)]).range(), 1, procs, i),
            cgather := GathDist(gg.part(1, i, Cols(spl), [procs, its/(procs)]).range(), 1, procs, i),

            DistSum(procs, i, procs, cscatter * C[1] * cgather)

        ) #apply
    ), #GT_CellDMP_Base

    # Converts all other TTensor constructs (IxA)L, L(IxA), (AxI) into L's and (IxA)s
    # The L's are factorized to PTensor * Comm_Cell * PTensor
    GT_CellDMP_gen := rec(
        maxSize    := false,
        minSize    := false,
        forTransposition := false,

        requiredFirstTag := ParCellDMP,

        # NOTE: Ensure applicability for vectorized distributed algo
        applicable := (self, t) >> let(
                rank   := Length(t.params[4]),
                procs  := t.firstTag().params[1],
                v      := When(Length(t.firstTag().params)>1, t.firstTag().params[2], 1),
                n      := t.params[4][1],
                rank = 1
                and PatternMatch(t, [GT, @(1), @(2,XChain), @(3,XChain), ...], empty_cx())
                and IsPosInt((n/v) / procs)
                and not(t.params[2]=GTPar and t.params[3]=GTPar)
                #and t.params[2]=GTVec and t.params[3]=GTVec #NOTE: remove this later
             ),

        children := (self, t) >> let(
            spl    := t.params[1],
            g      := t.params[2],
            s      := t.params[3],
            n      := t.params[4][1],
            procs  := t.firstTag().params[1],
            v      := When(Length(t.firstTag().params)>1, t.firstTag().params[2], 1),
            tags   := t.getTags(),

            ltags  := When(tags[1].leftChild(),  setStandAlone(tags), tags),
            rtags  := When(tags[1].rightChild(), setStandAlone(tags), tags),

           # Error("BP"),

            m      := Rows(spl),
            #splnew := When(v=1, spl, GT(spl.withTags([StickyL(v)]), g, s, [v])),
            splnew := When(v=1, spl, GT(spl, g, s, [v])),

            Cond(s = GTPar and g = GTVec, # (IxA)L case
                  [ [
                         GT(splnew, GTPar, GTPar, [n/v]).withTags(tags),
                         TL(m*n/v, n/v, 1, v).withTags(rtags)
                  ] ],

                  # TEMP: commenting this out. We shouldn't see this case
                  s = GTVec and g = GTPar, # L(IxA) case
                  Error("WTF?"),
                  #[ [
                  #       TL(m*n/v, m, 1, v).withTags(ltags),
                  #       GT(splnew, GTPar, GTPar, [n/v]).withTags(tags)
                  #] ],

                  s = GTVec and g = GTVec, # AxI case
                  [ [
                         TL(m*n/v, m, 1, v).withTags(ltags),
                         GT(splnew, GTPar, GTPar, [n/v]).withTags(tags),
                         TL(m*n/v, n/v, 1, v).withTags(rtags)
                  ] ]
            )
        ),

        apply := (self, t, C, Nonterms) >> When(Length(C)=2, C[1]*C[2], C[1]*C[2]*C[3])
    ) #GT_CellDMP_gen

));

NewRulesFor(TL, rec(
   TL_CellDMP := rec(
       maxSize       := false,
       minSize       := false,
       forTransposition := false,

       requiredFirstTag := ParCellDMP,

       applicable := (self, t) >> let(
           mn := t.params[1],
           m  := t.params[2],
           p  := t.firstTag().params[1],
           n  := mn/m,
           p2 := p*p,
           IsPosInt(m/p) and IsPosInt(mn/p2) and IsPosInt(n) and (n>p)
       ),

       children := (self, t) >> let(
           mnByv := t.params[1],
           m     := t.params[2],
           v     := t.params[4],
           p     := t.firstTag().params[1],
           nByv  := mnByv/m,
           p2    := p*p,

           # Eat up the parallel (ParCellDMP) tag.
           tags  := Drop(t.getTags(), 1),
           #When(IsBound(tags[1].isEdgeTL), Error("Edge"), Print("")),

           [ [ TL(mnByv/p, m/p, 1, v).withTags(tags), TL(nByv, p, 1, (m*v)/p).withTags(tags), InfoNt(m,nByv,p,v) ] ]
       ),

       apply := (self, t, C, Nonterms) >> let(
           m      := Nonterms[3].params[1],
           nByv   := Nonterms[3].params[2],
           p      := Nonterms[3].params[3],
           v      := Nonterms[3].params[4],
           p2     := p*p,
           tags   := t.getTags(),

           # Hacking RulesTerm inside here was done by ff.
           #fl     := PTensor(paradigms.vector.rewrite.RulesTerm(C[1]), p),
           #fr     := PTensor(paradigms.vector.rewrite.RulesTerm(C[2]), p),

           #fl     := When(tags[1].leftEdge(),   LeftEdge(PTensor(C[1], p)), PTensor(C[1], p)),
           #fr     := When(tags[1].rightEdge(), RightEdge(PTensor(C[2], p)), PTensor(C[2], p)),

           # Works, but need to terminate blockvperm
           #Cond(tags[1].leftEdge(),  (LeftEdge(fl) * Comm_Cell(p, (m*nByv*v)/p2) * fr),
           #     tags[1].rightEdge(), (    fl  * Comm_Cell(p, (m*nByv*v)/p2) * RightEdge(fr)),
           #                          (    fl  * Comm_Cell(p, (m*nByv*v)/p2) *      fr ))

           frules := MergedRuleSet(RulesSums, RulesFuncSimp, KillSymSPL, RulesVec, RulesTerm, RulesPropagate),

           csums := frules(C[2].sums()),
           sg := ScatGath(fTensor(fId(csums.dimensions[1]/csums.v), fId(csums.v)), fTensor(csums.func, fId(csums.v)) ),
           sgnew := frules(sg.toloop(csums.v)),

           csums1 := frules(C[1].sums()),
           sg1 := ScatGath(fTensor(fId(csums1.dimensions[1]/csums1.v), fId(csums1.v)), fTensor(csums1.func, fId(csums1.v)) ),
           sgnew1 := frules(sg1.toloop(csums1.v)),

           fl     := When(tags[1].leftEdge(),
                     #PTensor(csums1, p),
                     PTensor(sgnew1, p),
                     PTensor(C[1], p)),

           fr     := When(tags[1].rightEdge(),
                     #PTensor(csums, p),
                     PTensor(sgnew, p),
                     PTensor(C[2], p)),

           #Error("BP0"),

           fl * Comm_Cell(p, (m*nByv*v)/p2) * fr

       )
   ) #TL_CellDMP
));


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

Import(paradigms.distributed);

# local functions and variables are prefixed with an underscore.

_WriteStub := function(code, opts)
    local outstr, s, stub, i, testvec, multiline;

    # build the generic stub info
    stub := CopyFields(rec(
        MEM_SIZE := When(IsBound(code.dimensions), EvalScalar(code.dimensions[1]) * 4, 0),
        DATATYPE := Concat("\"", DeriveScalarType(opts), "\""),
        DATATYPE_NO_QUOTES := DeriveScalarType(opts),
        PAGESIZE := 4096,
        INITFUNC := "init_sub",
        FUNC := "sub",
        DESTROYFUNC := "destroy_sub",
        DATATYPE_SIZEINBYTES := Cond(DeriveScalarType(opts) = "float", 4, Cond(DeriveScalarType(opts) = "double", 8, 0)),
        NUMTHREADS := When(IsBound(opts.smp) and IsInt(opts.smp.numproc), opts.smp.numproc, 1),
        RADIX :=      When(IsBound(opts.smp) and IsInt(opts.smp.numproc) and IsBound(code.dimensions), (code.dimensions[1]/(2 * opts.smp.numproc)), 2),
        QUICKVERIFIER := When(IsBound(opts.quickverifier), opts.quickverifier, "nulltransform")
    ));

    if IsBound(code.dimensions) then
        stub.ALLOCATE_MEMORY := "1";
        stub.ROWS := EvalScalar(code.dimensions[1]);
        stub.COLUMNS := EvalScalar(code.dimensions[2]);
    else
        stub.ALLOCATE_MEMORY := "0";
    fi;

    outstr := opts.unparser.generated_by;
    for s in UserRecFields(stub) do
        if s <> "operations" then
            if IsList(stub.(s)) and not IsString(stub.(s)) then
                outstr := Concat(outstr, "#define ", s, " { ");
                for i in [1..Length(stub.(s))-1] do
                   outstr := Concat(outstr, String(stub.(s)[i]), ", ");
                od;
                outstr := Concat(outstr, String(Last(stub.(s))), " }\n");
            else
                outstr := Concat(outstr, "#define ", s, " ", String(stub.(s)), "\n");
            fi;
        fi;
    od;

    Print(outstr);

    ##  add extern function declarations ... required for cuda
    Print("\nextern void INITFUNC();\n");
    Print("extern void DESTROYFUNC();\n");
    Print("extern void FUNC( ", DeriveScalarType(opts), " *out, ", DeriveScalarType(opts), " *in );\n");

	#add testvector if specified in opts

	if IsBound(opts.testvector) then
		testvec := opts.testvector;
		multiline := false;
		if not IsVector(testvec) then
			Error("opts.testvector must be a valid vector");
		fi;
		Print("\n\nstatic ", DeriveScalarType(opts), " testvector[] = {");
		if Length(testvec) > 10 then
			Print("\n    ");
		fi;
		for i in [1 .. Length(testvec)] do
			if i > 1 then
				Print(", ");
				if Mod(i, 10) = 1 then
					Print("\n    ");
					multiline := true;
				fi;
			fi;
			Print(testvec[i]);
		od;
		if multiline then
			Print("\n");
		fi;
		Print("};\n");
	fi;

end;

_MakeOutDirString := function(opts)
	local tmp;

	tmp := GetEnv("SPIRAL_TEMP_OUT_PATH");
	if (tmp = "") then
        tmp := "/tmp";
    fi;
    return Concat(
		Cond(IsBound(opts.outdir), opts.outdir, tmp),
		"/",
		String(GetPid()));
end;

#
## _MakeOutDir
#
# create the output directory.
#

_MakeOutDir := function(opts)
    local outdir;

    outdir := _MakeOutDirString(opts);

    if '~' in outdir or '$' in outdir then
        Error("No shell vars are allowed at the moment.");
    fi;

    MakeDir(outdir);

    return outdir;
end;

_CallProfiler := function(request, code, opts)
    local outdir, fullcmd, errorvalue, retval, target, outputFile, operations;

	if (request = "") then
		request := "time";
	fi;

    outdir := _MakeOutDir(opts);

    # output the spiral 'code' representation to the output dir.
    PrintTo(Concat(outdir, "/code.g"), code);

    # output the full options and profile info
    # PrintRec would be great if it didn't freeze the second time you call it for some reason
    if IsBound( opts.operations )  then
        operations := opts.operations;
    fi;
    Unbind(opts.operations);
    PrintTo(Concat(outdir, "/opts.g"), Print(opts));
    if IsBound( operations )  then
        opts.operations := operations;
    fi;

    # generate C code to testcode file
    PrintTo(Concat(outdir, "/testcode.c"), opts.unparser.gen("sub", code, opts));

    # write testcode.h
    PrintTo(Concat(outdir, "/testcode.h"), _WriteStub(code, opts));

    target := When(IsBound(opts.target), opts.target, rec());
    outputFile := Concat(outdir, "/", request, ".txt");
    fullcmd := Concat("spiralprofiler -d ", outdir);
    fullcmd := Concat(fullcmd, " -r ", request);

    if IsBound(target.forward) then
        fullcmd := Concat(fullcmd, " -f ", String(target.forward));
    fi;

    if IsBound(target.name) then
        fullcmd := Concat(fullcmd, " -t ", String(target.name));
    fi;

    if IsBound(target.prefix) then
        fullcmd := Concat(fullcmd, " -P ", String(target.prefix));
	else
		fullcmd := Concat(fullcmd, " -P ", String(GetPid()), "_");
    fi;

    # Exec the profiler
	# uncomment following line to hide profiler debug and error messages
	#fullcmd := Concat(fullcmd, " 2> NUL");

	PrintLine(fullcmd);

	errorvalue := IntExec(fullcmd);

    if (errorvalue <> 0) then
        Print("Profiler failed with error value ", errorvalue, ":\n", fullcmd, "\n");
        return 1e100;
    fi;

	retval := ReadVal(outputFile);

    return retval;
end;

## CMeasure(code, opts)
##
## Call profiler to time transform implemented by code

CMeasure := (code, opts) -> _CallProfiler("time", code, opts);

## CMatrix(code, opts)
##
## Call profiler to generate matrix equivalent of transform implemented by code

CMatrix := function(code, opts)
	local retmat;
	retmat := _CallProfiler("matrix", code, opts);
	return Cond(IsMat(retmat), TransposedMat(retmat), retmat);
end;

## CVector(code, vector, opts)
##
## Call profiler to apply transform implemented by code to vector

CVector := function (code, vector, opts)
	local retvec;
	opts.testvector := vector;
	retvec :=  _CallProfiler("vector", code, opts);
	return retvec;
end;

#F Find maximum memory requirement of all arrays
#F Used to determine memory arena size (for temp array reuse)
findMaxMemReq := function(c)
   local i, maxOfChildren, thisChild, myMem, arrays, other;

   if not IsRec(c) or not IsBound(c.rChildren) then
      return 0;
   fi;

   # Catch init functions and return 0
   if ObjId(c) = func and c.id = "init" then
      return 0;
   fi;

   # Determine the mem requirements of each of our children recursively, and
   # take the max of this.
   maxOfChildren := 0;
   for i in c.rChildren() do
      thisChild := findMaxMemReq(i);
      if (thisChild > maxOfChildren) then
         maxOfChildren := thisChild;
      fi;
   od;

   # Determine our own memory requirements (recursion leaf also)
   myMem := 0;
   if ObjId(c) = decl then
      [arrays, other] := SplitBy(c.vars, x->IsArray(x.t));
      for i in arrays do
        #HACK to exclude twiddles
        if i.id[1] <> 'D' then
           myMem := myMem + (i.t.size * When(IsBound(i.t.t) and ObjId(i.t.t)=TVect, i.t.t.size, 1));
        fi;
      od;
   fi;

   #Print(ObjId(c), " myMem = ", myMem, ". maxOfChildren = ", maxOfChildren, "\n");
   return(myMem + maxOfChildren);
end;

#F This function is exclusively for the Cell
BuildStubOpts := function (code, opts)
    local arena_size, dist_loops, mbuf_its, memloop_its, parallel_its;
    if IsBound(opts.useMemoryArena) and opts.useMemoryArena then
       arena_size := findMaxMemReq(code);
       if ObjId(arena_size) = Value then arena_size := arena_size.v; fi;
       opts.profile.stubopts.ARENA_SIZE := arena_size;
    else
       opts.profile.stubopts.ARENA_SIZE := 1;
    fi;

    # # For the Cell: add parallelization param to profile.
    # if IsBound(opts.spus) then
    #    opts.profile.stubopts.SPUS := opts.spus;
    # fi;
    # NOTE (clean this up)
    # Override spus with info extracted from code - for DP to work properly.
    dist_loops := Collect(code, dist_loop);
    if Length(dist_loops) >=1 then
       opts.profile.stubopts.SPUS := dist_loops[1].P;
    else
       opts.profile.stubopts.SPUS := 1;
    fi;

    mbuf_its := Collect(code, multibuffer_loop);
    if Length(mbuf_its) >=1 then
       # Write the loop with the smallest mbuf_its range to stub.h since the backend uses this value to divide and declare arrays
       opts.profile.stubopts.MULTIBUFFER_ITERATIONS :=  Minimum(List([1..Length(mbuf_its)], i->Length(mbuf_its[i].range)));
       # Write info on whether last mbuf stage is a ping or a pong (will result of computation end up in X or Y)?
       opts.profile.stubopts.MBUF_ENDS_IN_Y :=  Length(mbuf_its) mod 2;

       # We're probably doing internal streaming, so let the backend know of that.
       opts.profile.stubopts.INTERNAL_MULTIBUFFERING  := 1;
    else
       opts.profile.stubopts.MULTIBUFFER_ITERATIONS := 1;
       opts.profile.stubopts.MBUF_ENDS_IN_Y := 1;
    fi;

    memloop_its := Collect(code, mem_loop);
    if Length(memloop_its) >=1 then
       opts.profile.stubopts.VECTOR_IN_MEM := Length(memloop_its[1].range);
       opts.profile.stubopts.MBUF_ENDS_IN_Y :=  Length(memloop_its) mod 2;
    else
       if IsBound(opts.profile.stubopts.VECTOR_IN_MEM) then
        Unbind(opts.profile.stubopts.VECTOR_IN_MEM);
       fi;
    fi;

    parallel_its := Collect(code, @(1, func, e->ObjId(e.cmd)=dist_loop)); # Only checking for topmost level of parallelism
    if Length(parallel_its) >=1 then
       opts.profile.stubopts.PARALLEL_ITERATIONS := parallel_its[1].cmd.P;
    else
       opts.profile.stubopts.PARALLEL_ITERATIONS := 1;
    fi;
end;

_StandardMeasure := (code, opts) -> _CallProfiler("time", code, opts);

_StandardBuild := (code, opts) -> _CallProfiler("build", code, opts);

_StandardMeasureVerify := function(code, opts, makeTarget)
	#PrintLine("_StandardMeasureVerify(), makeTarget: \"", makeTarget, "\"");
	if (makeTarget = "") then
		makeTarget := "time";
	fi;
	return _CallProfiler(makeTarget, code, opts);
end;


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

Class(AsmX86Unparser, Unparser, rec(

    asmvolatile := meth(self, o)
        local oo,a,x;

	a := [];
        oo := self.preprocess(o);
	while(ObjId(oo)=data) do
	  oo.var.asmpseudoregister:=Length(a);
	  Append(a,[[oo.var,oo.value]]);
	  Print("static const double ",oo.var.id,"=",oo.value.v,";\n");
	  oo:=oo.cmd;
	 od;
        Print(
	    "asm volatile (\n",
            Unparse(oo, self, 4, 2),
	    ":: ");
	for x in a do
	   Print("\"m\"(",x[1].id,"), ");
	od;
	Print("\"eax\"(X), \"ecx\"(Y));");
    end,

    preprocess := (self, o) >> o,

    # NOTE: dynamically set mul and ofs based on X.t, Y.t
    #        note -- these are in bytes (8 bytes = 64 bits = double fp)
    #switched to double %% for asm volatile
    regmap := rec(
	S := rec(reg:="%%esp",ofs:=-8, mul:=-8),
	X := rec(reg:="%%eax",ofs:=0, mul:=8),
	Y := rec(reg:="%%ecx",ofs:=0, mul:=8),

	A := rec(reg:="%%eax",ofs:=0, mul:=16),
	B := rec(reg:="%%ebx",ofs:=0, mul:=8),
	C := rec(reg:="%%ecx",ofs:=0, mul:=16),

	r0 := rec(reg:="%%xmm0"),
	r1 := rec(reg:="%%xmm1"),
	r2 := rec(reg:="%%xmm2"),
	r3 := rec(reg:="%%xmm3"),
	r4 := rec(reg:="%%xmm4"),
	r5 := rec(reg:="%%xmm5"),
	r6 := rec(reg:="%%xmm6"),
	r7 := rec(reg:="%%xmm7"),
	r8 := rec(reg:="%%xmm8"),
	r9 := rec(reg:="%%xmm9"),
	r10 := rec(reg:="%%xmm10"),
	r11 := rec(reg:="%%xmm11"),
	r12 := rec(reg:="%%xmm12"),
	r13 := rec(reg:="%%xmm13"),
	r14 := rec(reg:="%%xmm14"),
	r15 := rec(reg:="%%xmm15"),

	A0 := rec(reg:="%%xmm8"),
	A1 := rec(reg:="%%xmm9"),
	A2 := rec(reg:="%%xmm10"),
	A3 := rec(reg:="%%xmm11"),
	A4 := rec(reg:="%%xmm12"),
	A5 := rec(reg:="%%xmm13"),
	A6 := rec(reg:="%%xmm14"),
	A7 := rec(reg:="%%xmm15"),
    ),

    startFunc := meth(self, fname, stackofs, loadptrs)
        local v, i;
        PrintLine(".globl ", fname);
#	PrintLine("    .def  _", fname, "; .scl 2; .type 32; .endef");
        PrintLine("    .type  ", fname, ", @function");
	PrintLine("", fname, ":");

	# load arguments into registers
	for i in [1..Length(loadptrs)] do
	    v := loadptrs[i];
	    PrintLine("    movl  ", (stackofs+i-1)*4, "(%esp), ", self.regmap.(v.id).reg);
	od;
    end,

    header_top := meth(self, subname, o)
        local precomputed_data;
	PrintLine("    .data");
	PrintLine("    .align 8");
        precomputed_data := List(Collect(o, data), x->[x.var, x.value]);
	DoForAll(precomputed_data, d -> self.genData(d[1], d[2]));
	PrintLine("");
	PrintLine("    .text");
	PrintLine("    .align 8");
    end,

    header_func := (self, subname, o) >> self.startFunc(subname, 1, [Y, X]),

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

    footer := meth(self, subname, o)
        local init, loopvars;
	PrintLine("    ret");
	PrintLine("");
	self.startFunc(Concat("init_", subname), 0, []);
	PrintLine("    ret");
    end,

    genData := (self, v, val) >> Cond(
	val.t = TDouble, PrintLine(v, ": .double ", val.v),
	val.t = TInt,    PrintLine(v, ": .long ", val.v),
	IsArray(val.t) and val.t.t = TDouble,
	    val.t.t=TDouble, PrintLine(v, ": .double ", PrintCS(val.v)),
	IsArray(val.t) and val.t.t = TInt,
	    val.t.t=TDouble, PrintLine(v, ": .long ", PrintCS(val.v)),
        Error("Can't handle type ", val.t)),

    ###################
    # Helpers
    ###################

#HERE is a HACK to print correct GCC intrincsics
#old code is commented out
    instr2 := (self, mnem, src, dest) >>
#        Print("    ", mnem, "  ", self(src,0,0), ", ", self(dest,0,0), "\n"),
        Print("\"    ", mnem, "  ", self(src,0,0), ", ", self(dest,0,0), "\\n\\t\"\n"),

    isReg := (self, loc) >> IsVar(loc) and IsBound(self.regmap.(loc.id)),

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

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

    # just sequence them
    #removed the chain label
#    chain := (self,o,i,is) >> Print("chain_", BagAddr(o), ":\n",
#	DoForAll(o.cmds, c -> self(c, i, is))),
    chain := (self,o,i,is) >> Print( DoForAll(o.cmds, c -> self(c, i, is))),

    # all datas are handled in the header, just proceed to children
    data := (self,o,i,is) >> self(o.cmd, i, is),

    # nothing need to be declared in assembly, we just have registers and stack space
    decl := (self,o,i,is) >> self(o.cmd, i, is),

    # on Pentium M using sd seems to be more efficient
    assign     := (self,o,i,is) >> let(
	sfx := When(self.isReg(o.exp) and self.isReg(o.loc), "sd", "sd"), # apd
	self.instr2(Concat("mov", sfx), o.exp, o.loc)),

    assign_add := (self,o,i,is) >> let(
	sfx := When(self.isReg(o.exp) and self.isReg(o.loc), "sd", "sd"),
	self.instr2(Concat("add",sfx), o.exp, o.loc)),

    assign_sub := (self,o,i,is) >> let(
	sfx := When(self.isReg(o.exp) and self.isReg(o.loc), "sd", "sd"),
	self.instr2(Concat("sub", sfx), o.exp, o.loc)),

    assign_mul := (self,o,i,is) >> let(
	sfx := When(self.isReg(o.exp) and self.isReg(o.loc), "sd", "sd"),
	self.instr2(Concat("mul", sfx), o.exp, o.loc)),

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

    Exp := (self,o,i,is) >> Error("X86 Assembly backend expects 2-operand low-level representation, it must not have expressions, so I can't handle <o>"),

    dup := (self,o,i,is) >>
        Print( "shufpd(", self.regmap.(o.args[1].id).reg, ")"),

    nth := (self,o,i,is) >> Checked(IsValue(o.idx),
	let(d := self.regmap.(o.loc.id),
	    Print( (o.idx.v * d.mul + d.ofs), "(", d.reg, ")"))),

    var := (self,o,i,is) >> When(IsBound(self.regmap.(o.id)),
	Print(self.regmap.(o.id).reg),
	When(IsBound(o.asmpseudoregister),Print("%",o.asmpseudoregister),
	Print(o.id))),

    Value := CUnparser.Value
));


DK1 schwarz,1,Projekt		//creates kone
  S(1,1,2)			//rediefine edges as (r,r,h)


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

ImportAll(paradigms.smp);
ImportAll(paradigms.vector);

Class(IAGlobals, rec(
    getOpts := meth(arg)
        local opts, self, smpopts, isa, sseopts, optrec, tid;

        #   set up defaults
        optrec := rec(
            cpu := LocalConfig.cpuinfo,
            useSIMD := true,
            useSMP := true,
            useNewSettingsForICC := false,
            dataType := T_Real(64),
            globalUnrolling := 128,
            useArea := false,
            processIntTables := false,
            use64bit := LocalConfig.osinfo.is64bit()
        );

        smpopts := rec(
                numproc := LocalConfig.cpuinfo.cores,
                api := "OpenMP"
        );

        sseopts := rec(
            svct:=true,
            splitL:=false,
            oddSizes := false,
            stdTTensor := true,
            tsplPFA := false
        );

        self := arg[1];
        arg := Flat(Drop(arg, 1));

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

        if optrec.useSIMD then
            #   handle SSE
            #   let SSE work the unrolling magic
            sseopts.globalUnrolling := optrec.globalUnrolling;
            sseopts.useArea := optrec.useArea;
            sseopts.processIntTables := optrec.processIntTables;
            if IsBound(optrec.mode) then sseopts.mode := optrec.mode; fi;

            isa := optrec.cpu.getSimdIsa(optrec.dataType);
            opts := CopyFields(SIMDGlobals.getOpts(isa, sseopts), rec(
                IAconf := rec(
                    optrec := optrec,
                    smpopts := smpopts,
                    sseopts := sseopts
                ),

                unparser := When(smpopts.api = "OpenMP",
                    When(isa in [AVX_8x32f, AVX_4x64f],
                        When(IsBound(smpopts.OmpMode) and smpopts.OmpMode = "for",
                            spiral.libgen.OpenMP_AVXUnparser_ParFor,
                            spiral.libgen.OpenMP_AVXUnparser),
                        When(IsBound(smpopts.OmpMode) and smpopts.OmpMode = "for",
                            spiral.libgen.OpenMP_SSEUnparser_ParFor,
                            spiral.libgen.OpenMP_SSEUnparser)),
                    spiral.libgen.SMP_SSEUnparser),
                codegen := spiral.libgen.VecRecCodegen
            ));

            if optrec.useNewSettingsForICC then
                if IsBound(opts.language) then Unbind(opts.language); fi;
                if LocalConfig.osinfo.isLinux() then
                    if optrec.use64bit then
                        opts.profile := default_profiles.linux_x64_icc;
                    else
                        opts.profile := default_profiles.linux_x86_icc;
                    fi;
                elif LocalConfig.osinfo.isDarwin() then
                    Error("No Darwin SMP profiles defined yet");
                elif LocalConfig.osinfo.isWindows() then
                    if optrec.use64bit then
                        opts.profile := default_profiles.win_x64_icc;
                    else
                        opts.profile :=default_profiles.win_x86_icc;
                    fi;
                else
                    Error("Unknow OS");
                fi;
            fi;

        else
            if optrec.useSMP then Error("scalar SMP not implemented"); fi;

            opts := Copy(SpiralDefaults);

            opts.globalUnrolling := optrec.globalUnrolling;
            opts.useArea := optrec.useArea;
            opts.processIntTables := optrec.processIntTables;

            if optrec.dataType = T_Real(32) then
                opts := InitDataType(opts, "f32re");
            elif optrec.dataType = T_Real(64) then
                opts := InitDataType(opts, "f64re");
            else
                Error("unknown data type");
            fi;

            if optrec.useNewSettingsForICC then
                if IsBound(opts.language) then Unbind(opts.language); fi;
                if LocalConfig.osinfo.isLinux() then
                    if optrec.use64bit then
                        opts.profile := default_profiles.linux_x64_icc;
                    else
                        opts.profile := default_profiles.linux_x86_icc;
                    fi;
                elif LocalConfig.osinfo.isDarwin() then
                    Error("No Darwin SMP profiles defined yet");
                elif LocalConfig.osinfo.isWindows() then
                    if optrec.use64bit then
                        opts.profile := default_profiles.win_x64_icc;
                    else
                        opts.profile :=default_profiles.win_x86_icc;
                    fi;
                else
                    Error("Unknow OS");
                fi;
            fi;

            if optrec.useArea then
                opts.globalUnrolling := 2 * opts.globalUnrolling; # * Log2Int(opts.globalUnrolling);
                opts.markBlock := MarkBlocksAreaSums;
            fi;

            return opts;
        fi;

        # handle the SMP pthreads/OpenMP case
        if optrec.useSMP then
            opts.breakdownRules.GT := [ GT_Base, GT_NthLoop,
                CopyFields(GT_Par, rec(parEntireLoop := false, splitLoop := true)), GT_Par_odd,
                GT_Vec_AxI, GT_Vec_IxA, GT_Vec_IxA_Push, GT_Vec_IxA_L, GT_Vec_L_IxA
            ];
            opts.breakdownRules.TTensorI := Concat([
            CopyFields(TTensorI_toGT, rec(
                applicable := (self, t) >> t.hasTags() and ObjId(t.getTags()[1])=AParSMP ))],
                opts.breakdownRules.TTensorI);

            opts.breakdownRules.TTensorInd :=
                Concat([dsA_base_smp, dsA_smp, L_dsA_L_base_smp, L_dsA_L_smp],
                    opts.breakdownRules.TTensorInd);

            tid := When(smpopts.api = "OpenMP", threadId(), var("tid", TInt));
            opts.tags := Concat([ AParSMP(smpopts.numproc, tid)  ], opts.tags);

            if optrec.useNewSettingsForICC then
                if IsBound(opts.language) then Unbind(opts.language); fi;
                if LocalConfig.osinfo.isLinux() then
                    if optrec.use64bit then
                        opts.profile := When(smpopts.api = "OpenMP", default_profiles.linux_x64_icc_openmp, default_profiles.linux_x64_threads);
                    else
                        opts.profile := When(smpopts.api = "OpenMP", default_profiles.linux_x86_icc_openmp, default_profiles.linux_x86_threads);
                    fi;
                elif LocalConfig.osinfo.isDarwin() then
                    Error("No Darwin SMP profiles defined yet");
                elif LocalConfig.osinfo.isWindows() then
                    if optrec.use64bit then
                        opts.profile := When(smpopts.api = "OpenMP", default_profiles.win_x64_icc_openmp, default_profiles.win_x64_icc_threads);
                    else
                        opts.profile := When(smpopts.api = "OpenMP", default_profiles.win_x86_icc_openmp, default_profiles.win_x86_icc_threads);
                    fi;
                else
                    Error("Unknow OS");
                fi;
            else
                opts.language := When(smpopts.api = "OpenMP", optrec.cpu.OpenMP_lang, optrec.cpu.smp_lang);
            fi;

            if smpopts.api = "threads" then opts.subParams := [var("num_threads", TInt), var("tid", TInt)]; fi;
            opts.smp := smpopts;
        fi;

        Add(opts.includes, "<include/mm_malloc.h>");
        if not IsBound(opts.globalUnrolling) then opts.globalUnrolling := optrec.globalUnrolling; fi;
        opts.operations := rec(Print := (s) -> Print("<IA options>"));
        return opts;
    end
));