text:
# 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 ) ]);


text:
LowercaseString("alphaBETA");
UppercaseString("alphaBETA");

ext: gap

max_issues_repo_name: seanwallawalla-forks/RosettaCodeData

max_forks_repo_name: seanwallawalla-forks/RosettaCodeData


text:
ack := function(m, n)
  if m = 0 then
    return n + 1;
  elif (m > 0) and (n = 0) then
    return ack(m - 1, 1);
  elif (m > 0) and (n > 0) then
    return ack(m - 1, ack(m, n - 1));
  else
    return fail;
  fi;
end;


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

UnifyPair := (aa,bb) -> let(
    # NOTE: this converts 2->TInt, this is a hack
    a := Cond(IsInt(aa) or IsSymbolic(aa), TInt, ObjId(aa)=TFunc, _convType(aa), aa),
    b := Cond(IsInt(bb) or IsSymbolic(bb), TInt, ObjId(bb)=TFunc, _convType(bb), bb),
    aid := ObjId(a),    bid := ObjId(b),  avec := IsVecT(aa), bvec := IsVecT(bb),
    Cond(

     # YSV: this handles subtraction of pointers (=integer index)
     #      addition of pointers is not valid
#     aid=TPtr and bid=TPtr, TInt,

     a=b, a,
     a = TUnknown and b <> TUnknown, b,
     b = TUnknown and a <> TUnknown, a,

#     aid in [TPtr, TArray] and (b=TInt or bid in [T_Int, T_UInt]), TPtr(a.t, When(aid=TArray, [], a.qualifiers), When(aid=TArray, ptrAligned, a.alignment)),
#     bid in [TPtr, TArray] and (a=TInt or aid in [T_Int, T_UInt]), TPtr(b.t, When(bid=TArray, [], b.qualifiers), When(bid=TArray, ptrAligned, b.alignment)),
     #NOTE: this looks like an horrible hack but how else can it be done?
     #isn't this a fundamental weakness of C/C++ ?

#     aid=TPtr and bid=TArray, a,
#     aid=TArray and bid=TPtr, b,

     avec and bvec and a.size<>b.size,     TVect(UnifyPair(a.t, b.t), Maximum(a.size, b.size)),
     avec and bvec, Checked(a.size=b.size, TVect(UnifyPair(a.t, b.t), a.size)),
     avec, TVect(UnifyPair(a.t, b), a.size),
     bvec, TVect(UnifyPair(a, b.t), b.size),

     aid=TFixedPt and bid=TFixedPt,
        When(a = b, a, Error("Can't unify fixed point types of different bit width")),

     a=TComplex and b in [TReal, TComplex, TInt, TUInt], TComplex,
     b=TComplex and a in [TReal, TComplex, TInt, TUInt], TComplex,

     (aid=T_Real or a=TReal) and (bid in [T_UInt, T_Int] or b in [TReal, TInt, TUInt]), a,
     (bid=T_Real or b=TReal) and (aid in [T_UInt, T_Int] or a in [TReal, TInt, TUInt]), b,

     a=TInt and b in  [TInt,TBool,TUInt], TInt,
     a in [TInt,TUInt,TBool] and b=TInt,  TInt,

     aid=T_Complex and b in [TComplex, TReal, TInt], a,
     bid=T_Complex and a in [TComplex, TReal, TInt], b,

     aid in [T_Real, T_Int] and b=TComplex, T_Complex(a),
     bid in [T_Real, T_Int] and a=TComplex, T_Complex(b),

     aid=T_Complex and bid=T_Complex, T_Complex(UnifyPair(a.params[1], b.params[1])),
     aid=T_Complex and bid in [TFixedPt, T_Real, T_UInt, T_Int],
         T_Complex(UnifyPair(a.params[1], b)),
     bid=T_Complex and aid in [TFixedPt, T_Real, T_UInt, T_Int],
         T_Complex(UnifyPair(a, b.params[1])),

     a=TComplex and bid = TFixedPt, T_Complex(b),
     b=TComplex and aid = TFixedPt, T_Complex(a),

     aid=T_Real and bid=T_Real, T_Real(Maximum(a.params[1], b.params[1])),

     aid in [T_Int, T_UInt] and b in [TInt, TUInt], a,
     a in [TInt, TUInt] and bid in [T_Int, T_UInt], b,

     aid=T_UInt and bid=T_UInt, T_UInt(Maximum(a.params[1], b.params[1])),
     aid in [T_Int, T_UInt] and bid in [T_Int, T_UInt], T_Int(Maximum(a.params[1], b.params[1])),

     aid=TFixedPt, a,
     bid=TFixedPt, b,

     a=TDummy or b=TDummy, TDummy,

     IsArrayT(a) and IsArrayT(b), Checked(a.size=b.size, TArray(UnifyPair(a.t, b.t), a.size)),
     IsArrayT(a), TArray(UnifyPair(a.t, b), a.size),
     IsArrayT(b), TArray(UnifyPair(a, b.t), b.size),

     IsBound(a.unifyWith), a.unifyWith(b),
     IsBound(b.unifyWith), b.unifyWith(a),

     #MRT handles addition/subtraction of pointers and ints.
     aid = TPtr and b = TInt, b,
     a = TInt and bid = TPtr, a,
     #MRT END

     Error("Can't unify ", a, " and ", b)));

#F UnifyTypes(<types>) - given a list of types returns a
#F   most general type
#F   NOTE: complete this.
#F
UnifyTypes := function(types)
    local t, l, i, a, b;
    l := Length(types);

    if   l=0 then return TUnknown;
    elif l=1 then return types[1];
    fi;

    [i, t] := [2, types[1]];
    while i <= l do
        t := UnifyPair(t, types[i]);
	i := i+1;
    od;
    return t;
end;

UnifyTypesL := function(args)
    local t, l, i, a, b;
    l := Length(args);

    if   l=0 then return TUnknown;
    elif l=1 then return args[1].t;
    fi;

    [i, t] := [2, args[1].t];
    while i <= l do
        t := UnifyPair(t, args[i].t);
	i := i+1;
    od;
    return t;
end;

Declare(InferType);

UnifyTypesV := values -> UnifyTypes(List(values, InferType));

InferType := v -> let(gt := BagType(v),
   Cond(
       IsInt(v),
           TInt,
       gt = T_RAT or gt = T_DOUBLE,
           TReal,
       gt = T_CPLX,
           TComplex,
       gt = T_CYC,
           When(Im(v)=0, TReal, TComplex),
       gt = T_CHAR,
           TChar,
       gt = T_BOOL,
           TBool,
       IsString(v),
           TString,
       IsExp(v) or IsValue(v),
           When(IsBound(v.t), v.t, TUnknown),

       gt = T_RANGE, Checked(Length(v) >= 0,
       TArray(TInt, Length(v))),

       IsList(v), Checked(Length(v) >= 0,
       TArray(UnifyTypes(List(v, InferType)), Length(v))),

       Error("Can't infer the type of ", v)));

#NOTE: when 'v' is a list with expressions inside it will create TArray value which is wrong
V := v -> Cond(IsValue(v), v,
               IsExp(v), v.eval(),
               InferType(v).value(v));

ext: gi


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

_IsCompute  := (asgn) >> (IsBound(asgn.isCompute) and asgn.isCompute);
_IsLoad  := (asgn) >> (IsBound(asgn.isLoad) and asgn.isLoad);
_IsStore  := (asgn) >> (IsBound(asgn.isStore) and asgn.isStore);

#NOTE: Need a much better marking heuristic
MarkSWPLoops := function(loop)
  # Obviously, we can't sw-pipeline if we don't have adequate iterations
  if Length(loop.range) >=4 then
    # Current heuristic: Mark loops where the loop body has at least 10 commands.
    if IsBound(loop.cmd) and IsBound(loop.cmd.cmd) and IsBound(loop.cmd.cmd.cmds) and Length(loop.cmd.cmd.cmds) >= 10 then
      return(loop_sw(loop.rChildren()[1], loop.rChildren()[2], loop.rChildren()[3]));
    else
      return(loop);
    fi;
  else
    return(loop);
  fi;
end;

MarkLoadinPreds := function(asgn)
  local i;
   if not _IsCompute(asgn) then # Mark as load
      asgn.isLoad := true;
   fi;

   # Mark all predecessors as loads
   if IsBound(asgn.loc) and IsBound(asgn.loc.pred) then
     for i in [1..Length(asgn.loc.pred)] do
        if IsBound(asgn.loc.pred[i].def) then
           MarkLoadinPreds(asgn.loc.pred[i].def);
        fi;
     od;
   fi;
end;

MarkStoreinPreds := function(asgn)
  local i;
   if not _IsCompute(asgn) then # Mark as store
      asgn.isStore := true;

     # Mark all predecessors as stores (stops at computes)
     for i in [1..Length(asgn.loc.pred)] do
        if IsBound(asgn.loc.pred[i].def) then
           MarkStoreinPreds(asgn.loc.pred[i].def);
        fi;
     od;
   fi;
end;

