#ifndef AMX_KERNELS_HPP
#define AMX_KERNELS_HPP
#include <algorithm>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <memory>

#include "amx_buffers.hpp"
#include "amx_config.hpp"
#include "amx_quantization.hpp"
#include "amx_utils.hpp"
#include "llama.cpp/ggml-impl.h"
#include "llama.cpp/ggml-quants.h"
#include "llamafile/sgemm.h"
#include "utils.hpp"

namespace amx {

// Compile-time detection: true when AMX intrinsics are available
#if defined(__AMX__) || defined(__AMXINT8__) || defined(__AMXBF16__) || defined(__AMX_TILE__) || defined(HAVE_AMX)
inline constexpr bool AMX_AVAILABLE = true;
#ifndef HAVE_AMX
#define HAVE_AMX
#endif
#else
inline constexpr bool AMX_AVAILABLE = false;
#endif

/*
We use 1-3-3
 C = A x B


A is a row major matrix of size M x K, usually an Linear Layer weight matrix
B is a col major vector of size K x N, usually an input vector, N is usually
quite small

   B
 A C
 A C
 A C

  TMM 0-2: A
  TMM 3: B
  TMM 4-6: C

   3
 0 4
 1 5
 2 6
*/

template <class, class>
struct dpb133 {
  static void run();
};

template <>
inline void dpb133<int8_t, int8_t>::run() {
  _tile_dpbssd(4, 0, 3);
  _tile_dpbssd(5, 1, 3);
  _tile_dpbssd(6, 2, 3);
}

template <>
inline void dpb133<int8_t, uint8_t>::run() {
  _tile_dpbsud(4, 0, 3);
  _tile_dpbsud(5, 1, 3);
  _tile_dpbsud(6, 2, 3);
}

template <>
inline void dpb133<uint8_t, int8_t>::run() {
  _tile_dpbusd(4, 0, 3);
  _tile_dpbusd(5, 1, 3);
  _tile_dpbusd(6, 2, 3);
}

template <>
inline void dpb133<uint8_t, uint8_t>::run() {
  _tile_dpbuud(4, 0, 3);
  _tile_dpbuud(5, 1, 3);
  _tile_dpbuud(6, 2, 3);
}

template <int TILE_K = 32>
struct GemmKernel133 {
  static constexpr int TILE_M = 16;
  static constexpr int TILE_N = 16;
  static constexpr int VNNI_BLK = 4;
  static constexpr int OUTPUT_T_SIZE = 4;

  static constexpr int M_STEP = TILE_M * 3;
  static constexpr int N_STEP = TILE_N;
  static constexpr int K_STEP = TILE_K;

  static int recommended_nth(int m) { return (m + M_STEP - 1) / M_STEP; }

  static void config() {
#ifdef HAVE_AMX
    TileConfig tile_config;

    for (int i = 0; i < 3; i++) tile_config.set_row_col(i, TILE_M, TILE_K);

    tile_config.set_row_col(3, TILE_K / VNNI_BLK, TILE_N * VNNI_BLK);

    for (int i = 4; i < 7; i++) tile_config.set_row_col(i, TILE_M, TILE_N * OUTPUT_T_SIZE);

    tile_config.set_config();
#endif
  }

  template <typename TA, typename TB, typename TC>
  static void run_full_tile(const TA* a, size_t lda, const TB* b, size_t ldb, TC* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_loadd(0, a, lda);
    _tile_loadd(1, offset_pointer(a, lda * TILE_M), lda);
    _tile_loadd(2, offset_pointer(a, lda * TILE_M * 2), lda);

    _tile_loadd(3, b, ldb);

    _tile_loadd(4, c, ldc);
    _tile_loadd(5, offset_pointer(c, ldc * TILE_N), ldc);
    _tile_loadd(6, offset_pointer(c, ldc * TILE_N * 2), ldc);

    dpb133<TA, TB>::run();

    _tile_stored(4, c, ldc);
    _tile_stored(5, offset_pointer(c, ldc * TILE_N), ldc);
    _tile_stored(6, offset_pointer(c, ldc * TILE_N * 2), ldc);
#endif
  }

  template <typename TA, typename TB, typename TC>
  static void run_full_tile_zero(const TA* a, size_t lda, const TB* b, size_t ldb, TC* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_loadd(0, a, lda);
    _tile_loadd(1, offset_pointer(a, lda * TILE_M), lda);
    _tile_loadd(2, offset_pointer(a, lda * TILE_M * 2), lda);

    _tile_loadd(3, b, ldb);

    _tile_zero(4);
    _tile_zero(5);
    _tile_zero(6);

    dpb133<TA, TB>::run();

    // debug_tiles(7);

    _tile_stored(4, c, ldc);
    _tile_stored(5, offset_pointer(c, ldc * TILE_N), ldc);
    _tile_stored(6, offset_pointer(c, ldc * TILE_N * 2), ldc);
#endif
  }

  static void convert_full_tile_b_to_vnni_inplace(void* b) { transpose_16x8_32bit((__m256i*)b); }

  template <typename TA>
  struct ATile {
    TA v[3 * TILE_M * TILE_K];
    void partial_load(TA* a, int m, int k, size_t lda) {
      // memset(v, 0, sizeof(TA) * 3 * TILE_M * TILE_K);
      for (int i = 0; i < m; i++) {
        for (int j = 0; j < k; j++) {
          v[i * TILE_K + j] = a[i * lda + j];
        }
      }
    }

    void partial_load_quant(block_q4_0* a, int m, int k, size_t lda) {
      assert(k == 32);
      // memset(v, 0, sizeof(TA) * 3 * TILE_M * TILE_K);
      __m256i* vv = (__m256i*)v;
      for (int i = 0; i < m; i++) {
        vv[i] = dequant4x32(offset_pointer(a, lda * i)->qs);
        vv[i] = _mm256_sub_epi8(vv[i], _mm256_set1_epi8(8));
      }
    }

    void partial_load_quant(block_q8_0* a, int m, int k, size_t lda) {
      assert(k == 32);
      // memset(v, 0, sizeof(TA) * 3 * TILE_M * TILE_K);
      __m256i* vv = (__m256i*)v;
      for (int i = 0; i < m; i++) {
        vv[i] = unaligned_copy8x32(offset_pointer(a, lda * i)->qs);
      }
    }

    template <typename QA>
    void partial_load_quant(TA* a, int m, size_t lda) {
      // memset(v, 0, sizeof(TA) * 3 * TILE_M * TILE_K);
      if constexpr (std::is_same_v<QA, blocks_aligned_q8_0_ref>) {
        __m512i* vv = (__m512i*)v;
        for (int i = 0; i < m; i++) {
          vv[i] = copy8x64(offset_pointer(a, lda * i));
        }
      } else if constexpr (std::is_same_v<QA, blocks_aligned_q4_0_ref>) {
        assert(0);
      } else {
        assert(0);
      }
    }

    void partial_load_quant(block_q4_K* a, int m, int inner_block_idx, size_t lda) {
      // memset(v, 0, sizeof(TA) * 3 * TILE_M * TILE_K);
      __m256i* vv = (__m256i*)v;

      size_t qs_offset = inner_block_idx / 2 * 32;
      for (int i = 0; i < m; i++) {
        block_q4_K* spa = offset_pointer_row_major(a, i, 0, lda);
        if (inner_block_idx % 2 == 0) {
          vv[i] = lo4bit(spa->qs + qs_offset);
        } else {
          vv[i] = hi4bit(spa->qs + qs_offset);
        }
      }
    }

    void partial_load_quant(blocks_aligned_q8_0_ref a, int m, int k, int blck_stride) {
      // memset(v, 0, sizeof(TA) * 3 * TILE_M * TILE_K);
      __m512i* vv = (__m512i*)v;
      for (int i = 0; i < m; i++) {
        vv[i] = copy8x64(a.offset(blck_stride * i).qs);
      }
    }
  };

  template <typename TB>
  struct alignas(64) BTile {
    TB v[TILE_N * TILE_K];
    __m512 scale = {};

    void partial_load(TB* b, int n, int k, size_t ldb) {
      for (int i = 0; i < n; i++) {
        for (int j = 0; j < k; j++) {
          v[i * TILE_K + j] = b[i * ldb + j];
        }
      }
      transpose_16x8_32bit((__m256i*)v);
    }

    void partial_load_quant(block_q8_0* b, int n, int k, size_t ldb) {
      assert(k == 32);
      memset(v, 0, sizeof(TB) * TILE_K * TILE_N);
      __m256i* vv = (__m256i*)v;
      float* bss = reinterpret_cast<float*>(&scale);
      for (int i = 0; i < n; i++) {
        vv[i] = unaligned_copy8x32(offset_pointer(b, ldb * i)->qs);
        float sb = GGML_FP16_TO_FP32(offset_pointer_col_major(b, 0, i, ldb)->d);
        bss[i] = sb;
      }

      transpose_16x8_32bit(vv);
    }

    void partial_load_quant(blocks_aligned_q8_0_ref b, int n, int k, int blck_stride) {
      assert(k == 64);
      memset(v, 0, sizeof(TB) * TILE_K * TILE_N);
      __m512i* vv = (__m512i*)v;
      float* vs = reinterpret_cast<float*>(&scale);
      for (int i = 0; i < n; i++) {
        auto ref = b.offset(blck_stride * i);
        vv[i] = copy8x64(ref.qs);
        float sb = GGML_FP16_TO_FP32(*ref.d);
        vs[i] = sb;
      }
      transpose_16x16_32bit(vv);
    }

    void load_from(TB* b, size_t ldb) {
      __m256i* vb = (__m256i*)b;
      __m256i* vo = (__m256i*)v;
      for (int i = 0; i < 16; i++) {
        vo[i] = *offset_pointer(vb, ldb * i);
      }
      transpose_16x8_32bit(vo);
    }

    template <typename TA, typename TC>
    void run_full_ac(TA* a, size_t lda, TC* c, size_t ldc) {
      run_full_tile(a, lda, v, TILE_N * VNNI_BLK, c, ldc);
    }
  };

  template <typename TB>
  struct alignas(64) BTileSum {
    TB v[TILE_N * TILE_K];
    __m512 scale = {};
    __m512 sum = {};
    void partial_load_quant(block_q8_K* b, int n, int inner_block_idx, size_t ldb) {
      memset(v, 0, TILE_K * TILE_N);
      __m256i* vv = (__m256i*)v;
      float* scale_s = reinterpret_cast<float*>(&scale);
      float* sum_s = reinterpret_cast<float*>(&sum);
      for (int i = 0; i < n; i++) {
        block_q8_K* spb = offset_pointer_col_major(b, 0, i, ldb);
        vv[i] = unaligned_copy8x32(spb->qs + inner_block_idx * 32);
        scale_s[i] = spb->d;
        sum_s[i] =
            spb->bsums[inner_block_idx * 2] + spb->bsums[inner_block_idx * 2 + 1];  // TODO: may this will be slow
        // printf("scale[%d] = %f, sum_s[%d] = %f\n", i, scale_s[i], i,
        // sum_s[i]);
      }
      transpose_16x8_32bit(vv);
    }
  };
  template <typename TC>
  struct alignas(64) CTile {
    static_assert(sizeof(TC) == 4);
    TC v[3 * TILE_M * TILE_N] = {};

    void partial_load(TC* c, int m, int n, size_t ldc) {
      for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
          v[i * TILE_N + j] = offset_pointer(c, ldc * i)[j];
        }
      }
    }

    void partial_store(TC* c, int m, int n, size_t ldc) {
      for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
          offset_pointer(c, ldc * i)[j] = v[i * TILE_N + j];
        }
      }
    }

    void to_fp32() {
      __m512i* vv = (__m512i*)v;
      __m512* vf = (__m512*)v;
      for (int i = 0; i < 3 * TILE_M; i++) {
        vf[i] = _mm512_cvtepi32_ps(vv[i]);
      }
    }
  };

  template <typename TA, typename TB, typename TC>
  struct PartialTiles {
    ATile<TA> ta;
    BTile<TB> tb;
    CTile<TC> tc;
    void partial_run(int m, int n, int k, TA* a, size_t lda, TB* b, size_t ldb, TC* c, size_t ldc) {
      ta.partial_load(a, m, k, lda);
      tb.partial_load(b, n, k, ldb);
      tc.partial_load(c, m, n, ldc);
      run_full_tile(ta.v, TILE_K, tb.v, TILE_N * VNNI_BLK, tc.v, TILE_N * OUTPUT_T_SIZE);
      tc.partial_store(c, m, n, ldc);
    }

    template <typename QA>
    void partial_run_quant(int m, int n, int k, QA* a, size_t lda, block_q8_0* b, size_t ldb, float* c, size_t ldc) {
      assert(QK4_0 == 32);
      assert(QK8_0 == 32);

      ta.partial_load_quant(a, m, k, lda);
      tb.partial_load_quant(b, n, k, ldb);

      run_full_tile_zero(ta.v, TILE_K, tb.v, TILE_N * VNNI_BLK, tc.v, TILE_N * OUTPUT_T_SIZE);

      __m512i* cs = (__m512i*)tc.v;
      for (int i = 0; i < m; i++) {
        __m512 as = _mm512_set1_ps(GGML_FP16_TO_FP32(offset_pointer_row_major(a, i, 0, lda)->d));
        __m512* now = reinterpret_cast<__m512*>(offset_pointer_row_major(c, i, 0, ldc));
        *now = _mm512_fmadd_ps(_mm512_mul_ps(as, tb.scale), _mm512_cvtepi32_ps(cs[i]), *now);
      }
    }

    template <typename QA>
    void partial_run_quant_ac(int m, int n, int k, QA* a, size_t lda, float* c, size_t ldc) {
      assert(QK4_0 == 32);
      assert(QK8_0 == 32);

      ta.partial_load_quant(a, m, k, lda);

      run_full_tile_zero(ta.v, TILE_K, tb.v, TILE_N * VNNI_BLK, tc.v, TILE_N * OUTPUT_T_SIZE);

      __m512i* cs = (__m512i*)tc.v;
      for (int i = 0; i < m; i++) {
        __m512 as = _mm512_set1_ps(GGML_FP16_TO_FP32(offset_pointer_row_major(a, i, 0, lda)->d));
        __m512* now = reinterpret_cast<__m512*>(offset_pointer_row_major(c, i, 0, ldc));
        *now = _mm512_fmadd_ps(_mm512_mul_ps(as, tb.scale), _mm512_cvtepi32_ps(cs[i]), *now);
      }
    }

    template <typename AQA>
    void partial_run_quant_ac(int m, int n, int k, AQA a, int a_blck_stride, float* c, size_t ldc) {
      assert(AQA::block_size == 64);

      ta.partial_load_quant(a, m, k, a_blck_stride);

      run_full_tile_zero(ta.v, TILE_K, tb.v, TILE_N * VNNI_BLK, tc.v, TILE_N * OUTPUT_T_SIZE);

      __m512i* cs = (__m512i*)tc.v;
      for (int i = 0; i < m; i++) {
        __m512 as = _mm512_set1_ps(GGML_FP16_TO_FP32(*a.offset(i * a_blck_stride).d));
        // printf("%f\n", GGML_FP16_TO_FP32(*a.offset(i * a_blck_stride).d));
        __m512* now = reinterpret_cast<__m512*>(offset_pointer_row_major(c, i, 0, ldc));
        *now = _mm512_fmadd_ps(_mm512_mul_ps(as, tb.scale), _mm512_cvtepi32_ps(cs[i]), *now);
      }
    }
  };

  template <typename TA, typename TB, typename TC>
  struct PartialTilesSum {
    ATile<TA> ta;
    BTileSum<TB> tb;
    CTile<TC> tc;

    void partial_run_quant_ac(int m, int n, int inner_block_idx, block_q4_K* a, size_t lda, float* c, size_t ldc,
                              float a_scale, float a_min) {
      ta.partial_load_quant(a, m, inner_block_idx, lda);

      run_full_tile_zero(ta.v, TILE_K, tb.v, TILE_N * VNNI_BLK, tc.v, TILE_N * OUTPUT_T_SIZE);

      __m512i* cs = (__m512i*)tc.v;
      for (int i = 0; i < m; i++) {
        __m512* now = reinterpret_cast<__m512*>(offset_pointer_row_major(c, i, 0, ldc));
        *now = _mm512_fmadd_ps(_mm512_sub_ps(_mm512_mul_ps(_mm512_cvtepi32_ps(cs[i]), _mm512_set1_ps(a_scale)),
                                             _mm512_mul_ps(tb.sum, _mm512_set1_ps(a_min))),
                               tb.scale, *now);
        // C += Bscale * (Ascale * dp - Amin * Bsum)
      }
    }
  };
};

struct GemmKernel133BF {
  using dt = ggml_bf16_t;
  using output_t = float;
  static constexpr int TILE_M = 16;
  static constexpr int TILE_K = 32;
  static constexpr int TILE_N = 16;
  static constexpr int VNNI_BLK = 2;

  static constexpr int M_STEP = TILE_M * 3;
  static constexpr int N_STEP = TILE_N;
  static constexpr int K_STEP = TILE_K;

  static int recommended_nth(int m) { return (m + M_STEP - 1) / M_STEP; }
  static void config() {
#ifdef HAVE_AMX
    enable_amx();
    TileConfig tile_config;

    // size is 16 x 32
    for (int i = 0; i < 3; i++) tile_config.set_row_col(i, TILE_M, TILE_K * sizeof(dt));

    // size is 8 x 64
    tile_config.set_row_col(3, TILE_K / VNNI_BLK, TILE_N * VNNI_BLK * sizeof(dt));

    // size is 16 x 64
    for (int i = 4; i < 7; i++) tile_config.set_row_col(i, TILE_M, TILE_N * sizeof(output_t));

    tile_config.set_config();
#endif
  }

  static void run_full_tile(const dt* a, size_t lda, const dt* b, size_t ldb, output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_loadd(0, a, lda);
    _tile_loadd(1, offset_pointer(a, lda * TILE_M), lda);
    _tile_loadd(2, offset_pointer(a, lda * TILE_M * 2), lda);

    _tile_loadd(3, b, ldb);

    _tile_loadd(4, c, ldc);
    _tile_loadd(5, offset_pointer(c, ldc * TILE_N), ldc);
    _tile_loadd(6, offset_pointer(c, ldc * TILE_N * 2), ldc);

    _tile_dpbf16ps(4, 0, 3);
    _tile_dpbf16ps(5, 1, 3);
    _tile_dpbf16ps(6, 2, 3);

    // debug_tiles(7);

    _tile_stored(4, c, ldc);
    _tile_stored(5, offset_pointer(c, ldc * TILE_N), ldc);
    _tile_stored(6, offset_pointer(c, ldc * TILE_N * 2), ldc);
#endif
  }

