// Copyright 2025 RnD Center "ELVEES", JSC

#include <elcore50-signal-lib/fft.h>

float maximum_norm(const float *dsp_res, const float *ref_res, const int size) {
  float diff = fabs(ref_res[0] - dsp_res[0]);
  for (size_t i = 1; i < size * 2; ++i) {
    float diff_i = fabs(ref_res[i] - dsp_res[i]);
    if (diff_i > diff) diff = diff_i;
  }

  return diff;
}

int main() {
  // Размер входного сигнала
  int32_t fft_size = 262144;

  // Выходной вектор для типа float16
  // Выравнивание нужно только при вычислениях во внешней памяти (DDR)
  int16_t *fdst = memalign(fft_size * 2 * sizeof(int16_t), fft_size * 2 * sizeof(int16_t));
  // Входной вектор для типа float16
  int16_t *fsrc = memalign(fft_size * 2 * sizeof(int16_t), fft_size * 2 * sizeof(int16_t));
  // Вектор точного решения
  float *fetalon = memalign(fft_size * 2 * sizeof(float), fft_size * 2 * sizeof(float));

  // Создание объекта преобразования
  fft_t fft_obj;
  fft_obj.src = fsrc;
  fft_obj.dst = fdst;
  fft_obj.int_mem = SL_XYRAM;
  fft_obj.dir = SL_IFFT;
  fft_obj.dim = SL_1D;
  int retval = fft_plan_1d(&fft_obj, fft_size, SL_HFLOAT);
  if (retval) {
    print_error_message(retval);
    // Освобождение ресурсов
    fft_free_sources(&fft_obj);
    free(fdst);
    free(fsrc);
    free(fetalon);
    return 1;
  }

  // Частота входного сигнала
  const int32_t frequency = 5;
  // Входной сигнал и точное решение
  memset(fetalon, 0.0, fft_size * 2 * sizeof(float));
  fetalon[2 * frequency + 1] = 1.0;
  // Преобразование входного сигнала типа float32 в тип float16
  float32_to_float16(fetalon, fsrc, fft_size * 2);

  // Вычисление обратного БПФ
  retval = fft_execute(&fft_obj);
  if (retval) {
    print_error_message(retval);
    // Освобождение ресурсов
    fft_free_sources(&fft_obj);
    free(fdst);
    free(fsrc);
    free(fetalon);
    return 1;
  }

  float *temp = malloc(fft_size * 2 * sizeof(float));
  if (!fft_obj.big_size || !fft_obj.int_mem) {
    // Нормировка результата обратного БПФ
    float16_to_float32(fdst, temp, fft_size * 2);
    for (size_t i = 0; i < fft_size * 2; ++i) {
      temp[i] /= fft_size;
    }
    float32_to_float16(temp, fdst, fft_size * 2);
    // Подготовка для вычисления прямого БПФ
    fft_obj.src = fdst;
    fft_obj.dst = fsrc;
  } else {
    // Нормировка результата обратного БПФ
    float16_to_float32(fsrc, temp, fft_size * 2);
    for (size_t i = 0; i < fft_size * 2; ++i) {
      temp[i] /= fft_size;
    }
    float32_to_float16(temp, fsrc, fft_size * 2);
    // Подготовка для вычисления прямого БПФ
    fft_obj.src = fsrc;
    fft_obj.dst = fdst;
  }

  // Вычисление прямого БПФ
  fft_obj.dir = SL_FFT;
  retval = fft_execute(&fft_obj);
  if (retval) {
    print_error_message(retval);
    // Освобождение ресурсов
    fft_free_sources(&fft_obj);
    free(fdst);
    free(fsrc);
    free(fetalon);
    free(temp);
    return 1;
  }

  // Проверка результата
  float16_to_float32(fsrc, temp, fft_size * 2);
  const float norm = maximum_norm(temp, fetalon, fft_size);
  printf("float16 1d sample result:\n");
  printf("fft_size = %d, ", fft_size);
  if (norm < 1e-2)
    printf("passed!\n");
  else
    printf("failed!\n");

  // Освобождение ресурсов
  fft_free_sources(&fft_obj);
  free(temp);
  free(fdst);
  free(fsrc);
  free(fetalon);
  return 0;
}