MarkAllSucceedingLoadsAsComputes := function(asgn)
  local i;
  # Mark all successors that are loads as computes
  #
  for i in [1..Length(asgn.loc.succ)] do
     if IsBound(asgn.loc.succ[i].def) and _IsLoad(asgn.loc.succ[i].def) then
       #Error("Aha!");
       asgn.loc.succ[i].def.isLoad := false;
       asgn.loc.succ[i].def.isCompute := true;
     fi;
  od;

end;

SubstLoopVarCopy := function(asgns, loopvar, value)
   local retval, i;
   retval := Copy(asgns);
   for i in retval do
      SubstVars(i, rec((loopvar.id) := value));
   od;
   return(retval);
end;

SoftwarePipeline := function(lsw)
local alllsws, allasgns, loads, stores, loopvar, n1, n2, computes, isCompute, arg, asgn, asgns, g0, g1, c0, sn2, sn1, cn1, bodyload, vars, computeCount;
# Collect all loops to be software pipelined

   # Mark all computes
   computes := Collect(lsw, [assign, ..., @(2, [add, mul, sub]), ...]);

   #Error("BP");

   # For each compute: kick out all those that don't have a TReal type for all exp.arg
   # NOTE: Include TVects in TReal. NOTE: Won't work for code where loopvar datatype = compute datatype
   computeCount := 0;
   for asgn in computes do
      isCompute := true;
      for arg in asgn.exp.args do
       if arg.t <> TReal and arg.t.__name__ <> "TVect" then
          isCompute := false;
       fi;
      od;

      if (isCompute) then
         asgn.isCompute := true;
         computeCount := computeCount + 1;
         #Print(".");
      fi;
   od;

   # Heuristic: if there're no computes, this is probably a huge permute (load or
   # store) block, and should not be sw-pipelined.

   if computeCount = 0 then
     return(loop(lsw.rChildren()[1], lsw.rChildren()[2], lsw.rChildren()[3]));
   fi;

   #Error("BP");

   # Mark all Loads
   # Loads are all predcessors of all the computes
   asgns := Collect(lsw, assign);
   for asgn in asgns do
      if IsBound(asgn.isCompute) and asgn.isCompute then
         #Print(asgn);
         MarkLoadinPreds(asgn);
      fi;
   od;

   #Error("BP");

   # HACK: must really check to ensure this is a store!
   asgns := Collect(lsw, assign);
   for asgn in asgns do
      if not _IsCompute(asgn) and not _IsLoad(asgn) then
         asgn.isStore := true;
         MarkStoreinPreds(asgn);
         #NOTE: the loopvar assign should also be a store
      fi;
   od;

   # Mark loads which are to be marked as computes because they are
   # both preceded by and succeeded by a compute

   # For each compute, find all successors that are loads, and mark them as
   # computes.

   asgns := Collect(lsw, assign);
   for asgn in asgns do
      if IsBound(asgn.isCompute) and asgn.isCompute then
         MarkAllSucceedingLoadsAsComputes(asgn);
      fi;
   od;

   # Now, change the loop_sw to a software pipelined loop
   # Cmds should be strictly in order.

   # Make a list of loads, computes, and stores
   loads    := [];
   computes := [];
   stores   := [];
   allasgns := Collect(lsw, chain)[1].cmds;
   for asgn in allasgns do
      if _IsLoad(asgn) then
         loads := Concatenation(loads, [asgn]);
      fi;
      if _IsCompute(asgn) then
         computes := Concatenation(computes, [asgn]);
      fi;
      if _IsStore(asgn) then
         stores := Concatenation(stores, [asgn]);
      fi;
   od;

   #Error("BP");

   loopvar := lsw.var;
   n1 := Length(lsw.range)-1;
   n2 := n1-1;

   g0  := SubstLoopVarCopy(loads, loopvar, V(0));
   g1  := SubstLoopVarCopy(loads, loopvar, V(1));
   c0  := Copy(computes);

   sn2 := SubstLoopVarCopy(stores, loopvar, V(n2));
   sn1 := SubstLoopVarCopy(stores, loopvar, V(n1));
   cn1 := Copy(computes);

   bodyload  := SubstLoopVarCopy(loads,  loopvar, add(loopvar, V(2)));
   #prologue := chain(g0,   c0,  g1); #epilogue := chain(sn2, cn1, sn1);

   vars := Collect(lsw, decl)[1].vars;
   lsw := decl(vars, chain(g0, c0, g1, loop(loopvar, n2, chain(stores, c0, bodyload)), sn2, cn1, sn1));
   #Print(lsw);
   return(lsw);
end;


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

# assuming the angles 2pi*a
alpha2pi := (n,i,a) -> Cond(i mod 2 = 0, (a + Int(i/2))/n, (1-a + Int(i/2))/n);

Mat_rDFT := (N,a,k) -> let(n:=N/2,
     ConcatList([0..n-1], i -> let(aa := alpha2pi(n,i,a), [
        Concatenation(     List([0..n-1], j -> cospi(k*2*j*aa)),
                    (-1)^i*List([0..n-1], j -> -sinpi(k*2*j*aa))),
        Concatenation(     List([0..n-1], j -> sinpi(k*2*j*aa)),
                    (-1)^i*List([0..n-1], j -> cospi(k*2*j*aa))) ])));

Mat_nrDFT := (N,a,k) -> let(n:=N/2,
     ConcatList([0..n-1], i -> let(
        raw := (i+a)/n,
        sign := When(raw > 1/2, -1, 1) * When(a > 1/2, -1, 1),
        aa := When(raw > 1/2, 1-raw, raw), [
        Concatenation(     List([0..n-1], j -> cospi(k*2*j*aa)),
                      sign*List([0..n-1], j -> -sinpi(k*2*j*aa))),
        Concatenation(     List([0..n-1], j -> sinpi(k*2*j*aa)),
                      sign*List([0..n-1], j -> cospi(k*2*j*aa))) ])));

_row_BRDFT_13 := (n,u,k) -> let(s := 1/sinpi(k*2*u),
   [ List([0..n-1], j -> -s*sinpi(k*2*u*(j-1))),
     List([0..n-1], j ->  s*sinpi(k*2*u*j)) ]);

# scaled = 2*cospi(u) * unscaled
# since 2*cospi(u) / sinpi(2*u) = 1/sinpi(u)
#
_row_BRDFT_24 := (n,u, k,scaled) -> let(s := When(scaled, 1/sinpi(k*u), 1/sinpi(k*2*u)),
   [ List([0..n-1], j -> -s*sinpi(k*2*u*(j-1/2))),
     List([0..n-1], j ->  s*sinpi(k*2*u*(j+1/2))) ]);

# F.T. for the polynomial algebra below, basis in time = (1, x, ..., x^(n-1))
#   R[x] / x^n - 1
#
Mat_BRDFT1 := (n,k) -> When(IsEvenInt(n),
   Concatenation(
       [List([0..n-1], i-> 1)],
       [List([0..n-1], i-> (-1)^i)],
       ConcatList([1..n/2-1], i -> _row_BRDFT_13(n, i/n, k))),
   Concatenation(
       [List([0..n-1], i-> 1)],
       ConcatList([1..(n-1)/2], i -> _row_BRDFT_13(n, i/n, k)))
);

Mat_BRDFT1U := (n,k) -> When(IsOddInt(n), Mat_BRDFT1(n),
   Concatenation(
       [List([0..n-1], i-> When(IsEvenInt(i), 1, 0))],
       [List([0..n-1], i-> When(IsEvenInt(i), 0, 1))],
       ConcatList([1..n/2-1], i -> _row_BRDFT_13(n, i/n, k))));

# -- i1 dst1 / dst1 i1
# -- i1 dst5 / dst5

# F.T. for the polynomial algebra below, basis in time = (x^(1/2),x^(3/2),..., x^(n-1/2))
#   R[x] / x^n - 1
#
Mat_BRDFT2 := (n,k,scaled) -> When(IsEvenInt(n),
   Concatenation(
       [List([0..n-1], i-> 1)],
       [List([0..n-1], i-> (-1)^i)],
       ConcatList([1..n/2-1], i -> _row_BRDFT_24(n, i/n, k, scaled))),
   Concatenation(
       [List([0..n-1], i-> 1)],
       ConcatList([1..(n-1)/2], i -> _row_BRDFT_24(n, i/n, k, scaled))));
# - dct2/dst2 dst2/dst2 - breaks?
# - i1 dst6 dst6 -- ok
# - i1 dst5 dst5 -- ok

# F.T. for the polynomial algebras below, basis in time = (1,x,..., x^(n-1))
# u = 1/4, n - even or odd
#   R[x] / x^n + 1
#
# u <> 1/4, n - even ("skew" BRDFT3)
#   R[x] / x^n - 2cos(2*pi*u) x^(n/2) + 1
#
Mat_BRDFT3 := (n, u, k) -> When(IsEvenInt(n),
   ConcatList([0..n/2-1], i -> _row_BRDFT_13(n, 2*alpha2pi(n, i, u), k)),
   Concatenation(
       ConcatList([0..(n-3)/2], i -> _row_BRDFT_13(n, 2*alpha2pi(n, i, u), k)),
       [List([0..n-1], i-> (-1)^i)])
);
# dst3/dst3