  struct ATile {
    dt v[3 * TILE_M * TILE_K];

    void partial_load(dt* a, int m, int k, size_t lda) {
      assert(k == TILE_K);
      __m512* vv = (__m512*)v;
      __m512* va = (__m512*)a;
      for (int i = 0; i < m; i++) {
        vv[i] = *offset_pointer_row_major(va, i, 0, lda);
      }
    }
  };

  struct alignas(64) BTile {
    dt v[TILE_N * TILE_K];

    void full_load(dt* b, size_t ldb) { partial_load(b, TILE_N, TILE_K, ldb); }

    void partial_load(dt* b, int n, int k, size_t ldb) {
      __m512* vv = (__m512*)v;
      __m512* vb = (__m512*)b;
      for (int i = 0; i < n; i++) {
        vv[i] = *offset_pointer_col_major(vb, 0, i, ldb);
      }
      transpose_16x16_32bit((__m512i*)v);
    }

    template <typename TA, typename TC>
    void run_full_ac(TA* a, size_t lda, TC* c, size_t ldc) {
      run_full_tile(a, lda, v, TILE_N * VNNI_BLK * sizeof(dt), c, ldc);
    }
  };

  struct alignas(64) CTile {
    output_t v[3 * TILE_M * TILE_N];
    // c must be 64 aligned, ldc must be 64 aligned
    void partial_load(float* c, int m, int n, size_t ldc) {
      assert(n <= TILE_N);
      __m512* vv = (__m512*)v;
      __m512* vc = (__m512*)c;
      for (int i = 0; i < m; i++) {
        vv[i] = *offset_pointer_row_major(vc, i, 0, ldc);
      }
    }

    void partial_store(float* c, int m, int n, size_t ldc) {
      assert(n <= TILE_N);
      __m512* vv = (__m512*)v;
      __m512* vc = (__m512*)c;
      for (int i = 0; i < m; i++) {
        *offset_pointer_row_major(vc, i, 0, ldc) = vv[i];
      }
    }
  };

  struct PartialTiles {
    ATile ta;
    BTile tb;
    CTile tc;
    void partial_run(int m, int n, int k, dt* a, size_t lda, dt* b, size_t ldb, output_t* c, size_t ldc) {
      ta.partial_load(a, m, k, lda);
      tb.partial_load(b, n, k, ldb);
      tc.partial_load(c, m, n, ldc);
      run_full_tile(ta.v, TILE_K * sizeof(dt), tb.v, TILE_N * VNNI_BLK * sizeof(dt), tc.v, TILE_N * sizeof(output_t));
      tc.partial_store(c, m, n, ldc);
    }
  };
};

template <typename T1, typename T2>
constexpr T2 convert_to(const T1& value) {
  if constexpr (std::is_same<T1, T2>::value) {
    return value;
  } else if constexpr (std::is_same<T1, ggml_bf16_t>::value && std::is_same<T2, float>::value) {
    return GGML_BF16_TO_FP32(value);
  } else if constexpr (std::is_same<T1, float>::value && std::is_same<T2, ggml_bf16_t>::value) {
    return GGML_FP32_TO_BF16(value);
  }
}

struct GemmKernel224BF {
  using dt = ggml_bf16_t;
  using output_t = float;
  static constexpr double ELEMENT_SIZE = 2;
  static constexpr int TILE_M = 16;
  static constexpr int TILE_K = 32;
  static constexpr int TILE_N = 16;
  static constexpr int VNNI_BLK = 2;

  static constexpr int M_STEP = TILE_M * 2;
  static constexpr int N_STEP = TILE_N * 2;
  static constexpr int K_STEP = TILE_K;

  static inline const int N_BLOCK = 256;
  static inline const int K_BLOCK = 1792;
  static std::string name() { return "BF16"; }

  static int recommended_nth(int n) { return (n + N_BLOCK - 1) / N_BLOCK; }

  static std::pair<int, int> split_range_n(int n, int ith, int nth) {
    int n_start = N_BLOCK * ith;
    int n_end = std::min(n, N_BLOCK * (ith + 1));
    return {n_start, n_end};
  }

  static void config() {
#ifdef HAVE_AMX
    enable_amx();
    TileConfig tile_config;

    // size is 16 x 32
    for (int i = 0; i < 2; i++) tile_config.set_row_col(i, TILE_M, TILE_K * sizeof(dt));

    // size is 16 x 32
    for (int i = 2; i < 4; i++) tile_config.set_row_col(i, TILE_K / VNNI_BLK, TILE_N * VNNI_BLK * sizeof(dt));

    // size is 16 x 16
    for (int i = 4; i < 8; i++) tile_config.set_row_col(i, TILE_M, TILE_N * sizeof(output_t));

    tile_config.set_config();
#endif
  }

  static void load_a(dt* a, size_t lda) {
#ifdef HAVE_AMX
    _tile_loadd(0, a, lda);
    _tile_loadd(1, offset_pointer(a, lda * TILE_M), lda);
#else
    (void)a;
    (void)lda;
#endif
  }

  static void load_b(dt* b, size_t ldb) {
#ifdef HAVE_AMX
    _tile_loadd(2, b, ldb);
    _tile_loadd(3, offset_pointer(b, ldb * TILE_N), ldb);
#else
    (void)b;
    (void)ldb;
#endif
  }

  static void clean_c() {
#ifdef HAVE_AMX
    _tile_zero(4);
    _tile_zero(5);
    _tile_zero(6);
    _tile_zero(7);
#endif
  }

