#ifndef F_UTILS_H
#define F_UTILS_H

#include "futile_cst.h"
#include <stdbool.h>
#include "yaml_strings.h"

F_DEFINE_TYPE(f_dump_buffer);
F_DEFINE_TYPE(f_none_object);
F_DEFINE_TYPE(f_progress_bar);

f90_f_progress_bar_pointer f90_f_progress_bar_copy_constructor(const f90_f_progress_bar* other);

f90_f_progress_bar_pointer f90_f_progress_bar_type_new(const char message[90],
  int ncall,
  int nstep,
  size_t t0);

void f90_f_progress_bar_free(f90_f_progress_bar_pointer self);

f90_f_progress_bar_pointer f90_f_progress_bar_empty(void);

f90_f_none_object_pointer f90_f_none_object_copy_constructor(const f90_f_none_object* other);

f90_f_none_object_pointer f90_f_none_object_type_new(const char* none);

void f90_f_none_object_free(f90_f_none_object_pointer self);

f90_f_dump_buffer_pointer f90_f_dump_buffer_copy_constructor(const f90_f_dump_buffer* other);

f90_f_dump_buffer_pointer f90_f_dump_buffer_type_new(const char* buf,
  int ipos);

void f90_f_dump_buffer_free(f90_f_dump_buffer_pointer self);

f90_f_dump_buffer_pointer f90_f_dump_buffer_empty(void);

f90_f_none_object_pointer f_none(void);

size_t f_time(void);

f90_f_progress_bar_pointer f_progress_bar_new(const int (*nstep));

bool f_tty(int unit);

int f_get_free_unit(const int (*unit));

int f_getpid(void);

void f_utils_errors(void);

void update_progress_bar(f90_f_progress_bar* bar,
  int istep);

void f_pause(int sec);

void f_utils_recl(int unt,
  int recl_max,
  int* recl);

void f_file_exists(const char* file,
  bool* exists);

void f_close(int unit);

void f_file_unit(const char* file,
  int* unit);

void f_mkdir(const char* dir,
  char* path,
  size_t path_len);

void f_delete_file(const char* file);

void f_move_file(const char* src,
  const char* dest);

void f_system(const char* command);

void f_rewind(int unit);

void f_open_file(int* unit,
  const char* file,
  const char (*status),
  const char (*position),
  const char (*action),
  const bool (*binary));

void f_diff_i(size_t n,
  int* a_add,
  int* b_add,
  int* diff);

void f_diff_i2i1(size_t n,
  const int* a,
  size_t a_dim_0,
  size_t a_dim_1,
  const int* b,
  size_t b_dim_0,
  int* diff);

void f_diff_i3i1(size_t n,
  const int* a,
  size_t a_dim_0,
  size_t a_dim_1,
  size_t a_dim_2,
  const int* b,
  size_t b_dim_0,
  int* diff);

void f_diff_i2(size_t n,
  const int* a,
  size_t a_dim_0,
  size_t a_dim_1,
  const int* b,
  size_t b_dim_0,
  size_t b_dim_1,
  int* diff);

void f_diff_i1(size_t n,
  const int* a,
  size_t a_dim_0,
  const int* b,
  size_t b_dim_0,
  int* diff);

void f_diff_i1i2(size_t n,
  const int* a,
  size_t a_dim_0,
  const int* b,
  size_t b_dim_0,
  size_t b_dim_1,
  int* diff);

void f_diff_li(size_t n,
  long* a_add,
  long* b_add,
  long* diff);

void f_diff_li2li1(size_t n,
  const long* a,
  size_t a_dim_0,
  size_t a_dim_1,
  const long* b,
  size_t b_dim_0,
  long* diff);

void f_diff_li2(size_t n,
  const long* a,
  size_t a_dim_0,
  size_t a_dim_1,
  const long* b,
  size_t b_dim_0,
  size_t b_dim_1,
  long* diff);

void f_diff_li1(size_t n,
  const long* a,
  size_t a_dim_0,
  const long* b,
  size_t b_dim_0,
  long* diff);

void f_diff_li1li2(size_t n,
  const long* a,
  size_t a_dim_0,
  const long* b,
  size_t b_dim_0,
  size_t b_dim_1,
  long* diff);

void f_diff_r(size_t n,
  float* a_add,
  float* b_add,
  float* diff);

void f_diff_d(size_t n,
  double* a_add,
  double* b_add,
  double* diff);

void f_diff_d1(size_t n,
  const double* a,
  size_t a_dim_0,
  const double* b,
  size_t b_dim_0,
  double* diff,
  size_t (*ind));

void f_diff_d2d3(size_t n,
  const double* a,
  size_t a_dim_0,
  size_t a_dim_1,
  const double* b,
  size_t b_dim_0,
  size_t b_dim_1,
  size_t b_dim_2,
  double* diff);