# F.T. for the polynomial algebras below, basis in time = (x^(1/2),x^(3/2),..., x^(n-1/2))
# u = 1/4, n - even or odd
#   R[x] / x^n + 1
#
# u <> 1/4, n - even ("skew" BRDFT4)
#   R[x] / x^n - 2cos(2*pi*u) x^(n/2) + 1
#
Mat_BRDFT4 := (n, u, k, scaled) -> When(IsEvenInt(n),
   ConcatList([0..n/2-1], i -> _row_BRDFT_24(n, 2*alpha2pi(n, i, u), k, scaled)),
   Concatenation(
       ConcatList([0..(n-3)/2], i -> _row_BRDFT_24(n, 2*alpha2pi(n, i, u), k, scaled)),
       [List([0..n-1], i-> (-1)^i)])
);
# dst4/dst4 -- scale(brdft4)

# spiral> $ * scale(Mat_BRDFT4(16,1/4));
# [ [ 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ],
#   [ 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1 ],
#   [ 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0 ],
#   [ 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0 ],
#   [ 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0 ],
#   [ 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0 ],
#   [ 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0 ],
#   [ 0, 0, 0, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, 0, 0 ],
#   [ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 ],
#   [ 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 ],
#   [ 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 ],
#   [ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ],
#   [ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 ],
#   [ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ],
#   [ 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 ],
#   [ 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0 ] ]

Class(Base_BRDFT, NonTerminal, TaggedObjectMixin, rec(
   isReal := True,
   dims := self >> let(n:=self.params[1], [n, n]),
   print := (self, i, is) >> let(base_print := NonTerminal.print,
       Print(base_print(self, i, is),
           When(IsBound(self.tags), Print(".withTags(", self.tags, ")"))))
));

Class(rDFT, Base_BRDFT, rec(
   abbrevs := [ (n)       -> Checked(IsPosIntSym(n), [n, 1/4, 1]),
                (n,a,rot) -> Checked(IsPosIntSym(n), IsRatSym(a), IsIntSym(rot),
                                     IsSymbolic(rot) or Gcd(n,rot)=1,
                    let(aa := When(IsRat(a) and a>1/2, 1-a,a), [n, aa, rot])) ],

   terminate := self >> Mat(Mat_rDFT(self.params[1], EvalScalar(self.params[2]), self.params[3])),
   hashAs := self >> ObjId(self)(self.params[1]).withTags(self.getTags()),
   HashId := self >> Concatenation([self.params[1]], self.getTags())
));

# BRDFT1(<n>) - real DFT of type 1 non-terminal
#  Freq. basis :
#      (1), (1,x)*, (1)
#  Time basis :
#      (1, x, ..., x^(n-1))
#  Polynomial algebra :
#      R[x] / x^n - 1
#
Class(BRDFT1, Base_BRDFT, rec(
   abbrevs := [ (n)   -> Checked(IsPosIntSym(n), [n, 1]),
                (n,rot) -> Checked(IsPosIntSym(n), IsIntSym(rot), [n, rot]) ],
   terminate := self >> let(mat := Mat(Mat_BRDFT1(self.params[1], EvalScalar(self.params[2]))),
       When(self.transposed, mat.transpose(), mat))
));

# Same as BRDFT1 but does not decompose polynomial algebra completely,
# keeping 1D spectral components fused (ie first spectral component is
# R[x]/x^2-1), with basis (1,x) as in all other components.
#
Class(UBRDFT1, Base_BRDFT, rec(
   abbrevs := [ (n)   -> Checked(IsPosIntSym(n), [n, 1]),
                (n,rot) -> Checked(IsPosIntSym(n), IsIntSym(rot), [n, rot]) ],
   terminate := self >> Mat(Mat_BRDFT1U(self.params[1], EvalScalar(self.params[2])))
));

# BRDFT3(<n>) - real DFT of type 3 non-terminal
#  Freq. basis :
#      (1), (1,x)*, (1)
#  Time basis :
#      (1, x, ..., x^(n-1))
#  Polynomial algebra :
#      R[x] / x^n + 1
#
Class(BRDFT3, Base_BRDFT, rec(
   abbrevs := [ (n)   -> Checked(IsPosIntSym(n),           [n, 1/4, 1]),
                (n,a) -> Checked(IsPosIntSym(n), IsRatSym(a),
                    let(aa := When(IsRat(a) and a>1/2, 1-a,a), [n, aa, 1])),
                (n,a, rot) -> Checked(IsPosIntSym(n), IsRatSym(a), IsIntSym(rot),
                    let(aa := When(IsRat(a) and a>1/2, 1-a,a), [n, aa, rot])) ],

   terminate := self >> Mat(Mat_BRDFT3(self.params[1], EvalScalar(self.params[2]), EvalScalar(self.params[3]))),
   hashAs := self >> ObjId(self)(self.params[1]),
));

# -----------------------------------
# Base changes and older misc stuff
# -----------------------------------

bblock := a -> let(aa := -2*cospi(2*a), [[-1,  aa], [-aa, aa^2-1]]);

bruun2 := (m,a) -> Checked((m mod 4)=0, let(
     a12 := When(a <= 1/2,
              [a/2, (a+1)/2],
              [(1-a)/2, (1-a+1)/2]),
         BlockMat([[I(m/2), Tensor(Mat(bblock(a12[1])), I(m/4))],
                   [I(m/2), Tensor(Mat(bblock(a12[2])), I(m/4))]])));

_omega := a -> E(Denominator(a))^Numerator(a);
_shift := lst -> Concatenation(lst{[2..Length(lst)]}, [lst[1]]);
_shift2 := lst -> Concatenation(lst{[2..Length(lst)]}, [-lst[1]]);

# Polynomial transform for p(x)
# P_{b,a} = [p_l(a_k)]_{k,l}
# where b = {p_0, ...} is basis, typically {1,x,x^2,...}
# and a = {a_0, ...} are roots of p(x)
#
# 2nd col is column of roots
#
# roots of x^n - omega{2pi*a}  (==omega( (a+i)/n ))
CycRoots := (n, a) -> List([0..n-1], i -> _omega((a+i)/n));

# roots of x^2n - 2 x^n cos(2*pi*f) + 1
# roots are ordered CCW on the unit circle
PhiRoots := (N, f) -> let(n:=Numerator(f), d:=Denominator(f),
    ConcatList([0..N-1], i -> [E(N*d)^(n+d*i), E(N*d)^(-n-d*(N-1-i))]));

PhiRoots2 := (N, f) -> let(n:=Numerator(f), d:=Denominator(f),
    Concatenation(
    List([0..N-1], i -> [E(2*N*d)^(n+d*i), E(2*N*d)^(-n-d*(2*N-1-i))])));

phiRootsNewOrder := (n, a) -> # first omega will have negative angle, we have to make it last => _shift
    _shift(ConcatList([0..n-1], i -> [_omega((-a+i)/n), _omega((a+i)/n)]));

phiRoots2NewOrder := (n, a) -> # first omega will have negative angle, we have to make it last and negate => _shift2
    _shift2(ConcatList([0..n-1], i -> [_omega((-a+i)/(2*n)), _omega((a+i)/(2*n))]));

Mat_BDFT := (n, a, rot) -> let(
    roots := phiRootsNewOrder(n/2, a),
    List([1..n], r -> List([0..n-1], c -> roots[r]^(rot*c))));

Mat_BDFT2 := (n, a, rot) -> let(
    roots := phiRoots2NewOrder(n/2, a),
    List([1..n], r -> List([0..n-1], c -> roots[r]^(rot*c))));

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

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

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

B1_time_r := n -> DirectSum(I(1), List([1..Int((n-1)/2)], i-> Mat(time_r(i/n))), I(_even(n)));
B3_time_r := n -> DirectSum(List([0..Int(n/2)-1], i -> Mat(time_r((i+1/2)/n))), I(_odd(n)));

B3_r_time := (n,a) -> DirectSum(List([0..Int(n/2)-1], i -> Mat(r_time(alphai(n,i,a)))), I(_odd(n)));

#ls := (n,func) -> List([0..n-1], func);

#MatBRDFT3 := (N,a) -> MatSPL(BlockMat(let(n:=N/2,
#     ls(n, i->let(phi := [[0,-1],[1,2*cospi(2*alphai(n,i,a))]],
#    ls(n, j->Mat(phi^(2*j))))))));

#MatBRDFT33 := (N,a) -> MatSPL(BlockMat(let(n:=N/2,
#     ls(n, i->let(phi := [[0,1],[1,-2*E(4)*sinpi(2*alphai(n,i,a))]],
#    ls(n, j->Mat(phi^(2*j))))))));

#r:=MatSPL(B3_time_r(7)*SRDFT3(7));

#MatBRDFT4 := (N,a) -> MatSPL(BlockMat(let(n:=N/2,
#     ls(n, i->let(start:=MatSPL(BRDFT4(2,alphai(n,i,a))), phi := [[0,-1],[1,2*cospi(2*alphai(n,i,a))]],
#    ls(n, j->Mat(start*phi^(2*j))))))));