  static void load_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_loadd(4, c, ldc);
    _tile_loadd(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_loadd(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_loadd(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void store_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_stored(4, c, ldc);
    _tile_stored(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_stored(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_stored(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void run_tile() {
#ifdef HAVE_AMX
    _tile_dpbf16ps(4, 0, 2);
    _tile_dpbf16ps(5, 0, 3);
    _tile_dpbf16ps(6, 1, 2);
    _tile_dpbf16ps(7, 1, 3);
#endif
  }

  struct BufferA {
    ggml_bf16_t* a;
    int max_m, k;

    static size_t required_size(int max_m, int k) { return sizeof(ggml_bf16_t) * max_m * k; }

    BufferA(int max_m, int k, void* ptr) : max_m(max_m), k(k) {
      assert(reinterpret_cast<intptr_t>(ptr) % 64 == 0);
      assert(max_m % M_STEP == 0);
      assert(k % K_STEP == 0);
      a = reinterpret_cast<ggml_bf16_t*>(ptr);
    }

    void set_data(void* new_ptr) { a = reinterpret_cast<ggml_bf16_t*>(new_ptr); }

    void from_mat(int m, ggml_bf16_t* src, int ith, int nth) {
      assert(m <= max_m);
      assert(ith == 0 && nth == 1);
      int m_block_size = (m + M_STEP - 1) / M_STEP * M_STEP;
      for (int m_begin = 0; m_begin < m; m_begin += M_STEP) {
        for (int k_block_begin = 0; k_block_begin < k; k_block_begin += K_BLOCK) {
          int k_block_size = std::min(K_BLOCK, k - k_block_begin);
          for (int k_begin = 0; k_begin < k_block_size; k_begin += K_STEP) {
            for (int i = 0; i < M_STEP && m_begin + i < m; i++) {
              __m512i* s = (__m512i*)(src + (m_begin + i) * k + k_block_begin + k_begin);
              __m512i* d =
                  (__m512i*)(a + k_block_begin * m_block_size + m_begin * k_block_size + k_begin * M_STEP + i * K_STEP);
              avx512_copy_32xbf16(s, d);
            }
          }
        }
      }
    }

    ggml_bf16_t* get_submat(int m, int k, int m_begin, int k_begin) {
      int m_block_size = (m + M_STEP - 1) / M_STEP * M_STEP;
      int k_block_begin = k_begin / K_BLOCK * K_BLOCK;
      k_begin -= k_block_begin;
      int k_block_size = std::min(K_BLOCK, k - k_block_begin);
      return a + k_block_begin * m_block_size + m_begin * k_block_size + k_begin * M_STEP;
    }
  };

  struct BufferB {
    ggml_bf16_t* b;
    int n, k;
    static constexpr bool SCALE = false;

    static size_t required_size(int n, int k) { return sizeof(ggml_bf16_t) * n * k; }

    BufferB(int n, int k, void* ptr) : n(n), k(k) {
      assert(reinterpret_cast<intptr_t>(ptr) % 64 == 0);
      assert(n % N_STEP == 0);
      assert(k % K_STEP == 0);
      b = reinterpret_cast<ggml_bf16_t*>(ptr);
    }

    void set_data(void* new_ptr) { b = reinterpret_cast<ggml_bf16_t*>(new_ptr); }

    void _pack_block(ggml_bf16_t* src, int src_stride, int n_block_begin, int n_block_size) {
      for (int n_begin = 0; n_begin < n_block_size; n_begin += N_STEP) {
        for (int k_block_begin = 0; k_block_begin < k; k_block_begin += K_BLOCK) {
          int k_block_size = std::min(K_BLOCK, k - k_block_begin);
          for (int k_begin = 0; k_begin < k_block_size; k_begin += K_STEP) {
            for (int i = 0; i < N_STEP; i++) {
              __m512i* s = (__m512i*)(src + (n_begin + i) * src_stride + k_block_begin + k_begin);
              __m512i* d = (__m512i*)(b + n_block_begin * k + k_block_begin * n_block_size + n_begin * k_block_size +
                                      k_begin * N_STEP + i * K_STEP);
              avx512_copy_32xbf16(s, d);
            }
            transpose_16x16_32bit((__m512i*)(b + n_block_begin * k + k_block_begin * n_block_size +
                                             n_begin * k_block_size + k_begin * N_STEP));
            transpose_16x16_32bit((__m512i*)(b + n_block_begin * k + k_block_begin * n_block_size +
                                             n_begin * k_block_size + k_begin * N_STEP + TILE_N * K_STEP));
          }
        }
      }
    }

    void from_mat(ggml_bf16_t* src, int ith, int nth) {
      auto [n_start, n_end] = split_range_n(n, ith, nth);
      int n_block_begin = n_start;
      int n_block_size = n_end - n_block_begin;
      _pack_block(src + n_block_begin * k, k, n_block_begin, n_block_size);
    }

    /**
     * @brief Pack a transposed matrix into BufferB format.
     *
     * src is a row-major (src_n, src_k) matrix. The target BufferB has shape (n=src_k, k=src_n),
     * i.e., the logical transpose. Each call processes one N_BLOCK of the target (selected by ith/nth).
     *
     * Uses a thread-local strip buffer for tiled transpose, then reuses the same packing logic as from_mat.
     */
    void from_mat_transposed(ggml_bf16_t* src, int src_n, int src_k, int ith, int nth) {
      auto [n_start, n_end] = split_range_n(n, ith, nth);
      int n_block_begin = n_start;
      int n_block_size = n_end - n_block_begin;
      if (n_block_size <= 0) return;

      // Thread-local strip buffer: n_block_size × k BF16 values
      thread_local std::vector<ggml_bf16_t> strip;
      strip.resize(n_block_size * k);

      // Tiled transpose from source into strip
      // Target row r (in N_BLOCK) corresponds to source column (n_block_begin + r)
      // Target col c corresponds to source row c
      // strip[r * k + c] = src[c * src_k + (n_block_begin + r)]
      constexpr int TILE = 32;
      for (int c_tile = 0; c_tile < k; c_tile += TILE) {
        int c_end = std::min(c_tile + TILE, k);
        for (int r_tile = 0; r_tile < n_block_size; r_tile += TILE) {
          int r_end = std::min(r_tile + TILE, n_block_size);
          for (int c = c_tile; c < c_end; c++) {
            for (int r = r_tile; r < r_end; r++) {
              strip[r * k + c] = src[c * src_k + (n_block_begin + r)];
            }
          }
        }
      }

      // Reuse existing packing logic on the transposed strip buffer
      _pack_block(strip.data(), k, n_block_begin, n_block_size);
    }

    /**
     * @brief Unpack BF16 BufferB back to row-major BF16 matrix (lossless).
     *
     * Reverses _pack_block(): un-VNNI-transpose each tile, then copy BF16
     * values back to row-major dst[n, k].
     */
    void to_mat(ggml_bf16_t* dst, int ith, int nth) {
      auto [n_start, n_end] = split_range_n(n, ith, nth);
      int n_block_begin = n_start;
      int n_block_size = n_end - n_block_begin;
      if (n_block_size <= 0) return;

      // Thread-local tile buffer for un-VNNI (N_STEP * K_STEP * sizeof(bf16) = 32*32*2 = 2048 bytes)
      alignas(64) ggml_bf16_t tile_copy[N_STEP * K_STEP];

      for (int n_begin = 0; n_begin < n_block_size; n_begin += N_STEP) {
        for (int k_block_begin = 0; k_block_begin < k; k_block_begin += K_BLOCK) {
          int k_block_size = std::min(K_BLOCK, k - k_block_begin);
          for (int k_begin = 0; k_begin < k_block_size; k_begin += K_STEP) {
            ggml_bf16_t* tile_src = b + n_block_begin * k + k_block_begin * n_block_size +
                                    n_begin * k_block_size + k_begin * N_STEP;

            // Copy tile and reverse VNNI transpose (self-inverse)
            memcpy(tile_copy, tile_src, N_STEP * K_STEP * sizeof(ggml_bf16_t));
            transpose_16x16_32bit((__m512i*)tile_copy);
            transpose_16x16_32bit((__m512i*)(tile_copy + TILE_N * K_STEP));

            // Copy rows back to row-major dst
            for (int i = 0; i < N_STEP; i++) {
              __m512i* s = (__m512i*)(tile_copy + i * K_STEP);
              __m512i* d = (__m512i*)(dst + (n_block_begin + n_begin + i) * k + k_block_begin + k_begin);
              avx512_copy_32xbf16(s, d);
            }
          }
        }
      }
    }

    /**
     * @brief Direct BufferB → transposed BufferB repack (no BF16 workspace).
     *
     * src has shape (src.n, src.k), this (dest) has shape (n=src.k, k=src.n).
     * For each dest tile: un-VNNI source tile → transpose 32×32 BF16 → re-VNNI → store.
     * BF16 is lossless, so this produces bit-identical results to to_mat + from_mat_transposed.
     */
    void from_bb_transposed(const BufferB& src, int ith, int nth) {
      assert(n == src.k && k == src.n);

      auto [n_start, n_end] = split_range_n(n, ith, nth);
      int dst_nb_begin = n_start;
      int dst_nb_size = n_end - dst_nb_begin;
      if (dst_nb_size <= 0) return;

      // Helper: compute tile pointer in a packed BF16 BB
      auto tile_ptr = [](ggml_bf16_t* base, int total_n, int total_k,
                         int abs_n, int abs_k) -> ggml_bf16_t* {
        int nb_begin = abs_n / N_BLOCK * N_BLOCK;
        int n_within = abs_n - nb_begin;
        int nb_size = std::min(N_BLOCK, total_n - nb_begin);
        int kb_begin = abs_k / K_BLOCK * K_BLOCK;
        int k_within = abs_k - kb_begin;
        return base + nb_begin * total_k + kb_begin * nb_size +
               n_within * std::min(K_BLOCK, total_k - kb_begin) + k_within * N_STEP;
      };

      alignas(64) ggml_bf16_t src_tile[N_STEP * K_STEP];
      alignas(64) ggml_bf16_t dst_tile[N_STEP * K_STEP];

      for (int dn = 0; dn < dst_nb_size; dn += N_STEP) {
        for (int dk_block = 0; dk_block < k; dk_block += K_BLOCK) {
          int dk_block_size = std::min(K_BLOCK, k - dk_block);
          for (int dk = 0; dk < dk_block_size; dk += K_STEP) {
            int abs_dn = dst_nb_begin + dn;
            int abs_dk = dk_block + dk;

            // Source tile at (abs_dk, abs_dn): src rows [abs_dk..+32), cols [abs_dn..+32)
            ggml_bf16_t* sp = tile_ptr(src.b, src.n, src.k, abs_dk, abs_dn);
            memcpy(src_tile, sp, N_STEP * K_STEP * sizeof(ggml_bf16_t));
            transpose_16x16_32bit((__m512i*)src_tile);
            transpose_16x16_32bit((__m512i*)(src_tile + TILE_N * K_STEP));

            // Transpose 32×32 BF16: dst_tile[j][i] = src_tile[i][j]
            for (int i = 0; i < N_STEP; i++) {
              for (int j = 0; j < K_STEP; j++) {
                dst_tile[j * K_STEP + i] = src_tile[i * K_STEP + j];
              }
            }

            // Re-VNNI and store to dest tile at (abs_dn, abs_dk)
            transpose_16x16_32bit((__m512i*)dst_tile);
            transpose_16x16_32bit((__m512i*)(dst_tile + TILE_N * K_STEP));

            ggml_bf16_t* dp = tile_ptr(b, n, k, abs_dn, abs_dk);
            memcpy(dp, dst_tile, N_STEP * K_STEP * sizeof(ggml_bf16_t));
          }
        }
      }
    }

    ggml_bf16_t* get_submat(int n, int k, int n_begin, int k_begin) {
      int n_block_begin = n_begin / N_BLOCK * N_BLOCK;
      n_begin -= n_block_begin;
      int n_block_size = std::min(N_BLOCK, n - n_block_begin);
      int k_block_begin = k_begin / K_BLOCK * K_BLOCK;
      k_begin -= k_block_begin;
      int k_block_size = std::min(K_BLOCK, k - k_block_begin);
      return b + n_block_begin * k + k_block_begin * n_block_size + n_begin * k_block_size + k_begin * N_STEP;
    }
  };

  struct BufferC {
    float* c;
    int max_m, n;
    // 物理布局(按 float 元素数)：
    // 逻辑矩阵 C 为 (max_m, n) 行主序，max_m 为 M_STEP 的倍数，
    // n 按 N_BLOCK 分块。
    // 存储顺序：
    //   n_block(N_BLOCK 列) → m_block(M_STEP 行) → n_step(N_STEP 列) → (M_STEP×N_STEP) 行主序 tile。
    // 因此可视为 5D：
    //   c[n_blocks][m_blocks][n_steps][M_STEP][N_STEP]，
    //   n_blocks = ceil(n / N_BLOCK)，m_blocks = max_m / M_STEP，
    //   n_steps = N_BLOCK / N_STEP（尾块可能更小）。
    // get_submat(m_begin, n_begin) 返回连续的 (M_STEP×N_STEP) tile 起始地址。

    static size_t required_size(int max_m, int n) { return sizeof(float) * max_m * n; }

    BufferC(int max_m, int n, void* ptr) : max_m(max_m), n(n) {
      assert(reinterpret_cast<intptr_t>(ptr) % 64 == 0);
      assert(max_m % M_STEP == 0);
      assert(n % N_STEP == 0);
      c = reinterpret_cast<float*>(ptr);
    }

    void set_data(void* new_ptr) { c = reinterpret_cast<float*>(new_ptr); }

    void to_mat(int m, ggml_bf16_t* dst, int ith, int nth) {
      assert(m <= max_m);
      auto [n_start, n_end] = split_range_n(n, ith, nth);
      int m_block_size = (m + M_STEP - 1) / M_STEP * M_STEP;
      int n_block_begin = n_start;
      int n_block_size = n_end - n_block_begin;
      for (int m_begin = 0; m_begin < m; m_begin += M_STEP) {
        for (int n_begin = 0; n_begin < n_block_size; n_begin += N_STEP) {
          for (int i = 0; i < M_STEP && m_begin + i < m; i++) {
            __m512* x0 =
                (__m512*)(c + m_block_size * n_block_begin + m_begin * n_block_size + n_begin * M_STEP + i * N_STEP);
            __m512* x1 = (__m512*)(c + m_block_size * n_block_begin + m_begin * n_block_size + n_begin * M_STEP +
                                   i * N_STEP + 16);
            avx512_32xfp32_to_32xbf16(x0, x1, (__m512i*)(dst + (m_begin + i) * n + n_block_begin + n_begin));
          }
        }
      }
    }

    float* get_submat(int m, int n, int m_begin, int n_begin) {
      int m_block_size = (m + M_STEP - 1) / M_STEP * M_STEP;
      int n_block_begin = n_begin / N_BLOCK * N_BLOCK;
      int n_block_size = std::min(N_BLOCK, n - n_block_begin);
      n_begin -= n_block_begin;
      return c + m_block_size * n_block_begin + m_begin * n_block_size + n_begin * M_STEP;
    }
  };
};

struct GemmKernel224Int8 {
  using dt = int8_t;
  using output_t = int32_t;
  static constexpr double ELEMENT_SIZE = 1;
  static constexpr int TILE_M = 16;
  static constexpr int TILE_K = 64;
  static constexpr int TILE_N = 16;
  static constexpr int VNNI_BLK = 4;

  static constexpr int M_STEP = TILE_M * 2;
  static constexpr int N_STEP = TILE_N * 2;
  static constexpr int K_STEP = TILE_K;

  // static inline const int N_BLOCK = 256;
  static inline const int N_BLOCK = 64;
  // static inline const int N_BLOCK = 32;
  static inline const int K_BLOCK = 3584;
  static std::string name() { return "INT8"; }

  static int recommended_nth(int n) { return (n + N_BLOCK - 1) / N_BLOCK; }

  static std::pair<int, int> split_range_n(int n, int ith, int nth) {
    int n_start = N_BLOCK * ith;
    int n_end = std::min(n, N_BLOCK * (ith + 1));
    return {n_start, n_end};
  }

  static void config() {
#ifdef HAVE_AMX
    enable_amx();
    TileConfig tile_config;

    // size is 16 x 64
    for (int i = 0; i < 2; i++) tile_config.set_row_col(i, TILE_M, TILE_K * sizeof(dt));

    // size is 16 x 64
    for (int i = 2; i < 4; i++) tile_config.set_row_col(i, TILE_K / VNNI_BLK, TILE_N * VNNI_BLK * sizeof(dt));

    // size is 16 x 16
    for (int i = 4; i < 8; i++) tile_config.set_row_col(i, TILE_M, TILE_N * sizeof(output_t));

    tile_config.set_config();
#endif
  }

  static void load_a(dt* a, size_t lda) {
#ifdef HAVE_AMX
    _tile_loadd(0, a, lda);
    _tile_loadd(1, offset_pointer(a, lda * TILE_M), lda);
#else
    (void)a;
    (void)lda;
#endif
  }

  static void load_b(dt* b, size_t ldb) {
#ifdef HAVE_AMX
    _tile_loadd(2, b, ldb);
    _tile_loadd(3, offset_pointer(b, ldb * TILE_N), ldb);
#else
    (void)b;
    (void)ldb;
#endif
  }

  static void clean_c() {
#ifdef HAVE_AMX
    _tile_zero(4);
    _tile_zero(5);
    _tile_zero(6);
    _tile_zero(7);
#endif
  }

  static void load_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_loadd(4, c, ldc);
    _tile_loadd(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_loadd(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_loadd(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void store_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_stored(4, c, ldc);
    _tile_stored(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_stored(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_stored(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void run_tile() {
#ifdef HAVE_AMX
    _tile_dpbssd(4, 0, 2);
    _tile_dpbssd(5, 0, 3);
    _tile_dpbssd(6, 1, 2);
    _tile_dpbssd(7, 1, 3);
#endif
  }

  using BufferA = BufferAImpl<GemmKernel224Int8>;
  using BufferC = BufferCImpl<GemmKernel224Int8>;

  struct BufferB {
    int8_t* b;
    float* d;
    int n, k;
    static constexpr bool SCALE = true;

    static size_t required_size(int n, int k) { return sizeof(int8_t) * n * k + sizeof(float) * n; }

    BufferB(int n, int k, void* ptr) : n(n), k(k) {
      assert(reinterpret_cast<intptr_t>(ptr) % 64 == 0);
      assert(n % N_STEP == 0);
      assert(k % K_STEP == 0);
      if (n % N_STEP || k % K_STEP) {
        printf("n: %d, k: %d, N_STEP: %d, K_STEP: %d\n", n, k, N_STEP, K_STEP);
        throw std::runtime_error("BufferB: n and k must be multiples of N_STEP and K_STEP");
      }
      b = reinterpret_cast<int8_t*>(ptr);
      d = reinterpret_cast<float*>(b + n * k);
    }

    void _pack_block(ggml_bf16_t* src_data, int src_stride, int n_block_begin, int n_block_size) {
      // Phase 1: compute per-row scales
      for (int n_begin = 0; n_begin < n_block_size; n_begin += N_STEP) {
        for (int i = 0; i < N_STEP; i++) {
          float amax = 0.0f;
          for (int j = 0; j < k; j += 32) {
            __m512 f0, f1;
            avx512_32xbf16_to_32xfp32((__m512i*)(src_data + (n_begin + i) * src_stride + j), &f0, &f1);
            amax = MAX(amax, _mm512_reduce_max_ps(_mm512_abs_ps(f0)));
            amax = MAX(amax, _mm512_reduce_max_ps(_mm512_abs_ps(f1)));
          }
          d[n_block_begin + n_begin + i] = amax / ((1 << 7) - 1);
        }
      }
      // Phase 2: quantize and pack
      for (int n_begin = 0; n_begin < n_block_size; n_begin += N_STEP) {
        for (int k_block_begin = 0; k_block_begin < k; k_block_begin += K_BLOCK) {
          int k_block_size = std::min(K_BLOCK, k - k_block_begin);
          for (int k_begin = 0; k_begin < k_block_size; k_begin += K_STEP) {
            for (int i = 0; i < N_STEP; i++) {
              __m512 id = _mm512_set1_ps(d[n_block_begin + n_begin + i] ? 1.0f / d[n_block_begin + n_begin + i] : 0.0f);
              int8_t* dst = b + n_block_begin * k + k_block_begin * n_block_size + n_begin * k_block_size +
                            k_begin * N_STEP + i * K_STEP;
              __m512 f0, f1, f2, f3;
              avx512_32xbf16_to_32xfp32((__m512i*)(src_data + (n_begin + i) * src_stride + k_block_begin + k_begin),
                                        &f0, &f1);
              avx512_32xbf16_to_32xfp32((__m512i*)(src_data + (n_begin + i) * src_stride + k_block_begin + k_begin) + 1,
                                        &f2, &f3);
              __m512i i0 = _mm512_cvtps_epi32(_mm512_mul_ps(f0, id));
              __m512i i1 = _mm512_cvtps_epi32(_mm512_mul_ps(f1, id));
              __m512i i2 = _mm512_cvtps_epi32(_mm512_mul_ps(f2, id));
              __m512i i3 = _mm512_cvtps_epi32(_mm512_mul_ps(f3, id));
              __m128i s0 = _mm512_cvtsepi32_epi8(i0);
              __m128i s1 = _mm512_cvtsepi32_epi8(i1);
              __m128i s2 = _mm512_cvtsepi32_epi8(i2);
              __m128i s3 = _mm512_cvtsepi32_epi8(i3);
              _mm_store_si128((__m128i*)dst, s0);
              _mm_store_si128((__m128i*)(dst + 16), s1);
              _mm_store_si128((__m128i*)(dst + 32), s2);
              _mm_store_si128((__m128i*)(dst + 48), s3);
            }
            transpose_16x16_32bit((__m512i*)(b + n_block_begin * k + k_block_begin * n_block_size +
                                             n_begin * k_block_size + k_begin * N_STEP));
            transpose_16x16_32bit((__m512i*)(b + n_block_begin * k + k_block_begin * n_block_size +
                                             n_begin * k_block_size + k_begin * N_STEP + TILE_N * K_STEP));
          }
        }
      }
    }

    void from_mat(ggml_bf16_t* src, int ith, int nth) {
      auto [n_start, n_end] = split_range_n(n, ith, nth);
      int n_block_begin = n_start;
      int n_block_size = n_end - n_block_begin;
      _pack_block(src + (size_t)n_block_begin * k, k, n_block_begin, n_block_size);
    }

    /**
     * @brief Pack a transposed matrix into INT8 BufferB format.
     *
     * src is a row-major (src_n, src_k) BF16 matrix. The target BufferB has shape (n=src_k, k=src_n).
     * Each call processes one N_BLOCK of the target (selected by ith/nth).
     */
    void from_mat_transposed(ggml_bf16_t* src, int src_n, int src_k, int ith, int nth) {
      auto [n_start, n_end] = split_range_n(n, ith, nth);
      int n_block_begin = n_start;
      int n_block_size = n_end - n_block_begin;
      if (n_block_size <= 0) return;

      // Thread-local strip buffer: n_block_size × k BF16 values
      thread_local std::vector<ggml_bf16_t> strip;
      strip.resize(n_block_size * k);

      // Tiled transpose from source into strip
      constexpr int TILE = 32;
      for (int c_tile = 0; c_tile < k; c_tile += TILE) {
        int c_end = std::min(c_tile + TILE, k);
        for (int r_tile = 0; r_tile < n_block_size; r_tile += TILE) {
          int r_end = std::min(r_tile + TILE, n_block_size);
          for (int c = c_tile; c < c_end; c++) {
            for (int r = r_tile; r < r_end; r++) {
              strip[r * k + c] = src[c * src_k + (n_block_begin + r)];
            }
          }
        }
      }

      // Reuse existing packing logic (scale computation + quantization) on the transposed strip buffer
      _pack_block(strip.data(), k, n_block_begin, n_block_size);
    }

    /**
     * @brief Dequantize INT8 BufferB back to BF16 row-major matrix.
     *
     * Reverses _pack_block(): un-VNNI-transpose each tile, then dequantize
     * int8 * per-row-scale -> float -> BF16.
     *
     * dst is a row-major (n, k) BF16 matrix. Each call processes one N_BLOCK
     * partition (selected by ith/nth).
     */
    void to_mat(ggml_bf16_t* dst, int ith, int nth) {
      auto [n_start, n_end] = split_range_n(n, ith, nth);
      int n_block_begin = n_start;
      int n_block_size = n_end - n_block_begin;
      if (n_block_size <= 0) return;

      // Thread-local tile buffer for un-VNNI (N_STEP * K_STEP = 32 * 64 = 2048 bytes)
      alignas(64) int8_t tile_copy[N_STEP * K_STEP];

      for (int n_begin = 0; n_begin < n_block_size; n_begin += N_STEP) {
        for (int k_block_begin = 0; k_block_begin < k; k_block_begin += K_BLOCK) {
          int k_block_size = std::min(K_BLOCK, k - k_block_begin);
          for (int k_begin = 0; k_begin < k_block_size; k_begin += K_STEP) {
            int8_t* tile_src = b + n_block_begin * k + k_block_begin * n_block_size +
                               n_begin * k_block_size + k_begin * N_STEP;

            // Copy tile and reverse VNNI transpose (transpose_16x16_32bit is self-inverse)
            memcpy(tile_copy, tile_src, N_STEP * K_STEP);
            transpose_16x16_32bit((__m512i*)tile_copy);
            transpose_16x16_32bit((__m512i*)(tile_copy + TILE_N * K_STEP));

            // tile_copy is now in original row-major int8 order:
            // tile_copy[i * K_STEP + j] = quantized value at logical row (n_begin+i), col (k_begin+j)
            // SIMD dequant: 16 int8 -> 16 fp32 (* scale) -> 16 bf16, 4 iterations per row (K_STEP=64)
            for (int i = 0; i < N_STEP; i++) {
              __m512 vs = _mm512_set1_ps(d[n_block_begin + n_begin + i]);
              ggml_bf16_t* dst_ptr = dst + (n_block_begin + n_begin + i) * k + k_block_begin + k_begin;
              int8_t* src_ptr = tile_copy + i * K_STEP;
              for (int j = 0; j < K_STEP; j += 32) {
                // Convert 16 int8 -> 16 int32 -> 16 fp32, multiply scale, convert to bf16
                __m128i i8_0 = _mm_load_si128((__m128i*)(src_ptr + j));
                __m128i i8_1 = _mm_load_si128((__m128i*)(src_ptr + j + 16));
                __m512i i32_0 = _mm512_cvtepi8_epi32(i8_0);
                __m512i i32_1 = _mm512_cvtepi8_epi32(i8_1);
                __m512 f0 = _mm512_mul_ps(_mm512_cvtepi32_ps(i32_0), vs);
                __m512 f1 = _mm512_mul_ps(_mm512_cvtepi32_ps(i32_1), vs);
                avx512_32xfp32_to_32xbf16(&f0, &f1, (__m512i*)(dst_ptr + j));
              }
            }
          }
        }
      }
    }

    /**
     * @brief Direct INT8 BufferB → transposed INT8 BufferB (no BF16 workspace).
     *
     * src has shape (src.n, src.k), this (dest) has shape (n=src.k, k=src.n).
     * Two-pass algorithm with register-based 16×16 sub-block transposes:
     *   Pass 1: SIMD absmax scan → per-dest-row scales d[j]
     *   Pass 2: 8 sub-blocks of 16×16: dequant → register transpose → quantize → VNNI-pack
     */
    void from_bb_transposed(const BufferB& src, int ith, int nth) {
      assert(n == src.k && k == src.n);

      auto [n_start, n_end] = split_range_n(n, ith, nth);
      int dst_nb_begin = n_start;
      int dst_nb_size = n_end - dst_nb_begin;
      if (dst_nb_size <= 0) return;

      auto tile_ptr = [](int8_t* base, int total_n, int total_k,
                         int abs_n, int abs_k) -> int8_t* {
        int nb_begin = abs_n / N_BLOCK * N_BLOCK;
        int n_within = abs_n - nb_begin;
        int nb_size = std::min(N_BLOCK, total_n - nb_begin);
        int kb_begin = abs_k / K_BLOCK * K_BLOCK;
        int k_within = abs_k - kb_begin;
        return base + nb_begin * total_k + kb_begin * nb_size +
               n_within * std::min(K_BLOCK, total_k - kb_begin) + k_within * N_STEP;
      };

      alignas(64) int8_t tile_copy[N_STEP * K_STEP];  // 2KB un-VNNI workspace

      // === Pass 1: SIMD per-dest-row absmax ===
      alignas(64) float absmax_arr[N_BLOCK];
      memset(absmax_arr, 0, dst_nb_size * sizeof(float));

      int c_start = (dst_nb_begin / K_STEP) * K_STEP;
      int c_end_limit = dst_nb_begin + dst_nb_size;

      for (int src_c = c_start; src_c < c_end_limit; src_c += K_STEP) {
        int col_lo = std::max(dst_nb_begin, src_c);
        int local_lo = col_lo - src_c;
        int buf_offset = col_lo - dst_nb_begin;
        int ncols = std::min(c_end_limit, src_c + K_STEP) - col_lo;
        int nchunks = ncols / 16;

        __m512 amax[4];
        for (int c = 0; c < nchunks; c++)
          amax[c] = _mm512_setzero_ps();

        for (int src_r = 0; src_r < src.n; src_r += N_STEP) {
          int8_t* sp = tile_ptr(src.b, src.n, src.k, src_r, src_c);
          memcpy(tile_copy, sp, N_STEP * K_STEP);
          transpose_16x16_32bit((__m512i*)tile_copy);
          transpose_16x16_32bit((__m512i*)(tile_copy + TILE_N * K_STEP));

          for (int i = 0; i < N_STEP; i++) {
            float abs_scale = src.d[src_r + i];
            abs_scale = abs_scale >= 0 ? abs_scale : -abs_scale;
            __m512 vs = _mm512_set1_ps(abs_scale);
            int8_t* row = tile_copy + i * K_STEP + local_lo;

            for (int c = 0; c < nchunks; c++) {
              __m128i i8_16 = _mm_load_si128((__m128i*)(row + c * 16));
              __m512i abs_i32 = _mm512_abs_epi32(_mm512_cvtepi8_epi32(i8_16));
              amax[c] = _mm512_max_ps(amax[c],
                _mm512_mul_ps(_mm512_cvtepi32_ps(abs_i32), vs));
            }
          }
        }

        for (int c = 0; c < nchunks; c++)
          _mm512_store_ps(absmax_arr + buf_offset + c * 16, amax[c]);
      }

      for (int j = 0; j < dst_nb_size; j++)
        d[dst_nb_begin + j] = absmax_arr[j] / 127.0f;

      // === Pass 2: register-based 16×16 sub-block transpose ===
      alignas(64) int8_t quant_tile[N_STEP * K_STEP];  // 2KB

      for (int dn = 0; dn < dst_nb_size; dn += N_STEP) {
        for (int dk_block = 0; dk_block < k; dk_block += K_BLOCK) {
          int dk_block_size = std::min(K_BLOCK, k - dk_block);
          for (int dk = 0; dk < dk_block_size; dk += K_STEP) {
            int abs_dn = dst_nb_begin + dn;
            int abs_dk = dk_block + dk;
            int c_align = (abs_dn / K_STEP) * K_STEP;
            int c_offset = abs_dn - c_align;

            for (int half = 0; half < 2; half++) {
              int src_r = abs_dk + half * N_STEP;
              int8_t* sp = tile_ptr(src.b, src.n, src.k, src_r, c_align);
              memcpy(tile_copy, sp, N_STEP * K_STEP);
              transpose_16x16_32bit((__m512i*)tile_copy);
              transpose_16x16_32bit((__m512i*)(tile_copy + TILE_N * K_STEP));

              for (int src_rb = 0; src_rb < N_STEP; src_rb += 16) {
                for (int src_cb = 0; src_cb < N_STEP; src_cb += 16) {
                  // Load 16×16 int8 sub-block, dequant to float in registers
                  __m512i regs[16];
                  for (int i = 0; i < 16; i++) {
                    int8_t* addr = tile_copy + (src_rb + i) * K_STEP + c_offset + src_cb;
                    float scale = src.d[src_r + src_rb + i];
                    __m512i i32 = _mm512_cvtepi8_epi32(_mm_load_si128((__m128i*)addr));
                    regs[i] = _mm512_castps_si512(
                      _mm512_mul_ps(_mm512_cvtepi32_ps(i32), _mm512_set1_ps(scale)));
                  }

                  // Transpose 16×16 in registers (32-bit element shuffle)
                  transpose_16x16_32bit(regs);

                  // Quantize transposed floats and store to quant_tile
                  int dest_rb = src_cb;              // 0 or 16
                  int dest_cb = half * 32 + src_rb;  // 0, 16, 32, or 48
                  for (int i = 0; i < 16; i++) {
                    float sv = d[abs_dn + dest_rb + i];
                    float id = sv ? 1.0f / sv : 0.0f;
                    __m512i q = _mm512_cvtps_epi32(
                      _mm512_mul_ps(_mm512_castsi512_ps(regs[i]), _mm512_set1_ps(id)));
                    _mm_store_si128(
                      (__m128i*)(quant_tile + (dest_rb + i) * K_STEP + dest_cb),
                      _mm512_cvtsepi32_epi8(q));
                  }
                }
              }
            }

            // VNNI pack
            transpose_16x16_32bit((__m512i*)quant_tile);
            transpose_16x16_32bit((__m512i*)(quant_tile + TILE_N * K_STEP));

            // Write to dest BB
            int8_t* dp = b + dst_nb_begin * k + dk_block * dst_nb_size +
                         dn * dk_block_size + dk * N_STEP;
            memcpy(dp, quant_tile, N_STEP * K_STEP);
          }
        }
      }
    }

    int8_t* get_submat(int n, int k, int n_begin, int k_begin) {
      int n_block_begin = n_begin / N_BLOCK * N_BLOCK;
      n_begin -= n_block_begin;
      int n_block_size = std::min(N_BLOCK, n - n_block_begin);
      int k_block_begin = k_begin / K_BLOCK * K_BLOCK;
      k_begin -= k_block_begin;
      int k_block_size = std::min(K_BLOCK, k - k_block_begin);
      return b + n_block_begin * k + k_block_begin * n_block_size + n_begin * k_block_size + k_begin * N_STEP;
    }

    float* get_scale(int n, int n_begin) { return d + n_begin; }
  };

  static void amx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, float* c, BufferA* ba,
                         BufferB* bb) {
    using K = GemmKernel224Int8;
    if (k_block_begin == 0) {
      K::clean_c();
    } else {
      K::load_c((int32_t*)c, K::N_STEP * sizeof(int32_t));
    }
    for (int k_begin = 0; k_begin < K::K_BLOCK && k_block_begin + k_begin < k; k_begin += K::K_STEP) {
      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin + k_begin), K::K_STEP * sizeof(int8_t));
      K::load_b(bb->get_submat(n, k, n_begin, k_block_begin + k_begin), K::K_STEP * sizeof(int8_t));
      K::run_tile();
    }
    K::store_c((int32_t*)c, K::N_STEP * sizeof(int32_t));
  }
  static void avx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, float* c, BufferA* ba,
                         BufferB* bb) {
    __m512i* c512 = (__m512i*)c;
    int m_block_end = std::min(m - m_begin, M_STEP);
    if (k_block_begin == 0) {
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        c512[m_i * 2] = _mm512_setzero_si512();
        c512[m_i * 2 + 1] = _mm512_setzero_si512();
      }
    }

    for (int k_begin = 0; k_begin < K_BLOCK && k_block_begin + k_begin < k; k_begin += K_STEP) {
      static_assert(K_STEP * sizeof(int8_t) == sizeof(__m512i));
      static_assert(N_STEP / TILE_N == 2, "Must be lke this");

      int32_t* a32 = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin + k_begin);
      __m512i* b512 = (__m512i*)bb->get_submat(n, k, n_begin, k_block_begin + k_begin);
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        for (int k_i = 0; k_i < 16; k_i++) {
          __m512i ma = _mm512_set1_epi32(a32[m_i * 16 + k_i]);
          for (int n_i = 0; n_i < 2; n_i++) {
            c512[m_i * 2 + n_i] = _mm512_dpbssd_epi32(c512[m_i * 2 + n_i], ma, b512[n_i * 16 + k_i]);
          }
        }
      }
    }
  }

  static void apply_scale(int m, int n, int m_begin, int n_begin, float* c, BufferA* ba, BufferB* bb) {
    using K = GemmKernel224Int8;
    int to = m - m_begin;
    if (m - m_begin > K::M_STEP) {
      to = K::M_STEP;
    }
    for (int i = 0; i < to; i++) {
      __m512 as = _mm512_set1_ps(*ba->get_scale(m, m_begin + i));
      __m512 bs = _mm512_load_ps(bb->get_scale(n, n_begin));
      __m512i now = _mm512_load_si512((__m512i*)(c + i * K::N_STEP));
      __m512 result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      _mm512_store_ps((__m512*)(c + i * K::N_STEP), result);
      bs = _mm512_load_ps(bb->get_scale(n, n_begin) + K::TILE_N);
      now = _mm512_load_si512((__m512i*)(c + i * K::N_STEP + K::TILE_N));
      result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      _mm512_store_ps((__m512*)(c + i * K::N_STEP + K::TILE_N), result);
    }
  }
};

struct GemmKernel224Int4 {
  using dt = void;
  using output_t = int32_t;
  static constexpr double ELEMENT_SIZE = 0.5;
  static constexpr int TILE_M = 16;
  static constexpr int TILE_K = 64;
  static constexpr int TILE_N = 16;
  static constexpr int VNNI_BLK = 4;

  static constexpr int M_STEP = TILE_M * 2;
  static constexpr int N_STEP = TILE_N * 2;
  static constexpr int K_STEP = TILE_K;

  // static inline const int N_BLOCK = 256;
  static inline const int N_BLOCK = 128;
  // static inline const int N_BLOCK = 64;
  // static inline const int K_BLOCK = 7168;
  static inline const int K_BLOCK = 3584;
  // static inline const int K_BLOCK = 2560;

  static std::string name() { return "INT4"; }

  static int recommended_nth(int n) { return (n + N_BLOCK - 1) / N_BLOCK; }

  static std::pair<int, int> split_range_n(int n, int ith, int nth) {
    int n_start = N_BLOCK * ith;
    int n_end = std::min(n, N_BLOCK * (ith + 1));
    return {n_start, n_end};
  }

  static void config() {
#ifdef HAVE_AMX
    enable_amx();
    TileConfig tile_config;

    // size is 16 x 64
    for (int i = 0; i < 2; i++) tile_config.set_row_col(i, TILE_M, TILE_K);

    // size is 16 x 64
    for (int i = 2; i < 4; i++) tile_config.set_row_col(i, TILE_K / VNNI_BLK, TILE_N * VNNI_BLK);

    // size is 16 x 16
    for (int i = 4; i < 8; i++) tile_config.set_row_col(i, TILE_M, TILE_N * sizeof(output_t));

    tile_config.set_config();
#endif
  }

  alignas(64) static constexpr uint8_t hi_mask_arr[64] = {
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0};

  alignas(64) static constexpr uint8_t lo_mask_arr[64] = {
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F};

  alignas(64) static constexpr uint8_t sign_mask_arr[64] = {
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
  };

  static __m512i hi_mask() { return *((__m512i*)(&hi_mask_arr[0])); }
  static __m128i hi_mask_128() { return *((__m128i*)(&hi_mask_arr[0])); }
  static __m512i lo_mask() { return *((__m512i*)(&lo_mask_arr[0])); }
  static __m128i lo_mask_128() { return *((__m128i*)(&lo_mask_arr[0])); }
  static __m128i si_mask_128() { return *((__m128i*)(&sign_mask_arr[0])); }

  static void load_b_hi(dt* b, size_t ldb) {
#ifdef HAVE_AMX
    // 在函数内部分配一个局部(栈上)对齐缓冲区
    alignas(64) int8_t local_buffer[TILE_N * TILE_K];
    __m512i* db = reinterpret_cast<__m512i*>(local_buffer);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_and_si512(hi_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * i)));
    }
    asm volatile("" ::: "memory");
    _tile_loadd(2, db, TILE_K);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_and_si512(hi_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * (i + TILE_N))));
    }
    asm volatile("" ::: "memory");
    _tile_loadd(3, db, TILE_K);
#else
    (void)b;
    (void)ldb;
#endif
  }

  static void load_b_lo(dt* b, size_t ldb) {
#ifdef HAVE_AMX
    // 在函数内部分配一个局部(栈上)对齐缓冲区
    alignas(64) int8_t local_buffer[TILE_N * TILE_K];
    __m512i* db = reinterpret_cast<__m512i*>(local_buffer);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_slli_epi32(_mm512_and_si512(lo_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * i))), 4);
    }
    asm volatile("" ::: "memory");
    _tile_loadd(2, db, TILE_K);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_slli_epi32(
          _mm512_and_si512(lo_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * (i + TILE_N)))), 4);
    }
    asm volatile("" ::: "memory");
    _tile_loadd(3, db, TILE_K);