void f_diff_d2d1(size_t n,
  const double* a,
  size_t a_dim_0,
  size_t a_dim_1,
  const double* b,
  size_t b_dim_0,
  double* diff);

void f_diff_d3d1(size_t n,
  const double* a,
  size_t a_dim_0,
  size_t a_dim_1,
  size_t a_dim_2,
  const double* b,
  size_t b_dim_0,
  double* diff);

void f_diff_d0d1(size_t n,
  double* a,
  const double* b,
  size_t b_dim_0,
  double* diff);

void f_diff_d2(size_t n,
  const double* a,
  size_t a_dim_0,
  size_t a_dim_1,
  const double* b,
  size_t b_dim_0,
  size_t b_dim_1,
  double* diff);

void f_diff_d3(size_t n,
  const double* a,
  size_t a_dim_0,
  size_t a_dim_1,
  size_t a_dim_2,
  const double* b,
  size_t b_dim_0,
  size_t b_dim_1,
  size_t b_dim_2,
  double* diff);

void f_diff_d1d2(size_t n,
  const double* a,
  size_t a_dim_0,
  const double* b,
  size_t b_dim_0,
  size_t b_dim_1,
  double* diff);

/* void f_diff_l(size_t n); */

void f_diff_c1i1(size_t n,
  const char* a,
  const int* b,
  size_t b_dim_0,
  int* diff);

void f_diff_c1li1(size_t n,
  const char* a,
  const size_t* b,
  size_t b_dim_0,
  size_t* diff);

void f_diff_c0i1(size_t n,
  const char* a,
  const int* b,
  size_t b_dim_0,
  int* diff);

void f_diff_c0li1(size_t n,
  const char* a,
  const size_t* b,
  size_t b_dim_0,
  size_t* diff);

void f_diff_li0li1(size_t n,
  size_t* a,
  const size_t* b,
  size_t b_dim_0,
  size_t* diff);

void f_diff_i0i1(size_t n,
  int* a,
  const int* b,
  size_t b_dim_0,
  int* diff);

size_t f_sizeof_i1(const int* datatype,
  size_t datatype_dim_0);

size_t f_sizeof_i2(const int* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1);

size_t f_sizeof_i3(const int* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2);

size_t f_sizeof_i4(const int* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3);

size_t f_sizeof_i5(const int* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4);

size_t f_sizeof_li1(const size_t* datatype,
  size_t datatype_dim_0);

size_t f_sizeof_li2(const size_t* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1);

size_t f_sizeof_li3(const size_t* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2);

size_t f_sizeof_li4(const size_t* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3);

size_t f_sizeof_li5(const size_t* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4);

/* void f_sizeof_l1(void); */

/* void f_sizeof_l2(void); */

/* void f_sizeof_l3(void); */

/* void f_sizeof_l4(void); */

/* void f_sizeof_l5(void); */

/* void f_sizeof_b1(void); */

/* void f_sizeof_b2(void); */

size_t f_sizeof_d1(const double* datatype,
  size_t datatype_dim_0);

size_t f_sizeof_d2(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1);

size_t f_sizeof_d3(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2);

size_t f_sizeof_d4(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3);

size_t f_sizeof_d5(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4);

size_t f_sizeof_d6(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4,
  size_t datatype_dim_5);

size_t f_sizeof_d7(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4,
  size_t datatype_dim_5,
  size_t datatype_dim_6);

size_t f_sizeof_r1(const float* datatype,
  size_t datatype_dim_0);

size_t f_sizeof_r2(const float* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1);

size_t f_sizeof_r3(const float* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2);

size_t f_sizeof_r4(const float* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3);

size_t f_sizeof_r5(const float* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4);

size_t f_sizeof_c0(const char* datatype);

/* void f_sizeof_c1(int ln); */

/* void f_sizeof_c2(int ln); */

/* void f_sizeof_z1(void); */

/* void f_sizeof_z2(void); */

/* void f_sizeof_z3(void); */

/* void f_sizeof_z4(void); */

/* void f_sizeof_z5(void); */

size_t f_size_i0(int datatype);

size_t f_size_i1(const int* datatype,
  size_t datatype_dim_0);

size_t f_size_i2(const int* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1);

size_t f_size_i3(const int* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2);

size_t f_size_i4(const int* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3);

size_t f_size_i5(const int* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4);

size_t f_size_li1(const size_t* datatype,
  size_t datatype_dim_0);

size_t f_size_li2(const size_t* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1);

size_t f_size_li3(const size_t* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2);

size_t f_size_li4(const size_t* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3);

size_t f_size_li5(const size_t* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4);

/* void f_size_l1(void); */

/* void f_size_l2(void); */