# roots := List(PhiRoots(100, 1/(4*23)), ComplexCyc);
# DoForAll(last, x->AppendTo("roots", ReComplex(x), " ", ImComplex(x), "\n"));
# PrintTo("tmp.gnuplot", "plot 'roots' with lp");
# SYS_EXEC("gnuplot tmp.gnuplot -");

# m88:=MatSPL(M(16,2))*MatSPL(DFT3(16))*MatSPL(bruun2(16,1/4))^-1;
scale := mat -> List(mat, x->x/x[1]);
# Note:
#   GathExtend(n, Even1).transpose() * BSkewDFT3(n, a) * VStack(I(n/2), O(n/2)) == SkewDTT(DCT3(n/2), 2*a)
#   GathExtend(n, Odd1).transpose()  * BSkewDFT4(n, a) * VStack(I(n/2), O(n/2)) == SkewDTT(DCT4(n/2), 2*a)
#
# BRDFT3 supports Odd0 input symmetry
# BRDFT4 supports Odd1 input symmetry (incorrectly defined in Spiral)
#
#   x3  := n ->       MatSPL(Gath(H(n, n/2, 0, 2)) * BRDFT3(n) * GathExtend(n, Odd0));   == PolyDTT(DCT3(n/2)) == DCT3(n/2), other half=zero
#   x33 := n ->       MatSPL(Gath(H(n, n/2, 1, 2)) * BRDFT3(n) * GathExtend(n, Even00)); == PolyDTT(DST3(n/2)), other half=first half * cospi( (2*i+1) / m), eg 1/8, 3/8, 5/8, 7/8 for n=8
#   y3  := n ->     2*MatSPL(Gath(H(n, n/2, 0, 2)) * BRDFT3(n) * DirectSum(I(1), -J(n-1))
#                  1/2*                                               * GathExtend(n, Odd1)));  == PolyDTT(DCT4(n/2)), other half=same
#   y33 := n ->     2*MatSPL(Gath(H(n, n/2, 0, 2)) * BRDFT3(n) * DirectSum(I(1), -J(n-1)) *
#                                                                   GathExtend(n, Even1));  == PolyDTT(DST4(n/2)), other half=neg
#
#   z3  := n ->       MatSPL(Gath(H(n, n/2, 1, 2)) * BRDFT4(n) * GathExtend(n, Odd1));  == PolyDTT(DCT4(n/2)), !new def
#   z33 := n -> scale(MatSPL(Gath(H(n, n/2, 0, 2)) * BRDFT4(n) * GathExtend(n, Even1)));  == PolyDTT(DST4(n/2)), !new def
#
#   x44 := n -> scale((-E(4)) * MatSPL(Gath(H(n, n/2, 1, 2)) * BRDFT4(n) * GathExtend(n, Odd1)));  == PolyDTT(DCT4(n/2))
#   x4 :=  n ->       (-E(4)) * MatSPL(Gath(H(n, n/2, 0, 2)) * BRDFT4(n) * GathExtend(n, Even1));  == DST4(n/2)
#   x4 :=  n ->  scale(-E(4)) * MatSPL(Gath(H(n, n/2, 0, 2)) * BRDFT4(n) * GathExtend(n, Even1));  == PolyDTT(DST4(n/2))

#   spiral> pm(Gath(H(16,8,0,2))*BRDFT3(16)*GathExtend(16, Odd0));
#
#   y := (n,a) -> MatSPL(Gath(H(n, n/2, 0, 2)) * BRDFT3(n,a) * DirectSum(I(1), -VStack(O(2,n/2-1), RI(n/2,n/2-1), O(n/2-3, n/2-1))));
#  == PolyDTT(SkewDTT(DST3(n/2), 2*a))
#

# # Decomposes C[x]/phi(.) or R[x]/phi(.) into 2-dim spectral
# # components with basis 1,x
# #
# Class(BRDFT3, NonTerminal, rec(
#     abbrevs := [ (n)   -> Checked(IsPosInt(n),           [n, 1/4]),
#                  (n,a) -> Checked(IsPosInt(n), IsRatSym(a),
#            let(aa := When(IsRat(a) and a>1/2, 1-a,a), [n, aa])) ],

#     terminate := self >> let(n:=self.params[1], a:=self.params[2],
#   M(n,n/2) * self.xmat() * BSkewDFT3(n, a).terminate()),

#     xmat := self >> let(n:=self.params[1], a:=self.params[2],
#   roots := Take(PhiRoots(n/2,a),n/2), cc := Global.Conjugate,
#   DirectSum(List(roots, r->Mat([[1,r],[1,cc(r)]]^-1)))^LIJ(n)),

#     isReal := True,
#     dims := self >> let(n:=self.params[1], [n, n]),
#     HashId := self >> self.params[1]
# ));

# Class(BRDFT4, BRDFT3, rec(
#     terminate := self >> let(n:=self.params[1], a:=self.params[2],
#   M(n,n/2) * self.xmat() * BSkewDFT4(n, a).terminate()),
# #    xmat := self >> let(n:=self.params[1], a:=self.params[2],
# # roots := Take(PhiRoots2(n/2,a),n/2), cc := Global.Conjugate,
# # DirectSum(List(roots, r->Mat([[r,r^3],[-cc(r),-cc(r)^3]]^-1)))^LIJ(n)),
# ));

#Class(BRDFT4, BRDFT3, rec(
#    terminate := self >> let(n:=self.params[1], a:=self.params[2],
#   Mat(scale(MatSPL(M(n,n/2) * self.xmat() * BSkewDFT4(n, a))))),
##    xmat := self >> let(n:=self.params[1], a:=self.params[2],
##  roots := Take(PhiRoots2(n/2,a),n/2), cc := Global.Conjugate,
##  DirectSum(List(roots, r->Mat([[r,r^3],[-cc(r),-cc(r)^3]]^-1)))^LIJ(n)),
#));

# -------------------------------------------------------------
# Other transforms
# -------------------------------------------------------------

# roots of BSkewDFT3(2*n, u)
#   { E(n)^(2u+i) } union { E(n)^(-2u-i) }
#
Class(BSkewDFT3, NonTerminal, TaggedObjectMixin, rec(
    abbrevs := [ (n)   -> Checked(IsPosIntSym(n),           [n, 1/4, 1]),
                 (n,a) -> Checked(IsPosIntSym(n), IsRatSym(a),
                    let(aa := When(IsRat(a) and a>1/2, 1-a,a), [n, aa, 1])),
                 (n,a,rot) -> Checked(IsPosIntSym(n), IsRatSym(a), IsIntSym(rot),
                    let(aa := When(IsRat(a) and a>1/2, 1-a,a), [n, aa, rot])) ],

    terminate := self >> let(N := self.params[1], a := self.params[2], rot := self.params[3],
        roots := PhiRoots(N/2, EvalScalar(a)),
        Mat(List([1..N], r ->
                List([0..N-1], c -> roots[r]^(rot*c))))),

    isReal := False,
    dims := self >> let(n:=self.params[1], [n, n]),
    hashAs := self >> ObjId(self)(self.params[1]),
));

Class(PSkewDFT3, NonTerminal, TaggedObjectMixin, rec(
    abbrevs := [ (n)   -> Checked(IsPosInt(n),                  [n, 1/4, 1]),
                 (n,a) -> Checked(IsPosInt(n), IsRatSym(a),
                     let(aa := When(IsRat(a) and a>1/2, 1-a,a), [n, aa, 1])),
                 (n,a,rot) -> Checked(IsPosInt(n), IsRatSym(a), IsIntSym(rot),
                     let(aa := When(IsRat(a) and a>1/2, 1-a,a), [n, aa, rot])) ],

    terminate := self >> let(N := self.params[1], a := EvalScalar(self.params[2]),
        rot := EvalScalar(self.params[3]),
        roots := PhiRoots(N/2, a),
        Mat(List([1..N], r ->
            Concatenation(
                List([0..N/2-1], c -> roots[r]^(rot*c)),
                List([N/2..N-1], c -> (roots[r]^(rot*c) - roots[r]^(rot*(c-N/2))*cospi(2*a*rot))/sinpi(2*a*rot)))))),

    isReal := False,
    dims := self >> let(n:=self.params[1], [n, n]),
    hashAs := self >> ObjId(self)(self.params[1]),
));

Class(BSkewDFT4, BSkewDFT3, rec(
    terminate := self >> let(N := self.params[1], a := EvalScalar(self.params[2]), rot := EvalScalar(self.params[3]),
	roots := PhiRoots2(N/2, a),
        Mat(List([1..N], r ->
	     List([0..N-1], c -> roots[r]^(rot*(2*c+1)))))),
    hashAs := self >> ObjId(self)(self.params[1]),
));