#else
    (void)b;
    (void)ldb;
#endif
  }

  static void load_a(dt* a, size_t lda) {
#ifdef HAVE_AMX
    _tile_stream_loadd(0, a, lda);
    _tile_stream_loadd(1, offset_pointer(a, lda * TILE_M), lda);
#else
    (void)a;
    (void)lda;
#endif
  }

  static void clean_c() {
#ifdef HAVE_AMX
    _tile_zero(4);
    _tile_zero(5);
    _tile_zero(6);
    _tile_zero(7);
#endif
  }

  static void load_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_loadd(4, c, ldc);
    _tile_loadd(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_loadd(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_loadd(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void store_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_stored(4, c, ldc);
    _tile_stored(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_stored(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_stored(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void run_tile() {
#ifdef HAVE_AMX
    _tile_dpbssd(4, 0, 2);
    _tile_dpbssd(5, 0, 3);
    _tile_dpbssd(6, 1, 2);
    _tile_dpbssd(7, 1, 3);
#endif
  }

  using BufferA = BufferAImpl<GemmKernel224Int4>;
  using BufferB = BufferBInt4Impl<GemmKernel224Int4>;
  using BufferC = BufferCImpl<GemmKernel224Int4>;

  static void avx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, float* c, BufferA* ba,
                         BufferB* bb) {
    using K = GemmKernel224Int4;
    __m512i* c512 = (__m512i*)c;
    int m_block_end = std::min(m - m_begin, M_STEP);
    if (k_block_begin == 0) {
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        c512[m_i * 2] = _mm512_setzero_si512();
        c512[m_i * 2 + 1] = _mm512_setzero_si512();
      }
    }

    for (int k_begin = 0; k_begin < K::K_BLOCK && k_block_begin + k_begin < k; k_begin += K::BufferB::B_K_STEP) {
      int32_t* a32_lo = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin + k_begin);
      int32_t* a32_hi = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin + k_begin + K::K_STEP);
      __m512i* b512 = (__m512i*)bb->get_submat(n, k, n_begin, k_block_begin + k_begin);
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        for (int k_i = 0; k_i < 16; k_i++) {
          __m512i ma_lo = _mm512_set1_epi32(a32_lo[m_i * 16 + k_i]);
          __m512i ma_hi = _mm512_set1_epi32(a32_hi[m_i * 16 + k_i]);
          for (int n_i = 0; n_i < 2; n_i++) {
            __m512i b512_lo = _mm512_slli_epi32(_mm512_and_si512(K::lo_mask(), b512[n_i * 16 + k_i]), 4);
            c512[m_i * 2 + n_i] = _mm512_dpbssd_epi32(c512[m_i * 2 + n_i], ma_lo, b512_lo);
            __m512i b512_hi = _mm512_and_si512(K::hi_mask(), b512[n_i * 16 + k_i]);
            c512[m_i * 2 + n_i] = _mm512_dpbssd_epi32(c512[m_i * 2 + n_i], ma_hi, b512_hi);
          }
        }
      }
    }
  }
  static void amx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, float* c, BufferA* ba,
                         BufferB* bb) {
    using K = GemmKernel224Int4;
    if (k_block_begin == 0) {
      K::clean_c();
    } else {
      // printf("load from c int4\n");
      K::load_c((int32_t*)c, K::N_STEP * sizeof(int32_t));
    }
    for (int k_begin = 0; k_begin < K::K_BLOCK && k_block_begin + k_begin < k; k_begin += K::BufferB::B_K_STEP) {
      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin + k_begin), K::K_STEP * sizeof(int8_t));
      K::load_b_lo(bb->get_submat(n, k, n_begin, k_block_begin + k_begin), K::BufferB::B_K_STEP / 2);
      K::run_tile();
      // DEBUG
      // if(m_begin == 0 && n_begin == 0 && k_begin==0){
      //   int8_t *ba_ptr = ba->get_submat(m, k, m_begin, k_block_begin + k_begin);
      //   int8_t *bb_ptr = (int8_t *)bb->get_submat(n, k, n_begin, k_block_begin + k_begin);
      //   printf("k_begin:%d,k_block_begin:%d\n",k_begin,k_block_begin);
      //   for(int j=0;j<4096;j++){
      //     printf("a[%d]: %d ", j, ba_ptr[j]);
      //   }
      //   printf("\n");
      //   for(int j=0;j<4096;j++){
      //     printf("b[%d]: %d ", j, bb_ptr[j]);
      //   }
      //   printf("\n");
      // }

      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin + k_begin + K::K_STEP), K::K_STEP * sizeof(int8_t));
      K::load_b_hi(bb->get_submat(n, k, n_begin, k_block_begin + k_begin), K::BufferB::B_K_STEP / 2);
      K::run_tile();
    }

    // debug_tiles_224();
    K::store_c((int32_t*)c, K::N_STEP * sizeof(int32_t));
    // DEBUG c 的值,第一行的前 30 列
    // printf("\nint4, m_begin:%d,n_begin:%d,k_block_begin:%d\n",m_begin,n_begin,k_block_begin);
    // for(int j=0;j<30;j++){
    //   printf("c[%d]: %d ", j, ((int32_t *)c)[j]);
    // }
    // printf("\n");
  }

  static void apply_scale(int m, int n, int m_begin, int n_begin, float* c, BufferA* ba, BufferB* bb) {
    using K = GemmKernel224Int4;
    int to = m - m_begin;
    if (m - m_begin > K::M_STEP) {
      to = K::M_STEP;
    }
    for (int i = 0; i < to; i++) {
      __m512 as = _mm512_set1_ps(*ba->get_scale(m, m_begin + i));
      __m512 bs = _mm512_load_ps(bb->get_scale(n, n_begin));
      __m512i now = _mm512_load_epi32((__m512i*)(c + i * K::N_STEP));
      __m512 result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      // if(i==0){
      //   printf("\nnormal\n");
      //   printf("m_begin:%d,n_begin:%d\n", m_begin, n_begin);
      //   // 打印 result 结果，16 个 float 数值
      //   for(int j = 0; j < 16; j++) {
      //     float val = *((float *) &result + j);
      //     int32_t now_val = *((int32_t *) &now + j);
      //     printf("result[%d]: %f,now:%d ", j, val, now_val);
      //   }
      //   printf("\n");
      // }
      _mm512_store_ps((__m512*)(c + i * K::N_STEP), result);
      bs = _mm512_load_ps(bb->get_scale(n, n_begin) + K::TILE_N);
      now = _mm512_load_si512((__m512i*)(c + i * K::N_STEP + K::TILE_N));
      result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      // if(i==0){
      //   printf("\nnormal\n");
      //   printf("m_begin:%d,n_begin:%d\n", m_begin, n_begin);
      //   // 打印 result 结果，16 个 float 数值
      //   for(int j = 0; j < 16; j++) {
      //     float val = *((float *) &result + j);
      //     int32_t now_val = *((int32_t *) &now + j);
      //     printf("result[%d]: %f,now:%d ", j+16, val, now_val);
      //   }
      //   printf("\n");
      // }
      _mm512_store_ps((__m512*)(c + i * K::N_STEP + K::TILE_N), result);
    }
  }
};

struct GemmKernel224Int4_1 {
  using dt = void;
  using output_t = int32_t;
  static constexpr double ELEMENT_SIZE = 0.5;
  static constexpr int TILE_M = 16;
  static constexpr int TILE_K = 64;
  static constexpr int TILE_N = 16;
  static constexpr int VNNI_BLK = 4;

  static constexpr int M_STEP = TILE_M * 2;
  static constexpr int N_STEP = TILE_N * 2;
  static constexpr int K_STEP = TILE_K;

  static inline const int N_BLOCK = 256;
  // static inline const int K_BLOCK = 7168;
  static inline const int K_BLOCK = 3584;
  // static inline const int K_BLOCK = 2560;
  static std::string name() { return "INT4_1"; }

  static int recommended_nth(int n) { return (n + N_BLOCK - 1) / N_BLOCK; }

  static std::pair<int, int> split_range_n(int n, int ith, int nth) {
    int n_start = N_BLOCK * ith;
    int n_end = std::min(n, N_BLOCK * (ith + 1));
    return {n_start, n_end};
  }

  static void config() {
#ifdef HAVE_AMX
    enable_amx();
    TileConfig tile_config;

    // size is 16 x 64
    for (int i = 0; i < 2; i++) tile_config.set_row_col(i, TILE_M, TILE_K);

    // size is 16 x 64
    for (int i = 2; i < 4; i++) tile_config.set_row_col(i, TILE_K / VNNI_BLK, TILE_N * VNNI_BLK);

    // size is 16 x 16
    for (int i = 4; i < 8; i++) tile_config.set_row_col(i, TILE_M, TILE_N * sizeof(output_t));

    tile_config.set_config();
#endif
  }

  alignas(64) static constexpr uint8_t hi_mask_arr[64] = {
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0};

