// Copyright 2025 RnD Center "ELVEES", JSC

#ifndef _HELPER_FOR_CPP_TESTS_H
#define _HELPER_FOR_CPP_TESTS_H

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#include <iomanip>
#include <iostream>

#define count_tics(tic_addr, instr_addr)        \
  asm volatile(                                 \
      "tcr     tic_cntr, r18  trl    %0, r6\n \
        tcr     instr_cntr, r19 trl    %1, r7\n \
        stl     r18, (r6)    stl     r19, (r7)" \
      :                                         \
      : "r"(tic_addr), "r"(instr_addr))

/// Заполнение массива случайными числами
template <class T>
void create_array(T* vect,   ///< [in/out] указатель на массив
                  int size,  ///< [in]     количество элементов массива
                  int sign   ///< [in]     знак случайных элементов -1-отр., 1-пол., 0-случ.
) {
  for (int i = 0; i < size; ++i) {
    vect[i] = (sign) ? sign * (rand() % 100) : (rand() % 100 - 50);
  }
}

/// Сравнение двух массивов
/// @return к-во ошибок, после 10 ошибок функция досрочно завершается
template <class T>
uint8_t compare_arrays(T* vect0,  ///< [in]  указатель на первый массив
                       T* vect1,  ///< [in]  указатель на второй массив
                       int size   ///< [in]  количество элементов массива
) {
  uint8_t ret = 0;
  int err_counter = 0;
  for (int i = 0; i < size; ++i) {
    if (vect0[i] != vect1[i]) {
      ret = 1;
      ++err_counter;
      if (err_counter == 10) break;
    }
  }

  return ret;
}

/// Сравнение двух массивов с относительной ошибкой
/// @return к-во ошибок, после 10 ошибок функция досрочно завершается
template <class T>
uint8_t compare_arrays_with_eps(T* vect0,  ///< [in]  указатель на первый массив
                                T* vect1,  ///< [in]  указатель на второй массив
                                int size,  ///< [in]  количество элементов массива
                                float eps  ///< [in]  величина максимально допустимой погрешности
) {
  uint8_t ret = 0;
  int err_counter = 0;
  for (int i = 0; i < size; ++i) {
    if (((float)abs(vect0[i] - vect1[i]) / (vect0[i] ? vect0[i] : 1)) > eps) {
      ret = 1;
      ++err_counter;
      if (err_counter == 10) break;
    }
  }

  return ret;
}

/// Печать заголовка таблицы
void print_table_header();

/// Печать информации о тесте
void print_performance(
    uint32_t* ref_tic_count,  ///< [in]  указатель на массив, где 0й элемент - к-во тактов на момент начала работы
                              ///        референсной функции, 1й - конца
    uint32_t* ref_instruction_count,  ///< [in]  указатель на массив, где 0й элемент - к-во инструкций на момент начала
                                      ///        работы референсной функции, 1й - конца
    uint32_t* tic_count,          ///< [in]  указатель на массив, где 0й элемент - к-во тактов на момент начала работы
                                  ///        оптимизированной функции, 1й - конца
    uint32_t* instruction_count,  ///< [in]  указатель на массив, где 0й элемент - к-во инструкций на момент начала
                                  ///        работы оптимизированной функции, 1й - конца
    int32_t input_bytes,          ///< [in]  количество обрабатываемых входных байтов
    int32_t ti_tics               ///< [in]  информация о количестве тактов процессора С66x от Texas Instrument
);

#endif