# NOTE: rot is missing
Class(BSkewPRDFT, PRDFT3, TaggedObjectMixin, rec(
    abbrevs := [ (n)   -> Checked(IsPosInt(n),           [n, 1/4]),
                 (n,a) -> Checked(IsPosInt(n), IsRatSym(a),
             let(aa := When(IsRat(a) and a>1/2, 1-a,a), [n, aa])) ],

    terminate := self >> let(N := self.params[1], a := EvalScalar(self.params[2]),
	roots := PhiRoots(N/2, a),
        Mat(List([0..N-1], r ->
	     When(r mod 2 = 0,
		 List([0..N-1], c -> self.projRe(roots[1+r/2]^c)),
		 List([0..N-1], c -> self.projIm(roots[1+(r-1)/2]^c)))))),
    hashAs := self >> ObjId(self)(self.params[1]),
));
# NOTE: rot is missing
Class(BSkewRDFT, PRDFT3, TaggedObjectMixin, rec(
    abbrevs := [ (n)   -> Checked(IsPosInt(n),           [n, 1/4]),
                 (n,a) -> Checked(IsPosInt(n), IsRatSym(a),
             let(aa := When(IsRat(a) and a>1/2, 1-a,a), [n, aa])) ],

    terminate := self >> let(N := self.params[1], a := EvalScalar(self.params[2]),
	K(N,2) * BSkewPRDFT(N,a).terminate()),

    hashAs := self >> ObjId(self)(self.params[1]),
));


text:
#
# crypting: Hashes and Crypto in GAP
#
# Implementations
#

BindGlobal("CRYPTING_SHA256_State_Family",
           NewFamily("CRYPTING_SHA256_State_Family"));

BindGlobal("CRYPTING_SHA256_State_Type",
           NewType(CRYPTING_SHA256_State_Family,
                   IsSHA256State) );

InstallGlobalFunction( CRYPTING_HexStringIntPad,
function(i, len, pad)
    local result;

    result := HexStringInt(i);
    if Length(result) < len then
        result := Concatenation(RepeatedString(pad,len - Length(result)), result);
    fi;
    return result;
end);

InstallGlobalFunction( CRYPTING_HexStringIntPad8,
    i -> CRYPTING_HexStringIntPad(i, 8, '0'));

InstallMethod( ViewString, "for a SHA256 state",
               [ IsSHA256State ], x -> "<sha256 state>");

InstallGlobalFunction( SHA256String,
function(str)
    local s;

    if not IsString(str) then
        ErrorNoReturn("usage: str has to be a string");
    fi;

    str := CopyToStringRep(str);
    s := CRYPTING_SHA256_INIT();
    CRYPTING_SHA256_UPDATE(s, str);
    return CRYPTING_SHA256_FINAL(s);
end);

InstallGlobalFunction( HMACSHA256,
function(key, str)
    if not IsString(key) then
        ErrorNoReturn("usage: key has to be a string");
    fi;
    if not IsString(str) then
        ErrorNoReturn("usage: str has to be a string");
    fi;
    key := CopyToStringRep(str);
    str := CopyToStringRep(str);
    return CRYPTING_SHA256_HMAC(key, str);
end);

max_stars_repo_name: gap-packages/crypting

max_issues_repo_name: gap-packages/crypting

max_forks_repo_name: gap-packages/crypting


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

Declare(ICompose, ISum, fTensor, fBase, fId, fCompose);
Declare(RC);

# ===========================================================================
# SPL Sums Notation
# ===========================================================================
# Blk - block
# Blk1 - 1x1 block
# Data(var, value, expr) - data
# ISum - iterative sum
# SUM - sum
#
# Gath(N,n,func) - gather matrix
# Scat(N,n,func) - scatter matrix
# Prm(N, read_func, write_func) - read: output->input, write: input->output
# Conj, ConjL, ConjR, ConjLR - generalized conjugation (arbitrary/no matrix to left and right)
# ConjDiag to conjugate block diagonals
# ===========================================================================

Class(SumsBase, rec(
    isSums := true,
    area := self >> Sum(self.children(), x->x.area()),
    sums := meth(self)
       local children, i, res;
       res := Copy(self);
       children := Map(res.rChildren(), c -> Cond(IsSPL(c), c.sums(), c));
       for i in [1..Length(children)] do
           res.rSetChild(i, children[i]);
       od;
       return res;
    end
));

Compose.area   := self >> Sum(self.children(), x->x.area());
Diag.area      := self >> Rows(self);
Scale.area     := self >> 0;
IsSumsSPL := o -> IsRec(o) and
                ((IsBound(o.isSums) and o.isSums) or
                 (Same(ObjId(o), Compose) and ForAll(o.children(), IsSumsSPL)));

# ==========================================================================
# TCast(<n>, <to_type>, <from_type>) - type conversion on <n> elements
# ==========================================================================
Class(TCast, SumsBase, Sym, rec(
    abbrevs := [
        (n, to_type)            -> Checked(IsPosInt0Sym(n), IsType(to_type), [n, to_type, TUnknown]),
        (n, to_type, from_type) -> Checked(IsPosInt0Sym(n), IsType(to_type), [n, to_type, from_type])
    ],
    def := (n, to_type, from_type) -> Perm((), n),
    dmn := self >> [ TArray(self.params[3], self.params[1]) ],
    rng := self >> [ TArray(self.params[2], self.params[1]) ],

    transpose := self >> self,
    conjTranspose := self >> self,
    inverse := self >> self,
    isSymmetric := True,
    isPermutation := False,
));

# ==========================================================================
# BB(<spl>) - basic block container, serves as barrier for rule application
# ==========================================================================
Class(BB, SumsBase, BaseContainer, rec(isBlock:=true,
    rng := meth(self) return self._children[1].rng(); end,
    dmn := meth(self) return self._children[1].dmn(); end,
));

Class(Buf, SumsBase, BaseContainer, rec(
    rng := meth(self) return self._children[1].rng(); end,
    dmn := meth(self) return self._children[1].dmn(); end
));

Declare(PushL, PushR, PushLR, NoPullLeft, NoPullRight);

# Forces propagation into *left* construct (e.g. ISum, RecursStep, BB, etc)
# Also .sums() conversion always returns same object, ie conversion of
# child is not forced. This is done to avoid Sigma-SPLizing constructs
# that we want to pull in .
Class(PushL, Buf, rec(
    sums := self >> self,
    transpose := self >> PushR(self.child(1).transpose()),
    normalizedArithCost := self >> self.child(1).normalizedArithCost(),
));

# Forces propagation into *right* construct (e.g. ISum, RecursStep, BB, etc)
# Also .sums() conversion always returns same object, ie conversion of
# child is not forced. This is done to avoid Sigma-SPLizing constructs
# that we want to pull in .
Class(PushR, Buf, rec(
    sums := self >> self,
    transpose := self >> PushL(self.child(1).transpose()),
    normalizedArithCost := self >> self.child(1).normalizedArithCost(),
));

# Allows propagation into either right or left construct (e.g. ISum, RecursStep, BB, etc)
# Also .sums() conversion always returns same object, ie conversion of
# child is not forced. This is done to avoid Sigma-SPLizing constructs
# that we want to pull in .
Class(PushLR, Buf, rec(sums := self >> self));

# Prevents propagation into constructs on both sides (e.g. ISum, RecursStep, BB, etc)
Class(NoPull, Buf);

# Prevents pull-in. Used for distributed stuff.
Class(NoPull_Dist, Buf);

# Prevents propagation pulling in of diags
Declare(NoDiagPullinRight);
Class(NoDiagPullin, Buf);
Class(NoDiagPullinLeft, Buf, rec(
    transpose := self >> NoDiagPullinRight(self.child(1).transpose())
));
Class(NoDiagPullinRight, Buf, rec(
    transpose := self >> NoDiagPullinLeft(self.child(1).transpose())
));

# Prevents propagation into left construct (e.g. ISum, RecursStep, BB, etc)
Class(NoPullLeft, Buf, rec(transpose := self >> NoPullRight(self.child(1).transpose())));

# Prevents propagation into right construct (e.g. ISum, RecursStep, BB, etc)
Class(NoPullRight, Buf, rec(transpose := self >> NoPullLeft(self.child(1).transpose())));

# Top level wrapper for Sigma-SPL formulas
Class(Formula, BaseContainer, SumsBase);

#F RecursStep(<expr>)
#F RecursStep(<yofs>, <xofs>, <expr>) - with implicit
#F     fAdd on Gath (xofs) and Scat (yofs) side
#F
Class(RecursStep, SumsBase, BaseContainer, rec(
    abbrevs := [ ch -> [0,0,ch],
                 (yofs,xofs,ch) -> [yofs, xofs, ch] ],
    new := (self, yofs, xofs, ch) >> SPL(WithBases(self,
    rec(yofs:=yofs, xofs:=xofs, _children := [ch], dimensions := ch.dims()))),
    rChildren := self >> [self.yofs, self.xofs, self._children[1]],
    rSetChild := meth(self, n, what)
        if n=1 then self.yofs := what;
        elif n=2 then self.xofs := what;
        elif n=3 then self._children[1] := what;
        else Error("<n> must be in [1..3]");
        fi;
    end,
));

Class(Inplace, SumsBase, BaseContainer, rec(
  rng:=self>>self._children[1].rng(),
  dmn:=self>>self._children[1].dmn(),
  numops:=self >>0, # YSV: what is this? pls remove or document
  toNonInplace := self >> self._children[1],
  isInplace := self >> true,
  normalizedArithCost := self >> self._children[1].normalizedArithCost(),
));

Class(LStep, SumsBase, BaseContainer, rec(
    toAMat := self >> AMatMat(Sum([I(Rows(self)), self.child(1)], MatSPL))
));