  alignas(64) static constexpr uint8_t lo_mask_arr[64] = {
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F};

  alignas(64) static constexpr uint8_t sign_mask_arr[64] = {
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
  };

  static __m512i hi_mask() { return *((__m512i*)(&hi_mask_arr[0])); }
  static __m128i hi_mask_128() { return *((__m128i*)(&hi_mask_arr[0])); }
  static __m512i lo_mask() { return *((__m512i*)(&lo_mask_arr[0])); }
  static __m128i lo_mask_128() { return *((__m128i*)(&lo_mask_arr[0])); }
  static __m128i si_mask_128() { return *((__m128i*)(&sign_mask_arr[0])); }

  static void load_b_hi(dt* b, size_t ldb) {
#ifdef HAVE_AMX
    // 在函数内部分配一个局部(栈上)对齐缓冲区
    alignas(64) int8_t local_buffer[TILE_N * TILE_K];
    __m512i* db = reinterpret_cast<__m512i*>(local_buffer);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_and_si512(hi_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * i)));
    }
    asm volatile("" ::: "memory");
    _tile_loadd(2, db, TILE_K);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_and_si512(hi_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * (i + TILE_N))));
    }
    asm volatile("" ::: "memory");
    _tile_loadd(3, db, TILE_K);
#else
    (void)b;
    (void)ldb;
#endif
  }

  static void load_b_lo(dt* b, size_t ldb) {
#ifdef HAVE_AMX
    // 在函数内部分配一个局部(栈上)对齐缓冲区
    alignas(64) int8_t local_buffer[TILE_N * TILE_K];
    __m512i* db = reinterpret_cast<__m512i*>(local_buffer);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_slli_epi32(_mm512_and_si512(lo_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * i))), 4);
    }
    asm volatile("" ::: "memory");
    _tile_loadd(2, db, TILE_K);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_slli_epi32(
          _mm512_and_si512(lo_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * (i + TILE_N)))), 4);
    }
    asm volatile("" ::: "memory");
    _tile_loadd(3, db, TILE_K);
#else
    (void)b;
    (void)ldb;
#endif
  }

  static void load_a(dt* a, size_t lda) {
#ifdef HAVE_AMX
    _tile_loadd(0, a, lda);
    _tile_loadd(1, offset_pointer(a, lda * TILE_M), lda);
#else
    (void)a;
    (void)lda;
#endif
  }

  // static void load_b(dt* b, size_t ldb) {
  //   _tile_loadd(2, b, ldb);
  //   _tile_loadd(3, offset_pointer(b, ldb * TILE_N), ldb);
  // }

  static void clean_c() {
#ifdef HAVE_AMX
    _tile_zero(4);
    _tile_zero(5);
    _tile_zero(6);
    _tile_zero(7);
#endif
  }

  static void load_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_loadd(4, c, ldc);
    _tile_loadd(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_loadd(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_loadd(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void store_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_stored(4, c, ldc);
    _tile_stored(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_stored(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_stored(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void run_tile() {
#ifdef HAVE_AMX
    _tile_dpbsud(4, 0, 2);
    _tile_dpbsud(5, 0, 3);
    _tile_dpbsud(6, 1, 2);
    _tile_dpbsud(7, 1, 3);
#endif
  }

  using BufferA = BufferAWithSumImpl<GemmKernel224Int4_1>;

  using BufferB = BufferBInt4WithZeroImpl<GemmKernel224Int4_1>;

  using BufferC = BufferCImpl<GemmKernel224Int4_1>;

  static void avx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, float* c, BufferA* ba,
                         BufferB* bb) {
    using K = GemmKernel224Int4_1;
    __m512i* c512 = (__m512i*)c;
    int m_block_end = std::min(m - m_begin, M_STEP);
    if (k_block_begin == 0) {
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        c512[m_i * 2] = _mm512_setzero_si512();
        c512[m_i * 2 + 1] = _mm512_setzero_si512();
      }
    }
    for (int k_begin = 0; k_begin < K::K_BLOCK && k_block_begin + k_begin < k; k_begin += K::BufferB::B_K_STEP) {
      int32_t* a32_lo = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin + k_begin);
      int32_t* a32_hi = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin + k_begin + K::K_STEP);
      __m512i* b512 = (__m512i*)bb->get_submat(n, k, n_begin, k_block_begin + k_begin);
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        for (int k_i = 0; k_i < 16; k_i++) {
          __m512i ma_lo = _mm512_set1_epi32(a32_lo[m_i * 16 + k_i]);
          __m512i ma_hi = _mm512_set1_epi32(a32_hi[m_i * 16 + k_i]);
          for (int n_i = 0; n_i < 2; n_i++) {
            __m512i b512_lo = _mm512_slli_epi32(_mm512_and_si512(K::lo_mask(), b512[n_i * 16 + k_i]), 4);
            c512[m_i * 2 + n_i] = _mm512_dpbusd_epi32_compat(c512[m_i * 2 + n_i], b512_lo, ma_lo);
            __m512i b512_hi = _mm512_and_si512(K::hi_mask(), b512[n_i * 16 + k_i]);
            c512[m_i * 2 + n_i] = _mm512_dpbusd_epi32_compat(c512[m_i * 2 + n_i], b512_hi, ma_hi);
          }
        }
      }
    }
  }
  static void amx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, float* c, BufferA* ba,
                         BufferB* bb) {
    using K = GemmKernel224Int4_1;
    if (k_block_begin == 0) {
      K::clean_c();
    } else {
      K::load_c((int32_t*)c, K::N_STEP * sizeof(int32_t));
    }
    for (int k_begin = 0; k_begin < K::K_BLOCK && k_block_begin + k_begin < k; k_begin += K::BufferB::B_K_STEP) {
      // printf("offset a %ld\n", pointer_offset(ba->get_submat(m, k, m_begin, k_block_begin + k_begin),
      // ba->a)); printf("offset b %ld\n", pointer_offset(bb->get_submat(n, k, n_begin, k_block_begin +
      // k_begin), bb->b));
      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin + k_begin), K::K_STEP * sizeof(int8_t));
      K::load_b_lo(bb->get_submat(n, k, n_begin, k_block_begin + k_begin), K::BufferB::B_K_STEP / 2);
      K::run_tile();
      // DEBUG
      // if(m_begin == 0 && n_begin == 0 && k_begin==0){
      //   int8_t *ba_ptr = ba->get_submat(m, k, m_begin, k_block_begin + k_begin);
      //   int8_t *bb_ptr = (int8_t *)bb->get_submat(n, k, n_begin, k_block_begin + k_begin);
      //   printf("k_begin:%d,k_block_begin:%d\n",k_begin,k_block_begin);
      //   for(int j=0;j<2048;j++){
      //     printf("a[%d]: %d ", j, ba_ptr[j]);
      //   }
      //   printf("\n");
      //   for(int j=0;j<2048;j++){
      //     printf("b[%d]: %d ", j, bb_ptr[j]);
      //   }
      //   printf("\n");
      // }
      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin + k_begin + K::K_STEP), K::K_STEP * sizeof(int8_t));
      K::load_b_hi(bb->get_submat(n, k, n_begin, k_block_begin + k_begin), K::BufferB::B_K_STEP / 2);
      K::run_tile();
    }

    // debug_tiles_224();
    K::store_c((int32_t*)c, K::N_STEP * sizeof(int32_t));
    // DEBUG c 的值,第一行的前 30 列
    // printf("\nint4_1, m_begin:%d,n_begin:%d,k_block_begin:%d\n",m_begin,n_begin,k_block_begin);
    // for(int j=0;j<30;j++){
    //   printf("c[%d]: %d ", j, ((int32_t *)c)[j]);
    // }
    // printf("\n");
  }

  static void apply_scale(int m, int n, int m_begin, int n_begin, float* c, BufferA* ba, BufferB* bb) {
    using K = GemmKernel224Int4_1;
    int to = m - m_begin;
    if (m - m_begin > K::M_STEP) {
      to = K::M_STEP;
    }
    for (int i = 0; i < to; i++) {
      __m512 as = _mm512_set1_ps(*ba->get_scale(m, m_begin + i));
      __m512 asum = _mm512_set1_ps(*ba->get_sum(m, m_begin + i));

      __m512 bs = _mm512_load_ps(bb->get_scale(n, n_begin));
      __m512 b_mins = _mm512_load_ps(bb->get_min(n, n_begin));
      __m512i now = _mm512_load_epi32((__m512i*)(c + i * K::N_STEP));
      __m512 result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      result = _mm512_add_ps(result, _mm512_mul_ps(asum, b_mins));
      _mm512_store_ps((__m512*)(c + i * K::N_STEP), result);

      bs = _mm512_load_ps(bb->get_scale(n, n_begin) + K::TILE_N);
      b_mins = _mm512_load_ps(bb->get_min(n, n_begin) + K::TILE_N);
      now = _mm512_load_si512((__m512i*)(c + i * K::N_STEP + K::TILE_N));
      result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      result = _mm512_add_ps(result, _mm512_mul_ps(asum, b_mins));
      _mm512_store_ps((__m512*)(c + i * K::N_STEP + K::TILE_N), result);
    }
  }
};

template <typename TA, typename TB, typename TC>
void mat_mul_single(int m, int n, int k, TA* a, size_t lda, TB* b, size_t ldb, TC* c, size_t ldc);
template <>
inline void mat_mul_single(int m, int n, int k, int8_t* a, size_t lda, int8_t* b, size_t ldb, int32_t* c, size_t ldc) {
  using Kernel = GemmKernel133<32>;
  for (int m_begin = 0; m_begin < m; m_begin += GemmKernel133<32>::M_STEP) {
    int m_end = std::min(m_begin + GemmKernel133<32>::M_STEP, m);
    for (int n_begin = 0; n_begin < n; n_begin += GemmKernel133<32>::N_STEP) {
      int n_end = std::min(n_begin + GemmKernel133<32>::N_STEP, n);
      for (int k_begin = 0; k_begin < k; k_begin += GemmKernel133<32>::K_STEP) {
        int k_end = std::min(k_begin + GemmKernel133<32>::K_STEP, k);
        int8_t* as = offset_pointer_row_major(a, m_begin, k_begin, lda);
        int8_t* bs = offset_pointer_col_major(b, k_begin, n_begin, ldb);
        int32_t* cs = offset_pointer_row_major(c, m_begin, n_begin, ldc);
        GemmKernel133<32>::BTile<int8_t> tb;
        if (n_end - n_begin == GemmKernel133<32>::N_STEP && k_end - k_begin == GemmKernel133<32>::K_STEP) {
          tb.load_from(bs, ldb);
        } else {
          tb.partial_load(bs, n_end - n_begin, k_end - k_begin, ldb);
        }
        if (m_end - m_begin == GemmKernel133<32>::M_STEP && k_end - k_begin == GemmKernel133<32>::K_STEP) {
          // printf("sub mat mul, full tile: (%d,%d),(%d,%d),(%d,%d)\n",
          // m_begin, m_end, n_begin, n_end, k_begin, k_end);
          tb.run_full_ac(as, lda, cs, ldc);
        } else {
          // printf("sub mat mul, partial tile: (%d,%d),(%d,%d),(%d,%d)\n",
          // m_begin, m_end, n_begin, n_end, k_begin, k_end);
          GemmKernel133<32>::PartialTiles<int8_t, int8_t, int32_t> p;
          p.partial_run(m_end - m_begin, n_end - n_begin, k_end - k_begin, as, lda, bs, ldb, cs, ldc);
        }
      }
    }
  }
}

template <>
inline void mat_mul_single(int m, int n, int k, ggml_bf16_t* a, size_t lda, ggml_bf16_t* b, size_t ldb, float* c,
                           size_t ldc) {
  // // GemmKernel133BF::config();

  // for (int m_begin = 0; m_begin < m; m_begin += GemmKernel133BF::M_STEP) {
  //   int m_end = std::min(m_begin + GemmKernel133BF::M_STEP, m);
  //   for (int n_begin = 0; n_begin < n; n_begin += GemmKernel133BF::N_STEP) {
  //     int n_end = std::min(n_begin + GemmKernel133BF::N_STEP, n);

  //     for (int k_begin = 0; k_begin < k; k_begin += GemmKernel133BF::K_STEP)
  //     {
  //       int k_end = std::min(k_begin + GemmKernel133BF::K_STEP, k);

  //       ggml_bf16_t* as = offset_pointer_row_major(a, m_begin, k_begin, lda);
  //       ggml_bf16_t* bs = offset_pointer_col_major(b, k_begin, n_begin, ldb);
  //       GemmKernel133BF::BTile tb;
  //       if (n_end - n_begin == GemmKernel133BF::N_STEP && k_end - k_begin ==
  //       GemmKernel133BF::K_STEP) {
  //         tb.full_load(bs, ldb);
  //       } else {
  //         tb.partial_load(bs, n_end - n_begin, k_end - k_begin, ldb);
  //       }
  //       float* cs = offset_pointer_row_major(c, m_begin, n_begin, ldc);

  //       if (m_end - m_begin == GemmKernel133<32>::M_STEP && k_end - k_begin
  //       == GemmKernel133<32>::K_STEP) {
  //         // printf("sub mat mul, full tile: (%d,%d),(%d,%d),(%d,%d)\n",
  //         m_begin, m_end, n_begin, n_end, k_begin,
  //         // k_end);
  //         tb.run_full_ac(as, lda, cs, ldc);
  //       } else {
  //         // printf("sub mat mul, partial tile: (%d,%d),(%d,%d),(%d,%d)\n",
  //         m_begin, m_end, n_begin, n_end, k_begin,
  //         // k_end);
  //         GemmKernel133BF::PartialTiles p;
  //         p.partial_run(m_end - m_begin, n_end - n_begin, k_end - k_begin,
  //         as, lda, bs, ldb, cs, ldc);
  //       }
  //     }
  //   }
  // }
}

template <typename QA>
void mat_mul_single(int m, int n, int k, QA* a, size_t lda, block_q8_0* b, size_t ldb, float* c, size_t ldc) {
  // amx::init();
  assert(QK8_0 == 32);
  assert(QK4_0 == 32);
  assert(GemmKernel133<32>::K_STEP == 32);
  // assert(reinterpret_cast<intptr_t>(c) % 64 == 0);
  assert(ldc % 64 == 0);

  // GemmKernal133::config();
  for (int n_begin = 0; n_begin < n; n_begin += GemmKernel133<32>::N_STEP) {
    int n_end = std::min(n_begin + GemmKernel133<32>::N_STEP, n);

    for (int k_begin = 0; k_begin < k; k_begin += GemmKernel133<32>::K_STEP) {
      int k_end = std::min(k_begin + GemmKernel133<32>::K_STEP, k);
      int kb = k_begin / GemmKernel133<32>::K_STEP;
      block_q8_0* bs = offset_pointer_col_major(b, kb, n_begin, ldb);
      GemmKernel133<32>::PartialTiles<int8_t, int8_t, int32_t> p;
      p.tb.partial_load_quant(bs, n_end - n_begin, k_end - k_begin, ldb);
      for (int m_begin = 0; m_begin < m; m_begin += GemmKernel133<32>::M_STEP) {
        int m_end = std::min(m_begin + GemmKernel133<32>::M_STEP, m);
        QA* as = offset_pointer_row_major(a, m_begin, kb, lda);

        float* cs = offset_pointer_row_major(c, m_begin, n_begin, ldc);
        // printf("sub mat mul: (%d,%d),(%d,%d),(%d,%d) %ld %ld\n", m_begin,
        // m_end, n_begin, n_end, k_begin, k_end,as-a,bs-b);

        // p.partial_run_quant(m_end - m_begin, n_end - n_begin, k_end -
        // k_begin, as, lda, bs, ldb, cs, ldc);
        p.partial_run_quant_ac(m_end - m_begin, n_end - n_begin, k_end - k_begin, as, lda, cs, ldc);
      }
    }
  }
}

inline void mat_mul_single(int m, int n, int k, block_q4_K* a, size_t lda, block_q8_K* b, size_t ldb, float* c,
                           size_t ldc) {
  assert(QK_K == 256);
  assert(k % QK_K == 0);
  assert(QK_K % GemmKernel133<32>::K_STEP == 0);
  assert(GemmKernel133<32>::K_STEP == 32);
  assert(ldc % 64 == 0);

  for (int m_begin = 0; m_begin < m; m_begin += GemmKernel133<32>::M_STEP) {
    int m_end = std::min(m_begin + GemmKernel133<32>::M_STEP, m);
    for (int n_begin = 0; n_begin < n; n_begin += GemmKernel133<32>::N_STEP) {
      int n_end = std::min(n_begin + GemmKernel133<32>::N_STEP, n);
      float* cs = offset_pointer_row_major(c, m_begin, n_begin, ldc);
      for (int k_bigstart = 0; k_bigstart < k; k_bigstart += QK_K) {
        int k_bigend = k_bigstart + QK_K;
        int super_block_index = k_bigstart / QK_K;

        block_q8_K* super_bs = offset_pointer_col_major(b, super_block_index, n_begin, ldb);

        block_q4_K* super_as = offset_pointer_row_major(a, m_begin, super_block_index, lda);
        float super_scale = GGML_FP16_TO_FP32(super_as->d);
        float super_min = GGML_FP16_TO_FP32(super_as->dmin);
        __m512 a_sm = _mm512_mul_ps(
            _mm512_cvtepi32_ps(_mm512_cvtepu8_epi32(make_q4K_scale_and_min(super_as->scales))),
            _mm512_insertf32x8(_mm512_castps256_ps512(_mm256_set1_ps(super_scale)), _mm256_set1_ps(super_min), 1));
        float* a_scale = reinterpret_cast<float*>(&a_sm);
        float* a_min = a_scale + 8;

        for (int inner_idx = 0; inner_idx < 256 / 32; inner_idx++) {
          amx::GemmKernel133<32>::PartialTilesSum<uint8_t, int8_t, float> t;
          // printf("sub mat mul: (%d,%d),(%d,%d),(%d,%d) %d\n", m_begin, m_end,
          // n_begin, n_end, k_bigstart,
          //        k_bigend,inner_idx);
          t.tb.partial_load_quant(super_bs, n_end - n_begin, inner_idx, ldb);
          t.partial_run_quant_ac(m_end - m_begin, n_end - n_begin, inner_idx, super_as, lda, cs, ldc,
                                 a_scale[inner_idx], a_min[inner_idx]);
        }
      }
    }
  }
}