/* void f_size_l3(void); */

/* void f_size_l4(void); */

/* void f_size_l5(void); */

/* void f_size_b1(void); */

/* void f_size_b2(void); */

size_t f_size_d0(double datatype);

size_t f_size_d1(const double* datatype,
  size_t datatype_dim_0);

size_t f_size_d2(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1);

size_t f_size_d3(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2);

size_t f_size_d4(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3);

size_t f_size_d5(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4);

size_t f_size_d6(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4,
  size_t datatype_dim_5);

size_t f_size_d7(const double* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4,
  size_t datatype_dim_5,
  size_t datatype_dim_6);

size_t f_size_r1(const float* datatype,
  size_t datatype_dim_0);

size_t f_size_r2(const float* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1);

size_t f_size_r3(const float* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2);

size_t f_size_r4(const float* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3);

size_t f_size_r5(const float* datatype,
  size_t datatype_dim_0,
  size_t datatype_dim_1,
  size_t datatype_dim_2,
  size_t datatype_dim_3,
  size_t datatype_dim_4);

size_t f_size_c0(const char* datatype);

/* void f_size_c1(int ln); */

/* void f_size_z1(void); */

/* void f_size_z2(void); */

/* void f_size_z3(void); */

/* void f_size_z4(void); */

/* void f_size_z5(void); */

void zero_string(char* str,
  size_t str_len);

void zero_li(size_t* val);

void zero_i(int* val);

void zero_r(float* val);

void zero_d(double* val);

void zero_l(bool* val);

/* void zero_ll(void); */

void put_to_zero_r1(float* da,
  size_t da_dim_0);

void put_to_zero_double(int n,
  double* da);

void put_to_zero_double_1(double* da,
  size_t da_dim_0);

void put_to_zero_double_2(double* da,
  size_t da_dim_0,
  size_t da_dim_1);

void put_to_zero_double_3(double* da,
  size_t da_dim_0,
  size_t da_dim_1,
  size_t da_dim_2);

void put_to_zero_double_4(double* da,
  size_t da_dim_0,
  size_t da_dim_1,
  size_t da_dim_2,
  size_t da_dim_3);

void put_to_zero_double_5(double* da,
  size_t da_dim_0,
  size_t da_dim_1,
  size_t da_dim_2,
  size_t da_dim_3,
  size_t da_dim_4);

void put_to_zero_double_6(double* da,
  size_t da_dim_0,
  size_t da_dim_1,
  size_t da_dim_2,
  size_t da_dim_3,
  size_t da_dim_4,
  size_t da_dim_5);

void put_to_zero_double_7(double* da,
  size_t da_dim_0,
  size_t da_dim_1,
  size_t da_dim_2,
  size_t da_dim_3,
  size_t da_dim_4,
  size_t da_dim_5,
  size_t da_dim_6);

void put_to_zero_integer(int n,
  int* da);

void put_to_zero_integer1(int* da,
  size_t da_dim_0);

void put_to_zero_integer2(int* da,
  size_t da_dim_0,
  size_t da_dim_1);

void put_to_zero_integer3(int* da,
  size_t da_dim_0,
  size_t da_dim_1,
  size_t da_dim_2);

void put_to_zero_long(int n,
  size_t* da);

void put_to_zero_long1(size_t* da,
  size_t da_dim_0);

void put_to_zero_long2(size_t* da,
  size_t da_dim_0,
  size_t da_dim_1);

void put_to_zero_long3(size_t* da,
  size_t da_dim_0,
  size_t da_dim_1,
  size_t da_dim_2);

void f_inc_i0(int* i,
  const int (*inc));

void f_ht_long(char out_time[95],
  size_t ns,
  const bool (*short_bn));

void f_humantime(char out_time[95],
  double ns,
  const bool (*short_bn));

void f_assert(bool condition,
  const char* id,
  const int (*err_id),
  const char (*err_name));

void f_assert_str(bool condition,
  const f90_f_string* id,
  const int (*err_id),
  const char (*err_name));

void f_assert_double(double condition,
  const char* id,
  const int (*err_id),
  const char (*err_name),
  const double (*tol));

void f_savetxt_d2(const char* file,
  const double* data,
  size_t data_dim_0,
  size_t data_dim_1);

bool f_get_option_l(bool default_bn,
  const bool (*opt));

void f_null_i0(int* val,
  const f90_f_none_object* nl);

void f_null_r0(float* val,
  const f90_f_none_object* nl);

void f_null_d0(double* val,
  const f90_f_none_object* nl);

/* void f_null_d1_ptr(void); */

/* void f_null_i1_ptr(void); */

/* void f_null_i2_ptr(void); */

void f_null_l0(bool* val,
  const f90_f_none_object* nl);

#endif
