// File: kernel.cpp
#include "loop_114.h"
#include <stdint.h>
#include <arm_sve.h>
#define restrict __restrict
#define SC_SVE_ATTR
#define FOR_COND(P, S, I, N) svptest_first(svptrue_b##S(), P = svwhilelt_b##S(I, N))
#define FOR_LOOP(T, I, M, N, P, S, W) for (T I = M; FOR_COND(P, S, I, N); I += svcnt##W())
#define FOR_LOOP_8(T, I, M, N, P)  FOR_LOOP(T, I, M, N, P, 8, b)
#define FOR_LOOP_16(T, I, M, N, P) FOR_LOOP(T, I, M, N, P, 16, h)
#define FOR_LOOP_32(T, I, M, N, P) FOR_LOOP(T, I, M, N, P, 32, w)
#define FOR_LOOP_64(T, I, M, N, P) FOR_LOOP(T, I, M, N, P, 64, d)
static inline uint32_t get_sve_vl(void) {
  uint64_t vl = 0; asm volatile("incb %[vl], all, mul #8" : [vl] "+r"(vl) : :);
  return (uint32_t)vl;
}
static inline uint32_t get_vl(void) { return get_sve_vl(); }

extern "C" void inner_loop_114(struct loop_114_data * input)

{
  int16_t * data = input->data;
  int16_t * res = input->res;
  int32_t n = input->n;
  int32_t lags = input->lags;
  int16_t scale = input->scale;

  svbool_t all = svptrue_b8();
  svuint32_t scales = svdup_u32(scale);

  svbool_t pgl;
  FOR_LOOP_16(int32_t, lag, 0, lags, pgl) {
    svint32_t acc_lo = svdup_s32(0);
    svint32_t acc_hi = svdup_s32(0);

    int16_t *lmt = data + n;
    for (int16_t *p0 = data, *p1 = data + lag; p0 < lmt; p0++, p1++) {
      svint16_t vec_cur = svdup_s16_x(all, p0[0]);
      svint16_t vec_lag = svld1_s16(pgl, p1);
      svint32_t lo = svasr_x(all, svmullb(vec_cur, vec_lag), scales);
      svint32_t hi = svasr_x(all, svmullt(vec_cur, vec_lag), scales);
      acc_lo = svadd_x(all, acc_lo, lo);
      acc_hi = svadd_x(all, acc_hi, hi);
    }

    svint16_t half_lo = svreinterpret_s16(acc_lo);
    svint16_t half_hi = svreinterpret_s16(acc_hi);
    svint16_t zipped = svtrn2_s16(half_lo, half_hi);

    svst1(pgl, res + lag, zipped);
  }
}