inline void mat_mul_single(int m, int n, int k, blocks_aligned_q8_0_ref a, int a_blck_stride, blocks_aligned_q8_0_ref b,
                           int b_blck_stride, float* c, size_t ldc) {
  using Kernel = GemmKernel133<64>;
  using TA = uint8_t;
  using TB = int8_t;

  for (int m_begin = 0; m_begin < m; m_begin += Kernel::M_STEP) {
    int m_end = std::min(m_begin + Kernel::M_STEP, m);
    for (int n_begin = 0; n_begin < n; n_begin += Kernel::N_STEP) {
      int n_end = std::min(n_begin + Kernel::N_STEP, n);
      for (int k_begin = 0; k_begin < k; k_begin += Kernel::K_STEP) {
        int k_end = std::min(k_begin + Kernel::K_STEP, k);

        int k_block = k_begin / Kernel::K_STEP;

        auto as = a.offset(m_begin * a_blck_stride + k_block);
        auto bs = b.offset(n_begin * b_blck_stride + k_block);
        auto cs = offset_pointer_row_major(c, m_begin, n_begin, ldc);

        // printf("sub mat mul: (%d,%d),(%d,%d),(%d,%d) %ld %ld\n", m_begin,
        // m_end, n_begin, n_end, k_begin, k_end,as.d-a.d,bs.d-b.d);

        Kernel::PartialTiles<TA, TB, int32_t> t;
        t.tb.partial_load_quant(bs, n_end - n_begin, k_end - k_begin, b_blck_stride);
        t.partial_run_quant_ac(m_end - m_begin, n_end - n_begin, k_end - k_begin, as, a_blck_stride, cs, ldc);
      }
    }
  }
}

inline void merge_mat(int d0, int d1, float* a, float* b, size_t ld) {
  __m512* va = (__m512*)a;
  __m512* vb = (__m512*)b;

  size_t d1v = (d1 + 15) / 16;

  for (int i = 0; i < d0; i++) {
    auto ta = offset_pointer_row_major(va, i, 0, ld);
    auto tb = offset_pointer_row_major(vb, i, 0, ld);
    for (int j = 0; j < d1v; j++) {
      ta[j] = _mm512_add_ps(ta[j], tb[j]);
    }
  }
}

inline void merge_mats(int d0, int d1, int cnt, float** data, size_t ld) {
  for (int i = 0; i < cnt; i++) {
    assert((intptr_t)data[i] % 64 == 0);
    assert(ld % 64 == 0);
  }

  while (cnt > 1) {
    int new_cnt = (cnt + 1) / 2;
    for (int i = 0; i < new_cnt; i++) {
      int j = new_cnt + i;
      if (j < cnt) {
        // printf("merge %d %d\n", i, j);
        merge_mat(d0, d1, data[i], data[j], ld);
      }
    }
    cnt = new_cnt;
  }
}

template <typename TA, typename TB, typename TC>
struct GemmKernel {
  static_assert(sizeof(TA) == -1, "No associated type defined for this type.");
  using type = GemmKernel224BF;
};

template <typename TB>
struct GemmKernel<uint8_t, TB, float> {
  using type = GemmKernel133<32>;
};

template <typename TB>
struct GemmKernel<int8_t, TB, float> {
  using type = GemmKernel133<32>;
};

template <>
struct GemmKernel<block_q4_0, block_q8_0, float> {
  using type = GemmKernel133<32>;
};

template <>
struct GemmKernel<block_q8_0, block_q8_0, float> {
  using type = GemmKernel133<32>;
};

template <>
struct GemmKernel<block_q4_K, block_q8_K, float> {
  using type = GemmKernel133<32>;
};

template <>
struct GemmKernel<ggml_bf16_t, ggml_bf16_t, float> {
  // using type = GemmKernel133BF;
  using type = GemmKernel224BF;
};

// template <typename TA, typename TB, typename TC>
// void mat_mul(int m, int n, int k, TA* a, size_t lda, TB* b, size_t ldb, TC*
// c, size_t ldc, int ith, int nth) {
//   using K = typename GemmKernel<TA, TB, TC>::type;

//   int m_partition_count = (m + K::M_STEP - 1) / K::M_STEP;
//   int partition_count_per_thread = (m_partition_count + nth - 1) / nth;
//   int partition_start = ith * partition_count_per_thread;
//   int partition_end = std::min(partition_start + partition_count_per_thread,
//   m_partition_count); int m_start = partition_start * K::M_STEP; int m_end =
//   std::min(m, partition_end * K::M_STEP);

//   mat_mul_single(m_end - m_start, n, k, offset_pointer(a, m_start * lda),
//   lda, b, ldb, offset_pointer(c, m_start * ldc),
//                  ldc);
// }

template <typename TA, typename TB, typename TC>
void mat_mul(int m, int n, int k, TA* a, size_t lda, TB* b, size_t ldb, TC* c, size_t ldc, int ith, int nth) {
  using K = typename GemmKernel<TA, TB, TC>::type;

  int n_partition_count = (n + K::N_STEP - 1) / K::N_STEP;
  int partition_count_per_thread = (n_partition_count + nth - 1) / nth;
  int partition_start = ith * partition_count_per_thread;
  int partition_end = std::min(partition_start + partition_count_per_thread, n_partition_count);
  int n_start = partition_start * K::N_STEP;
  int n_end = std::min(n, partition_end * K::N_STEP);

  mat_mul_single(m, n_end - n_start, k, a, lda, offset_pointer_col_major(b, 0, n_start, ldb), ldb,
                 offset_pointer_row_major(c, 0, n_start, ldc), ldc);
}

inline void mat_mul(int m, int n, int k, std::shared_ptr<GemmKernel224BF::BufferA> ba,
                    std::shared_ptr<GemmKernel224BF::BufferB> bb, std::shared_ptr<GemmKernel224BF::BufferC> bc, int ith,
                    int nth) {
  using K = GemmKernel224BF;
  assert(n % K::N_STEP == 0);
  assert(k % K::K_STEP == 0);

  auto [n_start, n_end] = K::split_range_n(n, ith, nth);

  // printf("n_start %d n_end %d\n", n_start, n_end);
  for (int k_block_begin = 0; k_block_begin < k; k_block_begin += K::K_BLOCK) {
    for (int m_begin = 0; m_begin < m; m_begin += K::M_STEP) {
      for (int n_begin = n_start; n_begin < n_end; n_begin += K::N_STEP) {
        float* c = bc->get_submat(m, n, m_begin, n_begin);
        // if (m - m_begin == 1) {
        if (false) {
          // if(k_block_begin==0&&m_begin==0&&n_begin==n_start)
          // printf("AVX");
          __m512* c512 = (__m512*)c;
          if (k_block_begin == 0) {
            for (int m_i = 0; m_i < m; m_i++) {
              c512[m_i * 2] = _mm512_setzero_ps();
              c512[m_i * 2 + 1] = _mm512_setzero_ps();
            }
          }

          for (int k_begin = 0; k_begin < K::K_BLOCK && k_block_begin + k_begin < k; k_begin += K::K_STEP) {
            int32_t* a32 = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin + k_begin);
            __m512bh* b512 = (__m512bh*)bb->get_submat(n, k, n_begin, k_block_begin + k_begin);
            for (int m_i = 0; m_i < m; m_i++) {
              for (int k_i = 0; k_i < 16; k_i++) {
                __m512bh ma = (__m512bh)_mm512_set1_epi32(a32[m_i * 16 + k_i]);
                for (int n_i = 0; n_i < 2; n_i++) {
                  c512[m_i * 2 + n_i] = _mm512_dpbf16_ps(c512[m_i * 2 + n_i], ma, b512[n_i * 16 + k_i]);
                }
              }
            }
          }

        } else {
          if (k_block_begin == 0) {
            K::clean_c();
          } else {
            K::load_c(c, K::N_STEP * sizeof(float));
          }
          for (int k_begin = 0; k_begin < K::K_BLOCK && k_block_begin + k_begin < k; k_begin += K::K_STEP) {
            K::load_a(ba->get_submat(m, k, m_begin, k_block_begin + k_begin), K::K_STEP * sizeof(ggml_bf16_t));
            K::load_b(bb->get_submat(n, k, n_begin, k_block_begin + k_begin), K::K_STEP * sizeof(ggml_bf16_t));
            K::run_tile();
          }
          K::store_c(c, K::N_STEP * sizeof(float));
        }
      }
    }
  }
}

inline void vec_mul(int m, int n, int k, std::shared_ptr<GemmKernel224BF::BufferA> ba,
                    std::shared_ptr<GemmKernel224BF::BufferB> bb, std::shared_ptr<GemmKernel224BF::BufferC> bc, int ith,
                    int nth) {
  mat_mul(m, n, k, ba, bb, bc, ith, nth);
}

template <typename K, bool amx_or_avx = true>
void integer_mat_mul(int m, int n, int k, typename K::BufferA* ba, typename K::BufferB* bb, typename K::BufferC* bc,
                     int ith, int nth) {
  assert(n % K::N_STEP == 0);
  assert(k % K::K_STEP == 0);

  auto [n_start, n_end] = K::split_range_n(n, ith, nth);

  for (int k_block_begin = 0; k_block_begin < k; k_block_begin += K::K_BLOCK) {
    for (int m_begin = 0; m_begin < m; m_begin += K::M_STEP) {
      for (int n_begin = n_start; n_begin < n_end; n_begin += K::N_STEP) {
        float* c = bc->get_submat(m, n, m_begin, n_begin);
        if constexpr (amx_or_avx && AMX_AVAILABLE) {
          K::amx_kernel(m, n, k, m_begin, n_begin, k_block_begin, c, ba, bb);
        } else {
          K::avx_kernel(m, n, k, m_begin, n_begin, k_block_begin, c, ba, bb);
        }

        if (k_block_begin + K::K_BLOCK >= k) {
          K::apply_scale(m, n, m_begin, n_begin, c, ba, bb);
        }
      }
    }
  }
}

inline void vec_mul(int m, int n, int k, std::shared_ptr<GemmKernel224Int8::BufferA> ba,
                    std::shared_ptr<GemmKernel224Int8::BufferB> bb, std::shared_ptr<GemmKernel224Int8::BufferC> bc,
                    int ith, int nth) {
  integer_mat_mul<GemmKernel224Int8, false>(m, n, k, ba.get(), bb.get(), bc.get(), ith, nth);
}

inline void mat_mul(int m, int n, int k, std::shared_ptr<GemmKernel224Int8::BufferA> ba,
                    std::shared_ptr<GemmKernel224Int8::BufferB> bb, std::shared_ptr<GemmKernel224Int8::BufferC> bc,
                    int ith, int nth) {
  integer_mat_mul<GemmKernel224Int8, true>(m, n, k, ba.get(), bb.get(), bc.get(), ith, nth);
}

inline void vec_mul(int m, int n, int k, std::shared_ptr<GemmKernel224Int4::BufferA> ba,
                    std::shared_ptr<GemmKernel224Int4::BufferB> bb, std::shared_ptr<GemmKernel224Int4::BufferC> bc,
                    int ith, int nth) {
  integer_mat_mul<GemmKernel224Int4, false>(m, n, k, ba.get(), bb.get(), bc.get(), ith, nth);
}

inline void mat_mul(int m, int n, int k, std::shared_ptr<GemmKernel224Int4::BufferA> ba,
                    std::shared_ptr<GemmKernel224Int4::BufferB> bb, std::shared_ptr<GemmKernel224Int4::BufferC> bc,
                    int ith, int nth) {
  integer_mat_mul<GemmKernel224Int4, true>(m, n, k, ba.get(), bb.get(), bc.get(), ith, nth);
}

inline void vec_mul(int m, int n, int k, std::shared_ptr<GemmKernel224Int4_1::BufferA> ba,
                    std::shared_ptr<GemmKernel224Int4_1::BufferB> bb, std::shared_ptr<GemmKernel224Int4_1::BufferC> bc,
                    int ith, int nth) {
  integer_mat_mul<GemmKernel224Int4_1, false>(m, n, k, ba.get(), bb.get(), bc.get(), ith, nth);
}

inline void mat_mul(int m, int n, int k, std::shared_ptr<GemmKernel224Int4_1::BufferA> ba,
                    std::shared_ptr<GemmKernel224Int4_1::BufferB> bb, std::shared_ptr<GemmKernel224Int4_1::BufferC> bc,
                    int ith, int nth) {
  integer_mat_mul<GemmKernel224Int4_1, true>(m, n, k, ba.get(), bb.get(), bc.get(), ith, nth);
}

inline void mat_mul(int m, int n, int k, blocks_aligned_q8_0_ref aref, int a_blck_stride, blocks_aligned_q8_0_ref bref,
                    int b_blck_stride, float* c, size_t ldc, int ith, int nth) {
  using K = GemmKernel133<64>;

  int m_partition_count = (m + K::M_STEP - 1) / K::M_STEP;
  int partition_count_per_thread = (m_partition_count + nth - 1) / nth;
  int partition_start = ith * partition_count_per_thread;
  int partition_end = std::min(partition_start + partition_count_per_thread, m_partition_count);
  int m_start = partition_start * K::M_STEP;
  int m_end = std::min(m, partition_end * K::M_STEP);

  mat_mul_single(m_end - m_start, n, k, aref.offset(m_start * a_blck_stride), a_blck_stride, bref, b_blck_stride,
                 offset_pointer(c, m_start * ldc), ldc);
}

// K-group quantization kernel with intermediate int32 accumulation
struct GemmKernel224Int4KGroup {
  using dt = void;
  using output_t = int32_t;
  static constexpr double ELEMENT_SIZE = 0.5;
  static constexpr int TILE_M = 16;
  static constexpr int TILE_K = 64;
  static constexpr int TILE_N = 16;
  static constexpr int VNNI_BLK = 4;
  static constexpr int M_STEP = TILE_M * 2;
  static constexpr int N_STEP = TILE_N * 2;
  static constexpr int K_STEP = TILE_K;
  static inline const int N_BLOCK = 256;
  // K_BLOCK should match k_group_size for proper scaling
  static inline const int K_BLOCK = 7168;  // Will be overridden by k_group_size

  static std::string name() { return "INT4_KGROUP"; }
  static int recommended_nth(int n) { return (n + N_BLOCK - 1) / N_BLOCK; }
  static std::pair<int, int> split_range_n(int n, int ith, int nth) {
    int n_start = N_BLOCK * ith;
    int n_end = std::min(n, N_BLOCK * (ith + 1));
    return {n_start, n_end};
  }

  static void config() {
#ifdef HAVE_AMX
    enable_amx();
    TileConfig tile_config;
    // size is 16 x 64
    for (int i = 0; i < 2; i++) tile_config.set_row_col(i, TILE_M, TILE_K);
    // size is 16 x 64
    for (int i = 2; i < 4; i++) tile_config.set_row_col(i, TILE_K / VNNI_BLK, TILE_N * VNNI_BLK);
    // size is 16 x 16
    for (int i = 4; i < 8; i++) tile_config.set_row_col(i, TILE_M, TILE_N * sizeof(output_t));
    tile_config.set_config();
#endif
  }

  alignas(64) static constexpr uint8_t hi_mask_arr[64] = {
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0};

  alignas(64) static constexpr uint8_t lo_mask_arr[64] = {
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F};

  static __m512i hi_mask() { return *((__m512i*)(&hi_mask_arr[0])); }
  static __m512i lo_mask() { return *((__m512i*)(&lo_mask_arr[0])); }

  static void clean_c() {
    _tile_zero(4);
    _tile_zero(5);
    _tile_zero(6);
    _tile_zero(7);
  }