Declare(RTWrap); # to avoid complaints in .transpose

Class(RTWrap, SumsBase, BaseContainer, rec(
    new := (self, rt) >> Checked(Global.formgen.IsRuleTree(rt),
    SPL(WithBases(self, rec(
        rt   := rt,
        root := rt.node))).setDims()),

    area := self >> Rows(self) * Cols(self),
    children := self >> [self.rt],
    child := (self, n) >> When(n=1, self.rt, Error("<n> must be 1")),
    setChild := rSetChildFields("rt"),
    rSetChild := ~.setChild,
    rChildren := ~.children,

    dims          := self >> self.rt.dims(),
    isPermutation := self >> self.rt.node.isPermutation(),
    isReal        := self >> self.rt.node.isReal(),
    toAMat        := self >> self.rt.node.toAMat(),

    transpose     := self >> RTWrap(self.rt.transpose()),
    conjTranspose := self >> InertConjTranspose(self),
    isInertConjTranspose := True,
));

# ==========================================================================
# COND(<spl>) - This is a 'switch' statement for SPLs
# ==========================================================================
Class(COND, SumsBase, BaseContainer, rec(
    abbrevs := [ arg -> let(f:=Flat(arg), [f[1], Drop(f, 1)]) ],
    new := (self, cond, spls) >> SPL(WithBases(self, rec(
        _children := spls,
        dimensions := spls[1].dimensions,
        cond := cond))),

    toAMat := self >> When(self.cond.ev()=V(true) or self.cond.ev()=1,
        self.child(1).toAMat(),
        self.child(2).toAMat()),

    rChildren := self >> Concatenation([self.cond], self._children),
    rSetChild := meth(self, n, newC)
        if n = 1 then self.cond := newC;
        else self.setChild(n-1, newC);
        fi;
    end,

    area := self >> Maximum(List(self.children(), x->x.area())),

    sums := self >> CopyFields(self, rec(_children := List(self._children, x->x.sums()))),
    transpose := self >> CopyFields(self, rec(_children := List(self._children, x->x.transpose()))),
));

Class(RC, SumsBase, BaseContainer, rec(
    dims := self >> let(d:=self.child(1).dims(), [2*d[1], 2*d[2]]),
    isReal := self >> true,

    # when RC(M) is transposed with M - complex, not only M is transposed, but also
    # each complex element of M as a 2x2 matrix is transposed == complex conjugation
    transpose := self >> CopyFields(self, rec(_children := [self.child(1).conjTranspose()],
        dimensions := [self.dimensions[2], self.dimensions[1]])),

    # RC(.) is real, conjTranspose is just a regular transpose
    conjTranspose := self >> self.transpose(),
    inverse := self >> CopyFields(self, rec(_children := [self.child(1).inverse()],
        dimensions := [self.dimensions[2], self.dimensions[1]])),

    sums := self >> CopyFields(self, rec(_children := [self.child(1).sums()])),
    area := self >> 2*self.child(1).area(),
    toAMat := self >> AMatMat(RealMatComplexMat(MatSPL(self.child(1)))),
    createCode := self >> Cond(IsBound(self.child(1).createCode), RC(self.child(1).createCode()), self),

    # assume that normalizedArithCost() always returns cost in real ops
    normalizedArithCost := self >> self.child(1).normalizedArithCost(),

));

# This takes a real matrix that can be seen as RC(A) and returns A as complex matrix
Class(CR, SumsBase, BaseContainer, rec(
    dims := self >> List(self.child(1).dimensions, e -> _unwrap(div(e,2))),

    # the derived matrix is real, but over the complex field
    isReal := self >> false,

    transpose := self >> CopyFields(self, rec(
	_children := [self.child(1).transpose()],
        dimensions := [self.dimensions[2], self.dimensions[1]])),

    # CR(.) is complex, but all entries are real, conjTranspose is just a regular transpose
    conjTranspose := self >> self.transpose(),

    inverse := self >> CopyFields(self, rec(_children := [self.child(1).inverse()],
        dimensions := [self.dimensions[2], self.dimensions[1]])),

    sums := self >> CopyFields(self, rec(_children := [self.child(1).sums()])),

    area := self >> 1/2*self.child(1).area(),

    toAMat := self >> let(mat := MatSPL(self.child(1)),
        rmat := List(mat{2*[1..Length(mat)/2]}, m -> m{2*[1..Length(m)/2]}),
        AMatMat(rmat)
        ),

    # assume that normalizedArithCost() always returns cost in real ops
    normalizedArithCost := self >> self.child(1).normalizedArithCost(),
    vcost := self >> self.child(1).vcost()

));

# ==========================================================================
# Blk(<mat>) - matrix block
# ==========================================================================
# Note: Blk should not check for M being a matrix,
#   otherwise cant reuse Blk for vector code
Class(Blk, SumsBase, Mat, rec(
    new := (self, M) >> SPL(WithBases(self, rec(
            element := M,
            TType   := Cond( # NOTE: add checks to M
                            IsList(M),     UnifyTypes(List(Flat(M), InferType)),
                            IsValue(M),    M.t.t,
			    IsSymbolic(M), M.t.t),
			))).setDims(),
    area := self >> Length(Filtered(Flat(self.element), k -> k<>0)),
    new  := (self, M) >> SPL(WithBases(self, rec(element := M))).setDims(),
    dims := self >> Dimensions(self.element)
));

# ==========================================================================
# Blk1(<val>) - 1x1 block
# ==========================================================================
Class(Blk1, SumsBase, BaseMat, rec(
    # Compare mathematically Blks disregarding differences in way to express code-level elements.
#    new := (self, val) >> SPL(WithBases(self, rec(dimensions:=[1,1], element:=val))),
#    toAMat := self >> AMatMat([[EvalScalar(Eval(self.element))]]),
    new := (self, val) >> SPL(WithBases(self, rec(dimensions:=[1,1], element:=EvalScalar(Eval(val))))),
    toAMat := self >> AMatMat([[self.element]]),
    transpose := self >> self,
    conjTranspose := self >> CopyFields(self, rec(element := Global.Conjugate(self.element))),
    inverse := self >> CopyFields(self, rec(element := 1 / self.element)),
    area := self >> 1,
    dims := self >> [1,1],
));

# ==========================================================================
# BlkConj() - pseudo 1x1 matrix when multiplied w/ complex number conjugates it
# ==========================================================================
Class(BlkConj, SumsBase, BaseMat, rec(
    rChildren := self >> [],
    rSetChild := (self, n, what) >> Error("no children"),
    new := (self) >> SPL(WithBases(self, rec(dimensions:=[1,1]))),
    toAMat := self >> AMatMat([[1]]),
    transpose := self >> self,
    conjTranspose := self >> self,
    inverse := self >> self,
    area := self >> 1
));

# ==========================================================================
# Data(<var>, <value>, <spl>) - introduces a data constant bound in <spl>
# ==========================================================================
Class(Data, SumsBase, BaseContainer, rec(
    new := (self, var, value, spl) >> Checked(IsVar(var), IsSPL(spl),
	SPL(WithBases(self, rec(
	    var := var, value := value, _children := [spl],
            dimensions := spl.dims())))),
    #-----------------------------------------------------------------------
    area := self >> self.child(1).area(),
    #-----------------------------------------------------------------------
    eval := meth(self)
        local d, c;
        if IsBound(self._evaluated) then return self._evaluated;
        else
            d := Cond(
                IsValue(self.value) or IsSymbolic(self.value), self.value,
                IsBound(self.value.tolist), V(self.value.tolist()),
                IsSPL(self.value), V(MatSPL(self.value)),
                self.value);
            c := Copy(self._children[1]);
            self._evaluated := SubstBottomUp(c, @(1, var, e->Same(e,self.var)), e -> d);
            return self._evaluated;
        fi;
    end,

    rChildren := self >> [self.var, self.value, self.child(1)],
    rSetChild := meth(self, n, newC)
        if n=1 then self.var := newC;
        elif n=2 then self.value := newC;
        elif n=3 then self._children[1] := newC;
        else Error("<n> must be between [1..3]");
        fi;
    end,
    from_rChildren := (self, rch) >> CopyFields(self, rec(
        var := rch[1], value := rch[2], _children := [rch[3]])),
    #-----------------------------------------------------------------------
    uneval := meth(self) Unbind(self._evaluated); return self; end,
    #-----------------------------------------------------------------------
    transpose := self >> CopyFields(self, rec(
        _children := [self.child(1).transpose()])).uneval(),
    #-----------------------------------------------------------------------
    toAMat := self >> self.eval().toAMat(),
    #-----------------------------------------------------------------------
    sums := self >> CopyFields(self, rec(_children := [self.child(1).sums()])),
));

Declare(Scat);

#F ==========================================================================
#F Gath(<func>) - gather (read) matrix
#F NOTE: implements affine transformations via funcExp hack.