  static void load_c(output_t* c, size_t ldc) {
    _tile_loadd(4, c, ldc);
    _tile_loadd(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_loadd(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_loadd(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
  }

  static void store_c(output_t* c, size_t ldc) {
    _tile_stored(4, c, ldc);
    _tile_stored(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_stored(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_stored(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
  }

  static void load_a(dt* a, size_t lda) {
    _tile_loadd(0, a, lda);
    _tile_loadd(1, offset_pointer(a, lda * TILE_M), lda);
  }

  static void load_b_lo(dt* b, size_t ldb) {
    alignas(64) int8_t local_buffer[TILE_N * TILE_K];
    __m512i* db = reinterpret_cast<__m512i*>(local_buffer);

    for (size_t i = 0; i < TILE_N; i++) {
      // __m512i temp = _mm512_and_si512(lo_mask(), *static_cast<__m512i *>(offset_pointer(b, ldb * i)));
      // db[i] = _mm512_slli_epi32(temp, 4);
      db[i] = _mm512_slli_epi32(_mm512_and_si512(lo_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * i))), 4);
    }
    asm volatile("" ::: "memory");
    _tile_loadd(2, db, TILE_K);

    for (size_t i = 0; i < TILE_N; i++) {
      // __m512i temp = _mm512_and_si512(lo_mask(), *static_cast<__m512i *>(offset_pointer(b, ldb * (i + TILE_N))));
      // db[i] = _mm512_slli_epi32(temp, 4);
      db[i] = _mm512_slli_epi32(
          _mm512_and_si512(lo_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * (i + TILE_N)))), 4);
    }
    asm volatile("" ::: "memory");
    _tile_loadd(3, db, TILE_K);
  }

  static void load_b_hi(dt* b, size_t ldb) {
    alignas(64) int8_t local_buffer[TILE_N * TILE_K];
    __m512i* db = reinterpret_cast<__m512i*>(local_buffer);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_and_si512(hi_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * i)));
    }
    asm volatile("" ::: "memory");
    _tile_loadd(2, db, TILE_K);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_and_si512(hi_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * (i + TILE_N))));
    }
    asm volatile("" ::: "memory");
    _tile_loadd(3, db, TILE_K);
  }

  static void run_tile() {
#ifdef HAVE_AMX
    _tile_dpbssd(4, 0, 2);
    _tile_dpbssd(5, 0, 3);
    _tile_dpbssd(6, 1, 2);
    _tile_dpbssd(7, 1, 3);
#endif
  }

  using BufferA = BufferAKGroupImpl<GemmKernel224Int4KGroup>;
  using BufferB = BufferBKGroupImpl<GemmKernel224Int4KGroup>;
  using BufferC = BufferCReduceImpl<GemmKernel224Int4KGroup>;

  // K-group aware AVX kernel - processes a single B_K_STEP chunk
  static void avx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, int32_t* int_c, BufferA* ba,
                         BufferB* bb, int k_group_size) {
    using K = GemmKernel224Int4KGroup;
    __m512i* c512 = (__m512i*)int_c;
    int m_block_end = std::min(m - m_begin, M_STEP);

    // Initialize int_c to zero at the start of k_group
    if (k_block_begin % k_group_size == 0) {
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        c512[m_i * 2] = _mm512_setzero_si512();
        c512[m_i * 2 + 1] = _mm512_setzero_si512();
      }
    }
    int k_offset = k_block_begin % K::BufferB::B_K_STEP;
    if (k_offset == 0) {
      int32_t* a32_lo = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin);
      __m512i* b512 = (__m512i*)bb->get_submat(n, k, n_begin, k_block_begin);
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        for (int k_i = 0; k_i < 16; k_i++) {
          __m512i ma_lo = _mm512_set1_epi32(a32_lo[m_i * 16 + k_i]);
          for (int n_i = 0; n_i < 2; n_i++) {
            __m512i b512_lo = _mm512_slli_epi32(_mm512_and_si512(K::lo_mask(), b512[n_i * 16 + k_i]), 4);
            c512[m_i * 2 + n_i] = _mm512_dpbssd_epi32(c512[m_i * 2 + n_i], ma_lo, b512_lo);
          }
        }
      }
    } else {
      int32_t* a32_hi = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin);
      __m512i* b512 = (__m512i*)bb->get_submat(n, k, n_begin, k_block_begin - K::K_STEP);
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        for (int k_i = 0; k_i < 16; k_i++) {
          __m512i ma_hi = _mm512_set1_epi32(a32_hi[m_i * 16 + k_i]);
          for (int n_i = 0; n_i < 2; n_i++) {
            __m512i b512_hi = _mm512_and_si512(K::hi_mask(), b512[n_i * 16 + k_i]);
            c512[m_i * 2 + n_i] = _mm512_dpbssd_epi32(c512[m_i * 2 + n_i], ma_hi, b512_hi);
          }
        }
      }
    }
  }

  // K-group aware AMX kernel - processes a single K_STEP chunk (lo or hi nibble)
  static void amx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, int32_t* int_c, BufferA* ba,
                         BufferB* bb, int k_group_size) {
    using K = GemmKernel224Int4KGroup;
    // Initialize or load int_c at start of k_group
    if (k_block_begin % k_group_size == 0) {
      K::clean_c();
    } else {
      K::load_c(int_c, K::N_STEP * sizeof(int32_t));
    }

    // Determine if we're processing lo or hi nibble based on position within B_K_STEP
    int k_offset = k_block_begin % K::BufferB::B_K_STEP;
    if (k_offset == 0) {
      // Process lo nibble
      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin), K::K_STEP * sizeof(int8_t));
      K::load_b_lo(bb->get_submat(n, k, n_begin, k_block_begin), K::BufferB::B_K_STEP / 2);
      K::run_tile();
    } else {
      // Process hi nibble (k_offset == K_STEP)
      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin), K::K_STEP * sizeof(int8_t));
      K::load_b_hi(bb->get_submat(n, k, n_begin, k_block_begin - K::K_STEP), K::BufferB::B_K_STEP / 2);
      K::run_tile();
    }

    K::store_c(int_c, K::N_STEP * sizeof(int32_t));
  }

  // K-group aware scale application
  static void apply_scale_kgroup(int m, int n, int m_begin, int n_begin, int k_begin, float* c, int32_t* int_c,
                                 BufferA* ba, BufferB* bb, int k, int k_group_size) {
    using K = GemmKernel224Int4KGroup;
    int to = m - m_begin;
    if (m - m_begin > K::M_STEP) {
      to = K::M_STEP;
    }

    for (int i = 0; i < to; i++) {
      // Get scale for this k_group
      __m512 as = _mm512_set1_ps(*ba->get_scale(m, m_begin + i, k, k_begin));
      __m512 bs = _mm512_load_ps(bb->get_scale(n, n_begin, k, k_begin));
      __m512i now = _mm512_load_epi32((__m512i*)(int_c + i * K::N_STEP));
      __m512 result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      // Load existing float value from c and add
      __m512 existing = _mm512_load_ps((__m512*)(c + i * K::N_STEP));
      result = _mm512_add_ps(existing, result);
      _mm512_store_ps((__m512*)(c + i * K::N_STEP), result);

      // Second half
      bs = _mm512_load_ps(bb->get_scale(n, n_begin, k, k_begin) + K::TILE_N);
      now = _mm512_load_si512((__m512i*)(int_c + i * K::N_STEP + K::TILE_N));
      result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      existing = _mm512_load_ps((__m512*)(c + i * K::N_STEP + K::TILE_N));
      result = _mm512_add_ps(existing, result);
      _mm512_store_ps((__m512*)(c + i * K::N_STEP + K::TILE_N), result);
    }
  }
};
struct GemmKernel224Int4_1KGroup {
  using dt = void;
  using output_t = int32_t;
  static constexpr double ELEMENT_SIZE = 0.5;
  static constexpr int TILE_M = 16;
  static constexpr int TILE_K = 64;
  static constexpr int TILE_N = 16;
  static constexpr int VNNI_BLK = 4;

  static constexpr int M_STEP = TILE_M * 2;
  static constexpr int N_STEP = TILE_N * 2;
  static constexpr int K_STEP = TILE_K;

  static inline const int N_BLOCK = 256;
  // static inline const int K_BLOCK = 7168;
  static inline const int K_BLOCK = 3584;
  // static inline const int K_BLOCK = 2560;
  static std::string name() { return "INT4_1K"; }

  static int recommended_nth(int n) { return (n + N_BLOCK - 1) / N_BLOCK; }

  static std::pair<int, int> split_range_n(int n, int ith, int nth) {
    int n_start = N_BLOCK * ith;
    int n_end = std::min(n, N_BLOCK * (ith + 1));
    return {n_start, n_end};
  }

  static void config() {
#ifdef HAVE_AMX
    enable_amx();
    TileConfig tile_config;

    // size is 16 x 64
    for (int i = 0; i < 2; i++) tile_config.set_row_col(i, TILE_M, TILE_K);

    // size is 16 x 64
    for (int i = 2; i < 4; i++) tile_config.set_row_col(i, TILE_K / VNNI_BLK, TILE_N * VNNI_BLK);

    // size is 16 x 16
    for (int i = 4; i < 8; i++) tile_config.set_row_col(i, TILE_M, TILE_N * sizeof(output_t));

    tile_config.set_config();
#endif
  }

  alignas(64) static constexpr uint8_t hi_mask_arr[64] = {
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0};

  alignas(64) static constexpr uint8_t lo_mask_arr[64] = {
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F};

  alignas(64) static constexpr uint8_t sign_mask_arr[64] = {
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
  };

  static __m512i hi_mask() { return *((__m512i*)(&hi_mask_arr[0])); }
  static __m128i hi_mask_128() { return *((__m128i*)(&hi_mask_arr[0])); }
  static __m512i lo_mask() { return *((__m512i*)(&lo_mask_arr[0])); }
  static __m128i lo_mask_128() { return *((__m128i*)(&lo_mask_arr[0])); }
  static __m128i si_mask_128() { return *((__m128i*)(&sign_mask_arr[0])); }

  static void load_b_hi(dt* b, size_t ldb) {
#ifdef HAVE_AMX
    // 在函数内部分配一个局部(栈上)对齐缓冲区
    alignas(64) int8_t local_buffer[TILE_N * TILE_K];
    __m512i* db = reinterpret_cast<__m512i*>(local_buffer);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_and_si512(hi_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * i)));
    }
    asm volatile("" ::: "memory");
    _tile_loadd(2, db, TILE_K);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_and_si512(hi_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * (i + TILE_N))));
    }
    asm volatile("" ::: "memory");
    _tile_loadd(3, db, TILE_K);
#else
    (void)b;
    (void)ldb;
#endif
  }

  static void load_b_lo(dt* b, size_t ldb) {
#ifdef HAVE_AMX
    // 在函数内部分配一个局部(栈上)对齐缓冲区
    alignas(64) int8_t local_buffer[TILE_N * TILE_K];
    __m512i* db = reinterpret_cast<__m512i*>(local_buffer);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_slli_epi32(_mm512_and_si512(lo_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * i))), 4);
    }
    asm volatile("" ::: "memory");
    _tile_loadd(2, db, TILE_K);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_slli_epi32(
          _mm512_and_si512(lo_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * (i + TILE_N)))), 4);
    }
    asm volatile("" ::: "memory");
    _tile_loadd(3, db, TILE_K);
#else
    (void)b;
    (void)ldb;
#endif
  }

  static void load_a(dt* a, size_t lda) {
#ifdef HAVE_AMX
    _tile_loadd(0, a, lda);
    _tile_loadd(1, offset_pointer(a, lda * TILE_M), lda);
#else
    (void)a;
    (void)lda;
#endif
  }

  // static void load_b(dt* b, size_t ldb) {
  //   _tile_loadd(2, b, ldb);
  //   _tile_loadd(3, offset_pointer(b, ldb * TILE_N), ldb);
  // }

  static void clean_c() {
#ifdef HAVE_AMX
    _tile_zero(4);
    _tile_zero(5);
    _tile_zero(6);
    _tile_zero(7);
#endif
  }

  static void load_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_loadd(4, c, ldc);
    _tile_loadd(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_loadd(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_loadd(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void store_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_stored(4, c, ldc);
    _tile_stored(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_stored(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_stored(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void run_tile() {
#ifdef HAVE_AMX
    _tile_dpbsud(4, 0, 2);
    _tile_dpbsud(5, 0, 3);
    _tile_dpbsud(6, 1, 2);
    _tile_dpbsud(7, 1, 3);
#endif
  }

  using BufferA = BufferAWithSumKGroupImpl<GemmKernel224Int4_1KGroup>;

  using BufferB = BufferBInt4WithZeroKGroupImpl<GemmKernel224Int4_1KGroup>;

  using BufferC = BufferCReduceImpl<GemmKernel224Int4_1KGroup>;

  static void avx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, int32_t* int_c, BufferA* ba,
                         BufferB* bb, int k_group_size) {
    using K = GemmKernel224Int4_1KGroup;
    __m512i* c512 = (__m512i*)int_c;
    int m_block_end = std::min(m - m_begin, M_STEP);
    if (k_block_begin % k_group_size == 0) {
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        c512[m_i * 2] = _mm512_setzero_si512();
        c512[m_i * 2 + 1] = _mm512_setzero_si512();
      }
    }
    int k_offset = k_block_begin % K::BufferB::B_K_STEP;
    if (k_offset == 0) {
      int32_t* a32_lo = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin);
      __m512i* b512 = (__m512i*)bb->get_submat(n, k, n_begin, k_block_begin);
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        for (int k_i = 0; k_i < 16; k_i++) {
          __m512i ma_lo = _mm512_set1_epi32(a32_lo[m_i * 16 + k_i]);
          for (int n_i = 0; n_i < 2; n_i++) {
            __m512i b512_lo = _mm512_slli_epi32(_mm512_and_si512(K::lo_mask(), b512[n_i * 16 + k_i]), 4);
            c512[m_i * 2 + n_i] = _mm512_dpbusd_epi32_compat(c512[m_i * 2 + n_i], b512_lo, ma_lo);
          }
        }
      }
    } else {
      int32_t* a32_hi = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin);
      __m512i* b512 = (__m512i*)bb->get_submat(n, k, n_begin, k_block_begin - K::K_STEP);
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        for (int k_i = 0; k_i < 16; k_i++) {
          __m512i ma_hi = _mm512_set1_epi32(a32_hi[m_i * 16 + k_i]);
          for (int n_i = 0; n_i < 2; n_i++) {
            __m512i b512_hi = _mm512_and_si512(K::hi_mask(), b512[n_i * 16 + k_i]);
            c512[m_i * 2 + n_i] = _mm512_dpbusd_epi32_compat(c512[m_i * 2 + n_i], b512_hi, ma_hi);
          }
        }
      }
    }
  }
  static void amx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, int32_t* int_c, BufferA* ba,
                         BufferB* bb, int k_group_size) {
    using K = GemmKernel224Int4_1KGroup;
    if (k_block_begin % k_group_size == 0) {
      K::clean_c();
    } else {
      K::load_c(int_c, K::N_STEP * sizeof(int32_t));
    }

    // Determine if we're processing lo or hi nibble based on position within B_K_STEP
    int k_offset = k_block_begin % K::BufferB::B_K_STEP;
    if (k_offset == 0) {
      // Process lo nibble
      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin), K::K_STEP * sizeof(int8_t));
      K::load_b_lo(bb->get_submat(n, k, n_begin, k_block_begin), K::BufferB::B_K_STEP / 2);
      K::run_tile();
    } else {
      // Process hi nibble (k_offset == K_STEP)
      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin), K::K_STEP * sizeof(int8_t));
      K::load_b_hi(bb->get_submat(n, k, n_begin, k_block_begin - K::K_STEP), K::BufferB::B_K_STEP / 2);
      K::run_tile();
    }

    K::store_c(int_c, K::N_STEP * sizeof(int32_t));
  }

  static void apply_scale_kgroup(int m, int n, int m_begin, int n_begin, int k_begin, float* c, int32_t* int_c,
                                 BufferA* ba, BufferB* bb, int k, int k_group_size) {
    using K = GemmKernel224Int4_1KGroup;
    int to = m - m_begin;
    if (m - m_begin > K::M_STEP) {
      to = K::M_STEP;
    }
    for (int i = 0; i < to; i++) {
      __m512 as = _mm512_set1_ps(*ba->get_scale(m, m_begin + i, k, k_begin));
      __m512 asum = _mm512_set1_ps(*ba->get_sum(m, m_begin + i, k, k_begin));

      __m512 bs = _mm512_load_ps(bb->get_scale(n, n_begin, k, k_begin));
      __m512 b_mins = _mm512_load_ps(bb->get_min(n, n_begin, k, k_begin));
      __m512i now = _mm512_load_epi32((__m512i*)(int_c + i * K::N_STEP));
      __m512 result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      result = _mm512_add_ps(result, _mm512_mul_ps(asum, b_mins));
      __m512 existing = _mm512_load_ps((__m512*)(c + i * K::N_STEP));
      result = _mm512_add_ps(result, existing);
      _mm512_store_ps((__m512*)(c + i * K::N_STEP), result);

      bs = _mm512_load_ps(bb->get_scale(n, n_begin, k, k_begin) + K::TILE_N);
      b_mins = _mm512_load_ps(bb->get_min(n, n_begin, k, k_begin) + K::TILE_N);
      now = _mm512_load_si512((__m512i*)(int_c + i * K::N_STEP + K::TILE_N));
      result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      result = _mm512_add_ps(result, _mm512_mul_ps(asum, b_mins));
      existing = _mm512_load_ps((__m512*)(c + i * K::N_STEP + K::TILE_N));
      result = _mm512_add_ps(result, existing);
      _mm512_store_ps((__m512*)(c + i * K::N_STEP + K::TILE_N), result);
    }
  }
};

struct GemmKernel224Int4_1_LowKGroup {
  using dt = void;
  using output_t = int32_t;
  static constexpr double ELEMENT_SIZE = 0.5;
  static constexpr int TILE_M = 16;
  static constexpr int TILE_K = 64;
  static constexpr int TILE_N = 16;
  static constexpr int VNNI_BLK = 4;

  static constexpr int M_STEP = TILE_M * 2;
  static constexpr int N_STEP = TILE_N * 2;
  static constexpr int K_STEP = TILE_K;

  static inline const int N_BLOCK = 256;
  // static inline const int K_BLOCK = 7168;
  static inline const int K_BLOCK = 3584;
  // static inline const int K_BLOCK = 2560;
  static std::string name() { return "INT4_1K"; }

  static int recommended_nth(int n) { return (n + N_BLOCK - 1) / N_BLOCK; }

  static std::pair<int, int> split_range_n(int n, int ith, int nth) {
    int n_start = N_BLOCK * ith;
    int n_end = std::min(n, N_BLOCK * (ith + 1));
    return {n_start, n_end};
  }

  static void config() {
#ifdef HAVE_AMX
    enable_amx();
    TileConfig tile_config;

    // size is 16 x 64
    for (int i = 0; i < 2; i++) tile_config.set_row_col(i, TILE_M, TILE_K);

    // size is 16 x 64
    for (int i = 2; i < 4; i++) tile_config.set_row_col(i, TILE_K / VNNI_BLK, TILE_N * VNNI_BLK);

    // size is 16 x 16
    for (int i = 4; i < 8; i++) tile_config.set_row_col(i, TILE_M, TILE_N * sizeof(output_t));

    tile_config.set_config();
#endif
  }

  alignas(64) static constexpr uint8_t hi_mask_arr[64] = {
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0};

  alignas(64) static constexpr uint8_t lo_mask_arr[64] = {
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F};

  alignas(64) static constexpr uint8_t sign_mask_arr[64] = {
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
      0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
  };

  static __m512i hi_mask() { return *((__m512i*)(&hi_mask_arr[0])); }
  static __m128i hi_mask_128() { return *((__m128i*)(&hi_mask_arr[0])); }
  static __m512i lo_mask() { return *((__m512i*)(&lo_mask_arr[0])); }
  static __m128i lo_mask_128() { return *((__m128i*)(&lo_mask_arr[0])); }
  static __m128i si_mask_128() { return *((__m128i*)(&sign_mask_arr[0])); }

  static void load_b_hi(dt* b, size_t ldb) {
#ifdef HAVE_AMX
    // 在函数内部分配一个局部(栈上)对齐缓冲区
    alignas(64) int8_t local_buffer[TILE_N * TILE_K];
    __m512i* db = reinterpret_cast<__m512i*>(local_buffer);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_srli_epi32(_mm512_and_si512(hi_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * i))), 4);
    }
    asm volatile("" ::: "memory");
    _tile_loadd(2, db, TILE_K);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_srli_epi32(
          _mm512_and_si512(hi_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * (i + TILE_N)))), 4);
    }
    asm volatile("" ::: "memory");
    _tile_loadd(3, db, TILE_K);
#else
    (void)b;
    (void)ldb;
#endif
  }

  static void load_b_lo(dt* b, size_t ldb) {
#ifdef HAVE_AMX
    // 在函数内部分配一个局部(栈上)对齐缓冲区
    alignas(64) int8_t local_buffer[TILE_N * TILE_K];
    __m512i* db = reinterpret_cast<__m512i*>(local_buffer);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_and_si512(lo_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * i)));
    }
    asm volatile("" ::: "memory");
    _tile_loadd(2, db, TILE_K);

    for (size_t i = 0; i < TILE_N; i++) {
      db[i] = _mm512_and_si512(lo_mask(), *static_cast<__m512i*>(offset_pointer(b, ldb * (i + TILE_N))));
    }
    asm volatile("" ::: "memory");
    _tile_loadd(3, db, TILE_K);
#else
    (void)b;
    (void)ldb;