#F as of Dec '2010, <func> can contain fInsert/fPad, which will create
#F funcExp(..) in the code. This is used for simulating affine (rather
#F than linear) transformation.
#F
#F Affine transformations can only be experessed using matrices, if we
#F use homogeneous coordinates, i.e., instead of [x_1, ..., x_n] use
#F always [x_1, ..., x_n, 1], for input/output vectors
#F
#F Gath.toAMat and everything else does NOT use homogeneous
#F coordinates, and thus we can't represent affine "gathers" with a
#F proper matrix, the only special case is when funcExp(0) is used to
#F insert 0s (this preserves linearity).
#F
#F Currently, we use the following semantics (to implement affine transf. using a hack)
#F
#F  nth(X, i)          == X[i]
#F  nth(X, funcExp(i)) == i
#F
#F The proper way of doing this would be instead (using h. coords, X[len(x)] = 1)
#F nth(X, funcExp(i)) -> i * nth(X, len(X)) = i * X[len(X)] = i
#F
#F See http://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations
# ==========================================================================
Class(Gath, SumsBase, BaseMat, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.func],
    rSetChild := rSetChildFields("func"),
    #-----------------------------------------------------------------------
    new := (self, func) >> SPL(WithBases(self, rec(
      	func := Checked(IsFunction(func) or IsFuncExp(func), func)))).setDims(),
    #-----------------------------------------------------------------------
    dims := self >> [self.func.domain(), self.func.range()],
    sums := self >> self,
    area := self >> Sum(Flat([self.func.domain()])),
    isReal := self >> true,
    transpose := self >> Scat(self.func),
    conjTranspose := self >> self.transpose(),
    inverse := self >> self.transpose(),
    #-----------------------------------------------------------------------
    toAMat := self >> let(
	n := EvalScalar(self.func.domain()),
        N := EvalScalar(self.func.range()),
        func := self.func.lambda(),
        AMatMat(List([0..n-1], row -> let(
            idx := EvalScalar(func.at(row)),
	    Cond(idx _is funcExp,
		     When(idx.args[1]=0, Replicate(N, 0),
			 Error("<self> is an affine (non-linear) transformation ",
			       "and can't be represented as a matrix")),
		 BasisVec(N, idx)))))
    ),
    #-----------------------------------------------------------------------
    toloop := (self, bksize) >> let(
	i := Ind(self.func.domain()),
	ISum(i,
            Scat(fTensor(fBase(i), fId(1))) *
            Gath(fCompose(self.func, fTensor(fBase(i), fId(1))))
	).split(bksize)
    ),
    #-----------------------------------------------------------------------
    normalizedArithCost := self >> 0,
    #-----------------------------------------------------------------------
    isIdentity := self >> IsIdentity(func),
));

#F ==========================================================================
#F Prm(<func>) - permutation, semantically same as Gath(<func>), but square
#F
#F NB: Prm should not be used with fPad/fInsert, which lead to affine
#F     transformations when used with Gath.
#F
#F Prm(f).transpose() = Prm(f.transpose())
#F
#F ==========================================================================
Class(Prm, Gath, rec(
    transpose := self >> CopyFields(self, rec(func:=self.func.transpose())),
    toAMat := self >> Perm(PermList(List(self.func.lambda().tolist(), e->e.v)+1),
                           self.func.domain()).toAMat(),
    #-----------------------------------------------------------------------
    normalizedArithCost := self >> 0
));

#   special perm to be gotten rid of in rewriting
Class(DelayedPrm, Prm);
Class(FormatPrm, Prm);

#F ==========================================================================
#F Scat(<func>) - scatter (write) matrix,  Scat(f) = Gath(f).transpose()
#F
#F NOTE: implements affine transformations via funcExp workaround.
#F        See Doc(Gath) for explanation
#F ==========================================================================
Class(Scat, SumsBase, BaseMat, rec(
    #-----------------------------------------------------------------------
    rChildren := self >> [self.func],
    rSetChild := rSetChildFields("func"),
    #-----------------------------------------------------------------------
    new := (self, func) >> SPL(WithBases(self, rec(
	func := Checked(IsFunction(func) or IsFuncExp(func), func)))).setDims(),
    #-----------------------------------------------------------------------
    dims := self >> [self.func.range(), self.func.domain()],
    sums := self >> self,
    area := self >> Sum(Flat([self.func.domain()])),
    isReal := self >> true,
    transpose := self >> Gath(self.func),
    conjTranspose := self >> self.transpose(),
    inverse := self >> self.transpose(),
    #-----------------------------------------------------------------------
    toAMat := self >> TransposedAMat(Gath(self.func).toAMat()),
    #-----------------------------------------------------------------------
    toloop := (self, bksize) >> Gath(self.func).toloop(bksize).transpose(),
    #-----------------------------------------------------------------------
    normalizedArithCost := self >> 0,
    #-----------------------------------------------------------------------
    isIdentity := self >> IsIdentity(self.func),
));

Class(ScatAcc, Scat, rec(
    codeletName:="SA",
    toloop := (self, bksize) >> Error("Not implemented")
));

Declare(ScatGath);

# ==========================================================================
# ScatGath(<sfunc>, <gfunc>)
# ==========================================================================
Class(ScatGath, SumsBase, BaseMat, rec(
    rChildren := self >> [self.sfunc, self.gfunc],
    rSetChild := rSetChildFields("sfunc", "gfunc"),
    #-----------------------------------------------------------------------
    new := (self, sfunc, gfunc) >> SPL(WithBases(self,
        rec(dimensions := [sfunc.range(), gfunc.range()],
	    sfunc := Checked(IsFunction(sfunc) or IsFuncExp(sfunc), sfunc),
	    gfunc := Checked(IsFunction(gfunc) or IsFuncExp(gfunc), gfunc)))),
    #-----------------------------------------------------------------------
    dims := self >> [self.sfunc.range(), self.gfunc.range()],
    area := self >> self.sfunc.domain(),
    isReal := self >> true,
    transpose := self >> ScatGath(self.gfunc, self.sfunc),
    conjTranspose := self >> self.transpose(),
    inverse := self >> self.transpose(),
    #-----------------------------------------------------------------------
    toAMat := meth(self)
        local s, gfunc, g, sdomain, gdomain, idx;
        # NOTE: FF: this is a temporary solution to work around Lamda
	#        problems with symbolic domains and variable substitution
        s := Scat(self.sfunc);
        sdomain := EvalScalar(self.sfunc.domain());
        gdomain := spiral.code.RulesStrengthReduce(self.gfunc.domain());
        if ObjId(self.gfunc) = Lambda and IsExp(gdomain) then
            idx := Ind(sdomain);
            gfunc := Lambda(idx, self.gfunc.at(idx)).setRange(self.gfunc.range());
        else
            gfunc := self.gfunc;
        fi;
        g :=Gath (gfunc);
        return s.toAMat() * g.toAMat();
    end,
       # Correct semantics: Scat(self.sfunc).toAMat() * Gath(self.gfunc).toAMat(),
    #-----------------------------------------------------------------------
    sums := self >> self, #self.toloop(self.maxBkSize()),
    #-----------------------------------------------------------------------
    maxBkSize := meth(self)
        local exp, l, d;
        exp := self.gfunc.domain();

        if IsValue(exp) or IsInt(exp) then return exp; fi;

        if IsExp(exp) and ObjId(exp)=mul and IsValue(exp.args[1]) then
            return exp.args[1];
        fi;

        if IsInt(exp.eval()) or IsValue(exp.eval()) then return EvalScalar(exp); fi;
        l := Lambda(Filtered(exp.free(), IsLoopIndex), exp);
        if Length(l.vars) > 1 then return 1; fi;
        d := List(spiral.sigma.GenerateData(l).tolist(), EvalScalar);
        return Gcd(d);
    end,
    #-----------------------------------------------------------------------
    toloop := (self, bksize) >> let(
	i := Ind(self.gfunc.domain()),
        ISum(i,
            Scat(fCompose(self.sfunc, fTensor(fBase(i), fId(1)))) *
            Gath(fCompose(self.gfunc, fTensor(fBase(i), fId(1))))
        ).split(bksize)
    )
));

# ==========================================================================
# SUM(<spl1>, <spl2>, ...) - non-overlapping matrix sum
# ==========================================================================
Declare(SUM, SUMAcc);
Class(SUM, SumsBase, BaseOperation, rec(
    area := self >> Sum(self.children(), x->x.area()),
    abbrevs := [ arg ->
    [ Flat(List(Flat(arg),
        s -> When(IsSPL(s) and Same(ObjId(s), SUM), s.children(), s))) ] ],
    #-----------------------------------------------------------------------
    new := meth(self, L)
        local dims;
        Constraint(Length(L) >= 1); Constraint(ForAll(L, IsSPL));
        if Length(L) = 1 then return L[1]; fi;
        dims := L[1].dims();
        if not (IsSymbolic(dims[1]) or IsSymbolic(dims[2])) and
           not ForAll(Drop(L, 1), x -> let(d:=x.dims(),
                   IsSymbolic(d[1]) or IsSymbolic(d[2]) or d = dims))
            then Error("Dimensions of summands do not match"); fi;
        return SPL(WithBases(self, rec( _children := L, dimensions := dims)));
    end,
    #-----------------------------------------------------------------------
    rng := self >> self.child(1).rng(),
    #-----------------------------------------------------------------------
    dmn := self >> self.child(1).dmn(),

    advdims := self >> self._children[1].advdims(),
    #-----------------------------------------------------------------------
#    dims := self >> self.child(1).dimensions,
    #-----------------------------------------------------------------------
    toAMat := self >> AMatMat(Sum(self._children, MatSPL)),
    #-----------------------------------------------------------------------
    isPermutation := self >> false,
    #-----------------------------------------------------------------------
    transpose := self >>   # we use CopyFields to copy all fields of self
        CopyFields(self, rec(
           _children := List(self._children, x->x.transpose()),
           dimensions := Reversed(self.dimensions))),
    inverse := self >>   # we use CopyFields to copy all fields of self
        CopyFields(self, rec(
           _children := List(self._children, x->x.inverse()),
           dimensions := Reversed(self.dimensions))),
    conjTranspose := self >>   # we use CopyFields to copy all fields of self
        CopyFields(self, rec(
           _children := List(self._children, x->x.conjTranspose()),
           dimensions := Reversed(self.dimensions)))
));