#endif
  }

  static void load_a(dt* a, size_t lda) {
#ifdef HAVE_AMX
    _tile_loadd(0, a, lda);
    _tile_loadd(1, offset_pointer(a, lda * TILE_M), lda);
#else
    (void)a;
    (void)lda;
#endif
  }

  // static void load_b(dt* b, size_t ldb) {
  //   _tile_loadd(2, b, ldb);
  //   _tile_loadd(3, offset_pointer(b, ldb * TILE_N), ldb);
  // }

  static void clean_c() {
#ifdef HAVE_AMX
    _tile_zero(4);
    _tile_zero(5);
    _tile_zero(6);
    _tile_zero(7);
#endif
  }

  static void load_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_loadd(4, c, ldc);
    _tile_loadd(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_loadd(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_loadd(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void store_c(output_t* c, size_t ldc) {
#ifdef HAVE_AMX
    _tile_stored(4, c, ldc);
    _tile_stored(5, offset_pointer(c, TILE_N * sizeof(output_t)), ldc);
    _tile_stored(6, offset_pointer(c, ldc * TILE_M), ldc);
    _tile_stored(7, offset_pointer(c, ldc * TILE_M + TILE_N * sizeof(output_t)), ldc);
#else
    (void)c;
    (void)ldc;
#endif
  }

  static void run_tile() {
#ifdef HAVE_AMX
    _tile_dpbsud(4, 0, 2);
    _tile_dpbsud(5, 0, 3);
    _tile_dpbsud(6, 1, 2);
    _tile_dpbsud(7, 1, 3);
#endif
  }

  using BufferA = BufferAWithSumKGroupImpl<GemmKernel224Int4_1_LowKGroup>;

  using BufferB = BufferBInt4WithZeroLowKGroupImpl<GemmKernel224Int4_1_LowKGroup>;

  using BufferC = BufferCReduceImpl<GemmKernel224Int4_1_LowKGroup>;

  static void avx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, int32_t* int_c, BufferA* ba,
                         BufferB* bb, int k_group_size) {
    using K = GemmKernel224Int4_1_LowKGroup;
    __m512i* c512 = (__m512i*)int_c;
    int m_block_end = std::min(m - m_begin, M_STEP);
    if (k_block_begin % k_group_size == 0) {
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        c512[m_i * 2] = _mm512_setzero_si512();
        c512[m_i * 2 + 1] = _mm512_setzero_si512();
      }
    }
    int k_offset = k_block_begin % K::BufferB::B_K_STEP;
    if (k_offset == 0) {
      int32_t* a32_lo = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin);
      __m512i* b512 = (__m512i*)bb->get_submat(n, k, n_begin, k_block_begin);
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        for (int k_i = 0; k_i < 16; k_i++) {
          __m512i ma_lo = _mm512_set1_epi32(a32_lo[m_i * 16 + k_i]);
          for (int n_i = 0; n_i < 2; n_i++) {
            __m512i b512_lo = _mm512_and_si512(K::lo_mask(), b512[n_i * 16 + k_i]);
            c512[m_i * 2 + n_i] = _mm512_dpbusd_epi32_compat(c512[m_i * 2 + n_i], b512_lo, ma_lo);
          }
        }
      }
    } else {
      int32_t* a32_hi = (int32_t*)ba->get_submat(m, k, m_begin, k_block_begin);
      __m512i* b512 = (__m512i*)bb->get_submat(n, k, n_begin, k_block_begin - K::K_STEP);
      for (int m_i = 0; m_i < m_block_end; m_i++) {
        for (int k_i = 0; k_i < 16; k_i++) {
          __m512i ma_hi = _mm512_set1_epi32(a32_hi[m_i * 16 + k_i]);
          for (int n_i = 0; n_i < 2; n_i++) {
            __m512i b512_hi = _mm512_srli_epi32(_mm512_and_si512(K::hi_mask(), b512[n_i * 16 + k_i]), 4);
            c512[m_i * 2 + n_i] = _mm512_dpbusd_epi32_compat(c512[m_i * 2 + n_i], b512_hi, ma_hi);
          }
        }
      }
    }
  }
  static void amx_kernel(int m, int n, int k, int m_begin, int n_begin, int k_block_begin, int32_t* int_c, BufferA* ba,
                         BufferB* bb, int k_group_size) {
    using K = GemmKernel224Int4_1_LowKGroup;
    if (k_block_begin % k_group_size == 0) {
      K::clean_c();
    } else {
      K::load_c(int_c, K::N_STEP * sizeof(int32_t));
    }

    // Determine if we're processing lo or hi nibble based on position within B_K_STEP
    int k_offset = k_block_begin % K::BufferB::B_K_STEP;
    if (k_offset == 0) {
      // Process lo nibble
      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin), K::K_STEP * sizeof(int8_t));
      K::load_b_lo(bb->get_submat(n, k, n_begin, k_block_begin), K::BufferB::B_K_STEP / 2);
      K::run_tile();
    } else {
      // Process hi nibble (k_offset == K_STEP)
      K::load_a(ba->get_submat(m, k, m_begin, k_block_begin), K::K_STEP * sizeof(int8_t));
      K::load_b_hi(bb->get_submat(n, k, n_begin, k_block_begin - K::K_STEP), K::BufferB::B_K_STEP / 2);
      K::run_tile();
    }

    K::store_c(int_c, K::N_STEP * sizeof(int32_t));
  }

  static void apply_scale_kgroup(int m, int n, int m_begin, int n_begin, int k_begin, float* c, int32_t* int_c,
                                 BufferA* ba, BufferB* bb, int k, int k_group_size) {
    using K = GemmKernel224Int4_1_LowKGroup;
    int to = m - m_begin;
    if (m - m_begin > K::M_STEP) {
      to = K::M_STEP;
    }
    for (int i = 0; i < to; i++) {
      __m512 as = _mm512_set1_ps(*ba->get_scale(m, m_begin + i, k, k_begin));
      __m512 asum = _mm512_set1_ps(*ba->get_sum(m, m_begin + i, k, k_begin));

      __m512 bs = _mm512_load_ps(bb->get_scale(n, n_begin, k, k_begin));
      __m512 b_mins = _mm512_load_ps(bb->get_min(n, n_begin, k, k_begin));
      __m512i now = _mm512_load_epi32((__m512i*)(int_c + i * K::N_STEP));
      __m512 result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      result = _mm512_add_ps(result, _mm512_mul_ps(asum, b_mins));
      __m512 existing = _mm512_load_ps((__m512*)(c + i * K::N_STEP));
      result = _mm512_add_ps(result, existing);
      _mm512_store_ps((__m512*)(c + i * K::N_STEP), result);

      bs = _mm512_load_ps(bb->get_scale(n, n_begin, k, k_begin) + K::TILE_N);
      b_mins = _mm512_load_ps(bb->get_min(n, n_begin, k, k_begin) + K::TILE_N);
      now = _mm512_load_si512((__m512i*)(int_c + i * K::N_STEP + K::TILE_N));
      result = _mm512_mul_ps(_mm512_mul_ps(as, bs), _mm512_cvtepi32_ps(now));
      result = _mm512_add_ps(result, _mm512_mul_ps(asum, b_mins));
      existing = _mm512_load_ps((__m512*)(c + i * K::N_STEP + K::TILE_N));
      result = _mm512_add_ps(result, existing);
      _mm512_store_ps((__m512*)(c + i * K::N_STEP + K::TILE_N), result);
    }
  }
};

// K2 Signed Int4 K-group quantization kernel (AVX only, no AMX)
// For K2 MoE - signed int4 range: [-8, 7]
struct GemmKernel224Int4SmallKGroup {
  using dt = uint8_t;  // packed int4 type
  using output_t = int32_t;
  static constexpr double ELEMENT_SIZE = 0.5;
  static constexpr int VNNI_BLK = 4;

  static constexpr int M_STEP = 1;
  static constexpr int N_STEP = 32;
  static constexpr int K_STEP = 32;

  static inline const int N_BLOCK = 256;
  // K_BLOCK should match k_group_size for proper scaling
  static inline const int K_BLOCK = 7168;  // Will be overridden by k_group_size

  static std::string name() { return "K2_INT4_KGROUP"; }
  static int recommended_nth(int n) { return (n + N_BLOCK - 1) / N_BLOCK; }
  static std::pair<int, int> split_range_n(int n, int ith, int nth) {
    int n_start = N_BLOCK * ith;
    int n_end = std::min(n, N_BLOCK * (ith + 1));
    return {n_start, n_end};
  }
  static void config() {}

  alignas(64) static constexpr uint8_t hi_mask_arr[32] = {
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0,
      0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0, 0xF0};

  alignas(64) static constexpr uint8_t lo_mask_arr[32] = {
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F,
      0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F};

  alignas(64) static constexpr uint8_t sign_xor_arr[32] = {
      0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88,
      0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88};
  static __m256i hi_mask() { return *((__m256i*)(&hi_mask_arr[0])); }
  static __m256i lo_mask() { return *((__m256i*)(&lo_mask_arr[0])); }
  static __m256i sign_xor_mask() { return *((__m256i*)(&sign_xor_arr[0])); }

  using BufferA = BufferASmallKGroupImpl<GemmKernel224Int4SmallKGroup>;
  using BufferB = BufferBInt4KGroupImpl<GemmKernel224Int4SmallKGroup>;  // Use new signed int4 buffer
  using BufferC = BufferCReduceImpl<GemmKernel224Int4SmallKGroup>;

  // K-group aware AVX kernel for signed int4
  static inline __m512i compressed_int4_to_int8_avx512(__m256i b256) {
    b256 = _mm256_xor_si256(b256, sign_xor_mask());
    __m256i b_hi = _mm256_and_si256(b256, hi_mask());
    __m256i b_lo = _mm256_slli_epi16(_mm256_andnot_si256(hi_mask(), b256), 4);

    __m256i unpack_lo = _mm256_unpacklo_epi8(b_lo, b_hi);
    __m256i unpack_hi = _mm256_unpackhi_epi8(b_lo, b_hi);
    __m512i result = _mm512_inserti64x4(_mm512_castsi256_si512(unpack_lo), unpack_hi, 1);
    const __m512i lane_shuffle = _mm512_set_epi64(7, 6, 3, 2, 5, 4, 1, 0);
    return _mm512_permutexvar_epi64(lane_shuffle, result);
  }
  static inline void integer_mat_vec_kgroup(int m, int n, int k, int k_group_size, BufferA* ba, BufferB* bb,
                                            BufferC* bc, int ith, int nth) {
    auto [n_start, n_end] = split_range_n(n, ith, nth);
    for (int m_begin = 0; m_begin < m; m_begin++) {
      float* c = bc->get_submat(m, n, m_begin, n_start);
      __m512i* a512 = (__m512i*)ba->get_submat(m, k, m_begin, 0);

      for (int n_block_begin = n_start; n_block_begin < n_end; n_block_begin++) {
        __m256i* b256 = (__m256i*)bb->get_submat(n, k, n_block_begin, 0);
        float* as = (float*)ba->get_scale(m, m_begin, k, 0);
        float* bs = (float*)bb->get_scale(n, n_block_begin, k, 0);

        __m512 sum = _mm512_setzero_ps();
#define WORK_K_BLOCK(k_block)                                                                     \
  {                                                                                               \
    __m256 abscale0 = _mm256_set1_ps(as[(k_block) * 2] * bs[(k_block) * 2]);                      \
    __m256 abscale1 = _mm256_set1_ps(as[(k_block) * 2 + 1] * bs[(k_block) * 2 + 1]);              \
    __m512 abscale = _mm512_insertf32x8(_mm512_castps256_ps512(abscale0), abscale1, 1);           \
    __m512i mul = _mm512_setzero_si512();                                                         \
    mul = _mm512_dpbssd_epi32(mul, a512[k_block], compressed_int4_to_int8_avx512(b256[k_block])); \
    sum = _mm512_add_ps(sum, _mm512_mul_ps(abscale, _mm512_cvtepi32_ps(mul)));                    \
  }

        for (int k_block = 0; k_block < k / 64; k_block += 2) {
          WORK_K_BLOCK(k_block);
          WORK_K_BLOCK(k_block + 1);
        }

        c[n_block_begin - n_start] = _mm512_reduce_add_ps(sum) / 16;
      }
    }
  }
};

inline void vec_mul_kgroup(int m, int n, int k, int k_group_size,
                           std::shared_ptr<GemmKernel224Int4SmallKGroup::BufferA> ba,
                           std::shared_ptr<GemmKernel224Int4SmallKGroup::BufferB> bb,
                           std::shared_ptr<GemmKernel224Int4SmallKGroup::BufferC> bc, int ith, int nth) {
  GemmKernel224Int4SmallKGroup::integer_mat_vec_kgroup(m, n, k, k_group_size, ba.get(), bb.get(), bc.get(), ith, nth);
}

inline void mat_mul_kgroup(int m, int n, int k, int k_group_size,
                           std::shared_ptr<GemmKernel224Int4SmallKGroup::BufferA> ba,
                           std::shared_ptr<GemmKernel224Int4SmallKGroup::BufferB> bb,
                           std::shared_ptr<GemmKernel224Int4SmallKGroup::BufferC> bc, int ith, int nth) {
  GemmKernel224Int4SmallKGroup::integer_mat_vec_kgroup(m, n, k, k_group_size, ba.get(), bb.get(), bc.get(), ith, nth);
}

// New k-group aware matrix multiplication function
template <typename K, bool amx_or_avx = true>
void integer_mat_mul_kgroup(int m, int n, int k, int k_group_size, typename K::BufferA* ba, typename K::BufferB* bb,
                            typename K::BufferC* bc, int ith, int nth) {
  assert(n % K::N_STEP == 0);
  assert(k % K::K_STEP == 0);
  assert(k % k_group_size == 0);

  auto [n_start, n_end] = K::split_range_n(n, ith, nth);
  // Process by k_groups
  for (int k_group_begin = 0; k_group_begin < k; k_group_begin += k_group_size) {
    for (int m_begin = 0; m_begin < m; m_begin += K::M_STEP) {
      for (int n_begin = n_start; n_begin < n_end; n_begin += K::N_STEP) {
        float* c = bc->get_submat(m, n, m_begin, n_begin);
        int32_t* int_c = bc->get_int_submat(m, n, m_begin, n_begin);

        // Initialize float c to zero at the very beginning
        if (k_group_begin == 0) {
          for (int i = 0; i < K::M_STEP && m_begin + i < m; i++) {
            for (int j = 0; j < K::N_STEP; j++) {
              c[i * K::N_STEP + j] = 0.0f;
            }
          }
        }
        for (int k_begin = k_group_begin; k_begin < std::min(k, k_group_begin + k_group_size); k_begin += K::K_STEP) {
          if constexpr (amx_or_avx && AMX_AVAILABLE) {
            K::amx_kernel(m, n, k, m_begin, n_begin, k_begin, int_c, ba, bb, k_group_size);
          } else {
            K::avx_kernel(m, n, k, m_begin, n_begin, k_begin, int_c, ba, bb, k_group_size);
          }
        }
        // }

        // Apply scale and accumulate to float buffer at end of k_group
        K::apply_scale_kgroup(m, n, m_begin, n_begin, k_group_begin, c, int_c, ba, bb, k, k_group_size);
      }
    }
  }
}

// Convenience functions for k-group kernels
inline void vec_mul_kgroup(int m, int n, int k, int k_group_size, std::shared_ptr<GemmKernel224Int4KGroup::BufferA> ba,
                           std::shared_ptr<GemmKernel224Int4KGroup::BufferB> bb,
                           std::shared_ptr<GemmKernel224Int4KGroup::BufferC> bc, int ith, int nth) {
  integer_mat_mul_kgroup<GemmKernel224Int4KGroup, false>(m, n, k, k_group_size, ba.get(), bb.get(), bc.get(), ith, nth);
}

inline void mat_mul_kgroup(int m, int n, int k, int k_group_size, std::shared_ptr<GemmKernel224Int4KGroup::BufferA> ba,
                           std::shared_ptr<GemmKernel224Int4KGroup::BufferB> bb,
                           std::shared_ptr<GemmKernel224Int4KGroup::BufferC> bc, int ith, int nth) {
  integer_mat_mul_kgroup<GemmKernel224Int4KGroup, true>(m, n, k, k_group_size, ba.get(), bb.get(), bc.get(), ith, nth);
}

// Convenience functions for k-group kernels
inline void vec_mul_kgroup(int m, int n, int k, int k_group_size,
                           std::shared_ptr<GemmKernel224Int4_1KGroup::BufferA> ba,
                           std::shared_ptr<GemmKernel224Int4_1KGroup::BufferB> bb,
                           std::shared_ptr<GemmKernel224Int4_1KGroup::BufferC> bc, int ith, int nth) {
  integer_mat_mul_kgroup<GemmKernel224Int4_1KGroup, false>(m, n, k, k_group_size, ba.get(), bb.get(), bc.get(), ith,
                                                           nth);
}

inline void mat_mul_kgroup(int m, int n, int k, int k_group_size,
                           std::shared_ptr<GemmKernel224Int4_1KGroup::BufferA> ba,
                           std::shared_ptr<GemmKernel224Int4_1KGroup::BufferB> bb,
                           std::shared_ptr<GemmKernel224Int4_1KGroup::BufferC> bc, int ith, int nth) {
  integer_mat_mul_kgroup<GemmKernel224Int4_1KGroup, true>(m, n, k, k_group_size, ba.get(), bb.get(), bc.get(), ith,
                                                          nth);
}

// Convenience functions for k-group kernels
inline void vec_mul_kgroup(int m, int n, int k, int k_group_size,
                           std::shared_ptr<GemmKernel224Int4_1_LowKGroup::BufferA> ba,
                           std::shared_ptr<GemmKernel224Int4_1_LowKGroup::BufferB> bb,
                           std::shared_ptr<GemmKernel224Int4_1_LowKGroup::BufferC> bc, int ith, int nth) {
  integer_mat_mul_kgroup<GemmKernel224Int4_1_LowKGroup, false>(m, n, k, k_group_size, ba.get(), bb.get(), bc.get(), ith,
                                                               nth);
}

inline void mat_mul_kgroup(int m, int n, int k, int k_group_size,
                           std::shared_ptr<GemmKernel224Int4_1_LowKGroup::BufferA> ba,
                           std::shared_ptr<GemmKernel224Int4_1_LowKGroup::BufferB> bb,
                           std::shared_ptr<GemmKernel224Int4_1_LowKGroup::BufferC> bc, int ith, int nth) {
  integer_mat_mul_kgroup<GemmKernel224Int4_1_LowKGroup, true>(m, n, k, k_group_size, ba.get(), bb.get(), bc.get(), ith,
                                                              nth);
}

}  // namespace amx

#endif  // AMX_KERNELS_HPP