# ==========================================================================
# SUMAcc(<spl1>, <spl2>, ...) - overlapping matrix sum
# ==========================================================================
Class(SUMAcc, SUM, rec(
   abbrevs := [ arg ->
    [ Flat(List(Flat(arg),
        s -> When(IsSPL(s) and Same(ObjId(s), SUMAcc), s.children(), s))) ] ]
 ));

# ==========================================================================
# ISum(<var>, <domain>, <spl>) - non-overlapping iterative matrix sum
# ==========================================================================
Class(ISum, SumsBase, BaseIterative, rec(
    needInterleavedLeft := self >> self.child(1).needInterleavedLeft(),
    needInterleavedRight := self >> self.child(1).needInterleavedRight(),
    cannotChangeDataFormat := self >> self.child(1).cannotChangeDataFormat(),
    totallyCannotChangeDataFormat := self >> self.child(1).totallyCannotChangeDataFormat(),

    directOper := SUM,
    area := self >> let(ac:=self._children[1].area(), ac * self.domain),
    #-----------------------------------------------------------------------
    rng := self >> self._children[1].rng(),
    dmn := self >> self._children[1].dmn(),
    dims := self >> [StripList(List(self.rng(),l->l.size)),StripList(List(self.dmn(),l->l.size))],

    advdims := self >> self._children[1].advdims(),

    #-----------------------------------------------------------------------
    transpose := self >> CopyFields(self, rec(
           _children := [self._children[1].transpose()],
           dimensions := Reversed(self.dimensions))),
    conjTranspose := self >> CopyFields(self, rec(
           _children := [self._children[1].conjTranspose()],
           dimensions := Reversed(self.dimensions))),
    inverse := self >> CopyFields(self, rec(
           _children := [self._children[1].inverse()],
           dimensions := Reversed(self.dimensions))),
    #-----------------------------------------------------------------------
    unroll := self >> SUM(self.unrolledChildren()),
    #-----------------------------------------------------------------------
    sums := self >> CopyFields(self, rec(
        _children := [self._children[1].sums()]))
));

Class(ISumLS, ISum);
Class(JamISum, ISum, rec(isBlockTransitive := true));
Class(Grp, Buf);

# ==========================================================================
# ICompose(<var>, <domain>, <spl>) - iterative matrix product
# ==========================================================================
Class(ICompose, SumsBase, BaseIterative, rec(
    area := self >> self._children[1].area() * self.domain,
    #-----------------------------------------------------------------------
    dims := self >> self._children[1].dimensions,
    #-----------------------------------------------------------------------
    unroll := self >> Compose(self.unrolledChildren()),
    #-----------------------------------------------------------------------
    transpose := self >> ICompose(self.var, self.domain,
        SubstVars(Copy(self._children[1].transpose()),
	          tab((self.var.id) := self.domain-1-self.var))),

    createCode := self >> Cond(IsBound(self._children[1].createCode),
        ICompose(self.var, self.domain, self._children[1].createCode()), self),

    prods := self >> let(base := self.__bases__[1],
        base(self.var, self.domain, self._children[1].prods())),

#    rChildren := self >> [self.var, self.domain, self._children[1]],
#
#    from_rChildren := (self, rch) >> ApplyFunc(ObjId(self), rch),
#
#    rSetChild := meth(self, n, newChild)
#        if n=1 then self.var := newChild;
#        elif n=2 then self.domain := newChild;
#        elif n=3 then self._children := [newChild];
#        else Error("<n> must be in [1..3]");
#        fi;
#    end
));

# ==========================================================================
# ISumAcc(<var>, <domain>, <spl>) - overlapping iterative matrix sum
# ==========================================================================
Class(ISumAcc, ISum);

# ==========================================================================
# IParSeq(<var>, <domain>, <fb>, <spl>)
# ==========================================================================
Declare(ParSeq);
Class(IParSeq, SumsBase, BaseIterative, rec(
    abbrevs := [ (v, fb_cnt, expr) -> [v, v.range, fb_cnt, expr]  ],
    new := meth(self, v, domain, fb_cnt, expr)
        local obj;
        #NOTE: check dimensions
        obj := Inherited(v, domain, expr);
        obj.fb_cnt := fb_cnt;
        return obj;
    end,

    dims := self >> self._children[1].dims(),
    unroll := self >> ParSeq( self.fb_cnt, Reversed(self.unrolledChildren())),

    filtCompL := (self, lst) >> lst{[1..self.fb_cnt]},
    filtCompR := (self, lst) >> lst{[1..self.fb_cnt]},
    filtSUML  := (self, lst) >> lst{[self.fb_cnt+1..Length(lst)]},
    filtSUMR  := (self, lst) >> lst{[self.fb_cnt+1..Length(lst)]},

    # area doesn take into account that we have composition and sum
    area := self >> self._children[1].area() * self.domain,

    print := (self, i, is) >> Print(
        self.name, "(", self.var, ", ", self.domain, ", ", self.fb_cnt, ",\n",
        Blanks(i+is), self._children[1].print(i+is, is), "\n",
        Blanks(i), ")", self.printA(),
        When(IsBound(self._setDims), Print(".overrideDims(", self._setDims, ")"), Print(""))
    ),
));

##############################################################################
Declare(Conj, ConjL, ConjR, ConjLR, ConjDiag);

Class(Conj, SumsBase, BaseOperation, rec(
    new    := (self, spl) >> SPL(WithBases(self, rec(_children:=[spl], dimensions := spl.dimensions))),
    dims   := self >> self._children[1].dims(),
    toAMat := self >> self.child(1).toAMat(),
    sums   := self >> self,
    transpose := self >> Conj(self.child(1).transpose())
));

Class(ConjL, Conj, rec(
    new    := (self, spl, lprm) >> SPL(WithBases(self, rec(_children:=[spl, lprm]))).setDims(),
    dims   := self >> [self.child(2).dims()[1], self.child(1).dims()[2]],
    toAMat := self >> self.child(2).toAMat() * self.child(1).toAMat(),
    sums   := self >> ConjL(self.child(1).sums(), self.child(2)),
    transpose := self >> ConjR(self.child(1).transpose(), self.child(2).transpose()),
));

Class(ConjR, Conj, rec(
    new    := (self, spl, rprm) >> SPL(WithBases(self, rec(_children:=[spl, rprm]))).setDims(),
    dims   := self >> [self._children[1].dims()[1], self._children[2].dims()[2]],
    toAMat := self >> self.child(1).toAMat() * self.child(2).toAMat(),
    sums   := self >> ConjR(self.child(1).sums(), self.child(2)),
    transpose := self >> ConjL(self.child(1).transpose(), self.child(2).transpose()),
));

Class(ConjLR, Conj, rec(
    new    := (self, spl, lprm, rprm) >> SPL(WithBases(self, rec(_children:=[spl, lprm, rprm]))).setDims(),
    dims   := self >> [ self.child(2).dims()[1], self.child(3).dims()[2] ],
    toAMat := self >> self.child(2).toAMat() * self.child(1).toAMat() * self.child(3).toAMat(),
    sums   := self >> ConjLR(self.child(1).sums(), self.child(2), self.child(3)),
    transpose := self >> ConjLR(self.child(1).transpose(), self.child(3).transpose(), self.child(2).transpose()),
));

Class(ConjDiag, Conj, rec(
    new    := (self, spl, lprm, rprm) >> SPL(WithBases(self, rec(_children:=[spl, lprm, rprm]))).setDims(),
    dims   := self >> [Rows(self.child(2)), Cols(self.child(3))],
    toAMat := self >> self.child(2).toAMat() * self.child(1).toAMat() * self.child(3).toAMat(),
    sums   := self >> ConjDiag(self.child(1).sums(), self.child(2), self.child(3)),
    transpose := self >> ConjDiag(self.child(1).transpose(), self.child(3).transpose(), self.child(2).transpose()),
));

Class(NeedInterleavedComplex, BaseContainer, rec(
    needInterleavedLeft := True,
    needInterleavedRight := True,
    sums := self >> self,
    area:= self >> self.child(1).area()
));


text:
# Built-in
Factorial(5);

# An implementation
fact := n -> Product([1 .. n]);