#ifndef _ARRAYS_H_
#define _ARRAYS_H_ 1

// (be careful. Current i2c source uses 7 and loops on
//   "<" from 1, not "<=" from 1 or "<" from 0...)
#define _imp_MAX_ARRAY_DIMS 6

// We need a stack to save dynamic bounds in when declaring arrays, in case one of
// the bounds in the declaration is the result of a function call and that function
// itself declares a dynamic array...  I wonder if I could have used alloca or malloc
// for this purpose and avoided the need to reserve space for a stack?

#define _imp_MAX_NESTED_ARRAY_DECLS 16
//  ( who knows if it is enough... we also *must* check for this overflow! )

#define _imp_MAX_BOUND_STACK (_imp_MAX_ARRAY_DIMS * _imp_MAX_NESTED_ARRAY_DECLS)
int next_free_bound_index = 0;

// This is the stack:
int _lb[_imp_MAX_ARRAY_DIMS * _imp_MAX_NESTED_ARRAY_DECLS], _ub[_imp_MAX_ARRAY_DIMS * _imp_MAX_NESTED_ARRAY_DECLS];


// This is the format of a vector for arrays of any dimension:
//  ( although 1D arrays can be optimised, in some circumstances )

typedef struct _imp_dv {
    void *base;                             // Pointer to element (LB_0, LB_1, ...)
    _imp_address aligned_element_size;      // Element stride in bytes
    int dims[_imp_MAX_ARRAY_DIMS * 2 + 1];  // dims[0] = #dims, followed by LB/UB pairs
} _imp_dv;

// Internal helper functions.  This is not how we extract bounds for a program to use.
// ('d' is the dimensionality of the array)

#define _imp_lb(dv, d)       ((dv)->dims[2 * (d) + 1])
#define _imp_ub(dv, d)       ((dv)->dims[2 * (d) + 2])
#define _imp_nelems(dv, d)   (_imp_ub(dv, d) - _imp_lb(dv, d) + 1)


// Convert an index from Imp's lower bound to C's 0 base.
// ( This is where I would insert the array bounds check, calling _imp_signal(?,?,?) ... )

// dv is the dopevector, i is the index, d is the dimension we're indexing. (first is 0)
// (NOT yet handling the reversal of index order for row- vs column-major layout)
#define _imp_adjust_lb(dv, i, d)   ((i) - _imp_lb(dv, d))


// Access the array element.  Uses some C macro hackery for variable number of indices

#define _imp_aref_1(type, dv, i0) \
    (*(type*)(( _imp_address )(dv)->base + \
        (_imp_adjust_lb(dv, i0, 0) * (dv)->aligned_element_size)))

#define _imp_aref_2(type, dv, i0, i1) \
    (*(type*)(( _imp_address )(dv)->base + \
        ((_imp_adjust_lb(dv, i0, 0)  * _imp_nelems(dv, 1) \
        + _imp_adjust_lb(dv, i1, 1)) * (dv)->aligned_element_size)))

#define _imp_aref_3(type, dv, i0, i1, i2) \
    (*(type*)(( _imp_address )(dv)->base + \
        (((_imp_adjust_lb(dv, i0, 0)  * _imp_nelems(dv, 1) \
         + _imp_adjust_lb(dv, i1, 1)) * _imp_nelems(dv, 2) \
         + _imp_adjust_lb(dv, i2, 2)) * (dv)->aligned_element_size)))

#define _imp_aref_4(type, dv, i0, i1, i2, i3) \
    (*(type*)(( _imp_address )(dv)->base + \
        ((((_imp_adjust_lb(dv, i0, 0)  * _imp_nelems(dv, 1) \
          + _imp_adjust_lb(dv, i1, 1)) * _imp_nelems(dv, 2) \
          + _imp_adjust_lb(dv, i2, 2)) * _imp_nelems(dv, 3) \
          + _imp_adjust_lb(dv, i3, 3)) * (dv)->aligned_element_size)))

#define _imp_aref_5(type, dv, i0, i1, i2, i3, i4) \
    (*(type*)(( _imp_address )(dv)->base + \
        (((((_imp_adjust_lb(dv, i0, 0)  * _imp_nelems(dv, 1) \
           + _imp_adjust_lb(dv, i1, 1)) * _imp_nelems(dv, 2) \
           + _imp_adjust_lb(dv, i2, 2)) * _imp_nelems(dv, 3) \
           + _imp_adjust_lb(dv, i3, 3)) * _imp_nelems(dv, 4) \
           + _imp_adjust_lb(dv, i4, 4)) * (dv)->aligned_element_size)))

#define _imp_aref_6(type, dv, i0, i1, i2, i3, i4, i5) \
    (*(type*)(( _imp_address )(dv)->base + \
        ((((((_imp_adjust_lb(dv, i0, 0)  * _imp_nelems(dv, 1) \
            + _imp_adjust_lb(dv, i1, 1)) * _imp_nelems(dv, 2) \
            + _imp_adjust_lb(dv, i2, 2)) * _imp_nelems(dv, 3) \
            + _imp_adjust_lb(dv, i3, 3)) * _imp_nelems(dv, 4) \
            + _imp_adjust_lb(dv, i4, 4)) * _imp_nelems(dv, 5) \
            + _imp_adjust_lb(dv, i5, 5)) * (dv)->aligned_element_size)))


// put it all together.  Should work as long as an arrayname parameter is
// consistently always the same (and matching) number of dimensions as expected by
// the callee, which I sincerely hope is being handled by pass1 of Imp77.

#define _imp_GET_MACRO(_1, _2, _3, _4, _5, _6, NAME, ...) NAME
#define _imp_aref(type, name, ...) _imp_GET_MACRO(__VA_ARGS__, _imp_aref_6, _imp_aref_5, _imp_aref_4, _imp_aref_3, _imp_aref_2, _imp_aref_1)(type, &name##_dv, __VA_ARGS__)


// This implementation involves massive duplication of macros because external
// array declarations within blocks need diferent handling from external arrays
// (which must have constant bounds); fortunately the translator knows at
// translate time if it is translating an external array (or indeed any
// array with constant bounds).  And then we have to handle %specs for external
// arrays differently, and probably construct local dopevectors for them.


// These ones are used in blocks when any bound in the declaration of the array
// can be a function call that has side-effects:

// ====================================================================================

// These calls *must* be matched by an _imp_end_array_* call.

#define _imp_freeze_bounds_1(linkage, type, name, lb1, ub1) \
    next_free_bound_index += 1; \
    _lb[next_free_bound_index-1] = lb1; _ub[next_free_bound_index-1] = ub1

#define _imp_freeze_bounds_2(linkage, type, name, lb1, ub1, lb2, ub2) \
    next_free_bound_index += 2; \
    _lb[next_free_bound_index-2] = lb1; _ub[next_free_bound_index-2] = ub1; \
    _lb[next_free_bound_index-1] = lb2; _ub[next_free_bound_index-1] = ub2

#define _imp_freeze_bounds_3(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3) \
    next_free_bound_index += 3; \
    _lb[next_free_bound_index-3] = lb1; _ub[next_free_bound_index-3] = ub1; \
    _lb[next_free_bound_index-2] = lb2; _ub[next_free_bound_index-2] = ub2; \
    _lb[next_free_bound_index-1] = lb3; _ub[next_free_bound_index-1] = ub3

#define _imp_freeze_bounds_4(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4) \
    next_free_bound_index += 4; \
    _lb[next_free_bound_index-4] = lb1; _ub[next_free_bound_index-4] = ub1; \
    _lb[next_free_bound_index-3] = lb2; _ub[next_free_bound_index-3] = ub2; \
    _lb[next_free_bound_index-2] = lb3; _ub[next_free_bound_index-2] = ub3; \
    _lb[next_free_bound_index-1] = lb4; _ub[next_free_bound_index-1] = ub4

#define _imp_freeze_bounds_5(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5) \
    next_free_bound_index += 5; \
    _lb[next_free_bound_index-5] = lb1; _ub[next_free_bound_index-5] = ub1; \
    _lb[next_free_bound_index-4] = lb2; _ub[next_free_bound_index-4] = ub2; \
    _lb[next_free_bound_index-3] = lb3; _ub[next_free_bound_index-3] = ub3; \
    _lb[next_free_bound_index-2] = lb4; _ub[next_free_bound_index-2] = ub4; \
    _lb[next_free_bound_index-1] = lb5; _ub[next_free_bound_index-1] = ub5

#define _imp_freeze_bounds_6(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5, lb6, ub6) \
    next_free_bound_index += 6; \
    _lb[next_free_bound_index-6] = lb1; _ub[next_free_bound_index-6] = ub1; \
    _lb[next_free_bound_index-5] = lb2; _ub[next_free_bound_index-5] = ub2; \
    _lb[next_free_bound_index-4] = lb3; _ub[next_free_bound_index-4] = ub3; \
    _lb[next_free_bound_index-3] = lb4; _ub[next_free_bound_index-3] = ub4; \
    _lb[next_free_bound_index-2] = lb5; _ub[next_free_bound_index-2] = ub5; \
    _lb[next_free_bound_index-1] = lb6; _ub[next_free_bound_index-1] = ub6

// ====================================================================================

#define _imp_create_array_1(linkage, type, name, lb1, ub1) \
    linkage type name \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]

#define _imp_create_array_2(linkage, type, name, lb1, ub1, lb2, ub2) \
    linkage type name \
             [_ub[next_free_bound_index-2]-_lb[next_free_bound_index-2]+1]  \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]

#define _imp_create_array_3(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3) \
    linkage type name \
             [_ub[next_free_bound_index-3]-_lb[next_free_bound_index-3]+1]  \
             [_ub[next_free_bound_index-2]-_lb[next_free_bound_index-2]+1]  \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]

#define _imp_create_array_4(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4) \
    linkage type name \
             [_ub[next_free_bound_index-4]-_lb[next_free_bound_index-4]+1]  \
             [_ub[next_free_bound_index-3]-_lb[next_free_bound_index-3]+1]  \
             [_ub[next_free_bound_index-2]-_lb[next_free_bound_index-2]+1]  \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]

#define _imp_create_array_5(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5) \
    linkage type name \
             [_ub[next_free_bound_index-5]-_lb[next_free_bound_index-5]+1]  \
             [_ub[next_free_bound_index-4]-_lb[next_free_bound_index-4]+1]  \
             [_ub[next_free_bound_index-3]-_lb[next_free_bound_index-3]+1]  \
             [_ub[next_free_bound_index-2]-_lb[next_free_bound_index-2]+1]  \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]

#define _imp_create_array_6(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5, lb6, ub6) \
    linkage type name \
             [_ub[next_free_bound_index-6]-_lb[next_free_bound_index-6]+1]  \
             [_ub[next_free_bound_index-5]-_lb[next_free_bound_index-5]+1]  \
             [_ub[next_free_bound_index-4]-_lb[next_free_bound_index-4]+1]  \
             [_ub[next_free_bound_index-3]-_lb[next_free_bound_index-3]+1]  \
             [_ub[next_free_bound_index-2]-_lb[next_free_bound_index-2]+1]  \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]

// ====================================================================================

#define _imp_create_dv_1(linkage, type, name, lb1, ub1) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[1] - (_imp_address)&name[0], \
      .dims = { \
        1, \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 1 */  \
      } \
    }

#define _imp_create_dv_2(linkage, type, name, lb1, ub1, lb2, ub2) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][1] - (_imp_address)&name[0][0], \
      .dims = { \
        2, \
        _lb[next_free_bound_index-2], _ub[next_free_bound_index-2],  /* bound 1 */  \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 2 */  \
      } \
    }

#define _imp_create_dv_3(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][1] - (_imp_address)&name[0][0][0], \
      .dims = { \
        3, \
        _lb[next_free_bound_index-3], _ub[next_free_bound_index-3],  /* bound 1 */  \
        _lb[next_free_bound_index-2], _ub[next_free_bound_index-2],  /* bound 2 */  \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 3 */  \
      } \
    }

#define _imp_create_dv_4(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][0][1] - (_imp_address)&name[0][0][0][0], \
      .dims = { \
        4, \
        _lb[next_free_bound_index-4], _ub[next_free_bound_index-4],  /* bound 1 */  \
        _lb[next_free_bound_index-3], _ub[next_free_bound_index-3],  /* bound 2 */  \
        _lb[next_free_bound_index-2], _ub[next_free_bound_index-2],  /* bound 3 */  \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 4 */  \
      } \
    }

#define _imp_create_dv_5(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][0][0][1] - (_imp_address)&name[0][0][0][0][0], \
      .dims = { \
        5, \
        _lb[next_free_bound_index-5], _ub[next_free_bound_index-5],  /* bound 1 */  \
        _lb[next_free_bound_index-4], _ub[next_free_bound_index-4],  /* bound 2 */  \
        _lb[next_free_bound_index-3], _ub[next_free_bound_index-3],  /* bound 3 */  \
        _lb[next_free_bound_index-2], _ub[next_free_bound_index-2],  /* bound 4 */  \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 5 */  \
      } \
    }

#define _imp_create_dv_6(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5, lb6, ub6) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][0][0][0][1] - (_imp_address)&name[0][0][0][0][0][0], \
      .dims = { \
        6, \
        _lb[next_free_bound_index-6], _ub[next_free_bound_index-6],  /* bound 1 */  \
        _lb[next_free_bound_index-5], _ub[next_free_bound_index-5],  /* bound 2 */  \
        _lb[next_free_bound_index-4], _ub[next_free_bound_index-4],  /* bound 3 */  \
        _lb[next_free_bound_index-3], _ub[next_free_bound_index-3],  /* bound 4 */  \
        _lb[next_free_bound_index-2], _ub[next_free_bound_index-2],  /* bound 5 */  \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 6 */  \
      } \
    }

// ====================================================================================

#define _imp_end_array_1(linkage, type, name, lb1, ub1) \
    next_free_bound_index -= 1

#define _imp_end_array_2(linkage, type, name, lb1, ub1, lb2, ub2) \
    next_free_bound_index -= 2

#define _imp_end_array_3(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3) \
    next_free_bound_index -= 3

#define _imp_end_array_4(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4) \
    next_free_bound_index -= 4

#define _imp_end_array_5(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5) \
    next_free_bound_index -= 5

#define _imp_end_array_6(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5, lb6, ub6) \
    next_free_bound_index -= 6

// ==================================================================================== Replacement for code below:

#define _imp_declare_array_1(linkage, type, name, lb1, ub1) \
  _imp_freeze_bounds_1(linkage, type, name, lb1, ub1); \
  _imp_create_array_1(linkage, type, name, lb1, ub1); \
  _imp_create_dv_1(linkage, type, name, lb1, ub1); \
  _imp_end_array_1(linkage, type, name, lb1, ub1)

#define _imp_declare_array_2(linkage, type, name, lb1, ub1, lb2, ub2) \
  _imp_freeze_bounds_2(linkage, type, name, lb1, ub1, lb2, ub2); \
  _imp_create_array_2(linkage, type, name, lb1, ub1, lb2, ub2); \
  _imp_create_dv_2(linkage, type, name, lb1, ub1, lb2, ub2); \
  _imp_end_array_2(linkage, type, name, lb1, ub1, lb2, ub2)

#define _imp_declare_array_3(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3) \
  _imp_freeze_bounds_3(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3); \
  _imp_create_array_3(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3); \
  _imp_create_dv_3(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3); \
  _imp_end_array_3(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3)

#define _imp_declare_array_4(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4) \
  _imp_freeze_bounds_4(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4); \
  _imp_create_array_4(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4); \
  _imp_create_dv_4(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4); \
  _imp_end_array_4(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4)

#define _imp_declare_array_5(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5) \
  _imp_freeze_bounds_5(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5); \
  _imp_create_array_5(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5); \
  _imp_create_dv_5(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5); \
  _imp_end_array_5(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5)

#define _imp_declare_array_6(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5, lb6, ub6) \
  _imp_freeze_bounds_6(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5, lb6, ub6); \
  _imp_create_array_6(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5, lb6, ub6); \
  _imp_create_dv_6(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5, lb6, ub6); \
  _imp_end_array_6(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5, lb6, ub6)

// ====================================================================================
#ifdef NEVER

#define _imp_declare_array_1(linkage, type, name, lb1, ub1)      \
    next_free_bound_index += 1; \
    _lb[next_free_bound_index-1] = lb1; _ub[next_free_bound_index-1] = ub1; \
    linkage type name \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]; \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[1] - (_imp_address)&name[0], \
      .dims = { \
        1, \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 1 */  \
      } \
    }; \
    next_free_bound_index -= 1

#define _imp_declare_array_2(linkage, type, name, lb1, ub1, lb2, ub2)    \
    next_free_bound_index += 2; \
    _lb[next_free_bound_index-2] = lb1; _ub[next_free_bound_index-2] = ub1; \
    _lb[next_free_bound_index-1] = lb2; _ub[next_free_bound_index-1] = ub2; \
    linkage type name \
             [_ub[next_free_bound_index-2]-_lb[next_free_bound_index-2]+1]  \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]; \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][1] - (_imp_address)&name[0][0], \
      .dims = { \
        2, \
        _lb[next_free_bound_index-2], _ub[next_free_bound_index-2],  /* bound 1 */  \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 2 */  \
      } \
    }; \
    next_free_bound_index -= 2

#define _imp_declare_array_3(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3) \
    next_free_bound_index += 3; \
    _lb[next_free_bound_index-3] = lb1; _ub[next_free_bound_index-3] = ub1; \
    _lb[next_free_bound_index-2] = lb2; _ub[next_free_bound_index-2] = ub2; \
    _lb[next_free_bound_index-1] = lb3; _ub[next_free_bound_index-1] = ub3; \
    linkage type name \
             [_ub[next_free_bound_index-3]-_lb[next_free_bound_index-3]+1]  \
             [_ub[next_free_bound_index-2]-_lb[next_free_bound_index-2]+1]  \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]; \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][1] - (_imp_address)&name[0][0][0], \
      .dims = { \
        3, \
        _lb[next_free_bound_index-3], _ub[next_free_bound_index-3],  /* bound 1 */  \
        _lb[next_free_bound_index-2], _ub[next_free_bound_index-2],  /* bound 2 */  \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 3 */  \
      } \
    }; \
    next_free_bound_index -= 3

#define _imp_declare_array_4(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4) \
    next_free_bound_index += 4; \
    _lb[next_free_bound_index-4] = lb1; _ub[next_free_bound_index-4] = ub1; \
    _lb[next_free_bound_index-3] = lb2; _ub[next_free_bound_index-3] = ub2; \
    _lb[next_free_bound_index-2] = lb3; _ub[next_free_bound_index-2] = ub3; \
    _lb[next_free_bound_index-1] = lb4; _ub[next_free_bound_index-1] = ub4; \
    linkage type name \
             [_ub[next_free_bound_index-4]-_lb[next_free_bound_index-4]+1]  \
             [_ub[next_free_bound_index-3]-_lb[next_free_bound_index-3]+1]  \
             [_ub[next_free_bound_index-2]-_lb[next_free_bound_index-2]+1]  \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]; \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][0][1] - (_imp_address)&name[0][0][0][0], \
      .dims = { \
        4, \
        _lb[next_free_bound_index-4], _ub[next_free_bound_index-4],  /* bound 1 */  \
        _lb[next_free_bound_index-3], _ub[next_free_bound_index-3],  /* bound 2 */  \
        _lb[next_free_bound_index-2], _ub[next_free_bound_index-2],  /* bound 3 */  \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 4 */  \
      } \
    }; \
    next_free_bound_index -= 4

#define _imp_declare_array_5(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5) \
    next_free_bound_index += 5; \
    _lb[next_free_bound_index-5] = lb1; _ub[next_free_bound_index-5] = ub1; \
    _lb[next_free_bound_index-4] = lb2; _ub[next_free_bound_index-4] = ub2; \
    _lb[next_free_bound_index-3] = lb3; _ub[next_free_bound_index-3] = ub3; \
    _lb[next_free_bound_index-2] = lb4; _ub[next_free_bound_index-2] = ub4; \
    _lb[next_free_bound_index-1] = lb5; _ub[next_free_bound_index-1] = ub5; \
    linkage type name \
             [_ub[next_free_bound_index-5]-_lb[next_free_bound_index-5]+1]  \
             [_ub[next_free_bound_index-4]-_lb[next_free_bound_index-4]+1]  \
             [_ub[next_free_bound_index-3]-_lb[next_free_bound_index-3]+1]  \
             [_ub[next_free_bound_index-2]-_lb[next_free_bound_index-2]+1]  \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]; \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][0][0][1] - (_imp_address)&name[0][0][0][0][0], \
      .dims = { \
        5, \
        _lb[next_free_bound_index-5], _ub[next_free_bound_index-5],  /* bound 1 */  \
        _lb[next_free_bound_index-4], _ub[next_free_bound_index-4],  /* bound 2 */  \
        _lb[next_free_bound_index-3], _ub[next_free_bound_index-3],  /* bound 3 */  \
        _lb[next_free_bound_index-2], _ub[next_free_bound_index-2],  /* bound 4 */  \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 5 */  \
      } \
    }; \
    next_free_bound_index -= 5

#define _imp_declare_array_6(linkage, type, name, lb1, ub1, lb2, ub2, lb3, ub3, lb4, ub4, lb5, ub5, lb6, ub6) \
    next_free_bound_index += 6; \
    _lb[next_free_bound_index-6] = lb1; _ub[next_free_bound_index-6] = ub1; \
    _lb[next_free_bound_index-5] = lb2; _ub[next_free_bound_index-5] = ub2; \
    _lb[next_free_bound_index-4] = lb3; _ub[next_free_bound_index-4] = ub3; \
    _lb[next_free_bound_index-3] = lb4; _ub[next_free_bound_index-3] = ub4; \
    _lb[next_free_bound_index-2] = lb5; _ub[next_free_bound_index-2] = ub5; \
    _lb[next_free_bound_index-1] = lb6; _ub[next_free_bound_index-1] = ub6; \
    linkage type name \
             [_ub[next_free_bound_index-6]-_lb[next_free_bound_index-6]+1]  \
             [_ub[next_free_bound_index-5]-_lb[next_free_bound_index-5]+1]  \
             [_ub[next_free_bound_index-4]-_lb[next_free_bound_index-4]+1]  \
             [_ub[next_free_bound_index-3]-_lb[next_free_bound_index-3]+1]  \
             [_ub[next_free_bound_index-2]-_lb[next_free_bound_index-2]+1]  \
             [_ub[next_free_bound_index-1]-_lb[next_free_bound_index-1]+1]; \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][0][0][0][1] - (_imp_address)&name[0][0][0][0][0][0], \
      .dims = { \
        6, \
        _lb[next_free_bound_index-6], _ub[next_free_bound_index-6],  /* bound 1 */  \
        _lb[next_free_bound_index-5], _ub[next_free_bound_index-5],  /* bound 2 */  \
        _lb[next_free_bound_index-4], _ub[next_free_bound_index-4],  /* bound 3 */  \
        _lb[next_free_bound_index-3], _ub[next_free_bound_index-3],  /* bound 4 */  \
        _lb[next_free_bound_index-2], _ub[next_free_bound_index-2],  /* bound 5 */  \
        _lb[next_free_bound_index-1], _ub[next_free_bound_index-1],  /* bound 6 */  \
      } \
    }; \
    next_free_bound_index -= 6

#endif

// =====================================================================================

#define _imp_GET_MACRO_PAIRS(_1a, _1b, _2a, _2b, _3a, _3b, _4a, _4b, _5a, _5b, _6a, _6b, NAME, ...) NAME

#define _imp_freeze_bounds(linkage, type, name, ...) _imp_GET_MACRO_PAIRS(__VA_ARGS__, \
    _imp_freeze_bounds_6, _imp_freeze_bounds_6, \
    _imp_freeze_bounds_5, _imp_freeze_bounds_5, \
    _imp_freeze_bounds_4, _imp_freeze_bounds_4, \
    _imp_freeze_bounds_3, _imp_freeze_bounds_3, \
    _imp_freeze_bounds_2, _imp_freeze_bounds_2, \
    _imp_freeze_bounds_1, _imp_freeze_bounds_1  \
    )(linkage, type, name, __VA_ARGS__)

#define _imp_create_array(linkage, type, name, ...) _imp_GET_MACRO_PAIRS(__VA_ARGS__, \
    _imp_create_array_6, _imp_create_array_6, \
    _imp_create_array_5, _imp_create_array_5, \
    _imp_create_array_4, _imp_create_array_4, \
    _imp_create_array_3, _imp_create_array_3, \
    _imp_create_array_2, _imp_create_array_2, \
    _imp_create_array_1, _imp_create_array_1  \
    )(linkage, type, name, __VA_ARGS__)

#define _imp_create_dv(linkage, type, name, ...) _imp_GET_MACRO_PAIRS(__VA_ARGS__, \
    _imp_create_dv_6, _imp_create_dv_6, \
    _imp_create_dv_5, _imp_create_dv_5, \
    _imp_create_dv_4, _imp_create_dv_4, \
    _imp_create_dv_3, _imp_create_dv_3, \
    _imp_create_dv_2, _imp_create_dv_2, \
    _imp_create_dv_1, _imp_create_dv_1  \
    )(linkage, type, name, __VA_ARGS__)

#define _imp_end_array(linkage, type, name, ...) _imp_GET_MACRO_PAIRS(__VA_ARGS__, \
    _imp_end_array_6, _imp_end_array_6, \
    _imp_end_array_5, _imp_end_array_5, \
    _imp_end_array_4, _imp_end_array_4, \
    _imp_end_array_3, _imp_end_array_3, \
    _imp_end_array_2, _imp_end_array_2, \
    _imp_end_array_1, _imp_end_array_1  \
    )(linkage, type, name, __VA_ARGS__)

#define _imp_declare_array(linkage, type, name, ...) _imp_GET_MACRO_PAIRS(__VA_ARGS__, \
    _imp_declare_array_6, _imp_declare_array_6, \
    _imp_declare_array_5, _imp_declare_array_5, \
    _imp_declare_array_4, _imp_declare_array_4, \
    _imp_declare_array_3, _imp_declare_array_3, \
    _imp_declare_array_2, _imp_declare_array_2, \
    _imp_declare_array_1, _imp_declare_array_1  \
    )(linkage, type, name, __VA_ARGS__)




#define _imp_declare_constboundsarraydecl_1(linkage, type, name, _lb1, _ub1)     \
    linkage type name \
             [_ub1-_lb1+1]

#define _imp_declare_constboundsarraydecl_2(linkage, type, name, _lb1, _ub1, _lb2, _ub2) \
    linkage type name \
             [_ub1-_lb1+1]  \
             [_ub2-_lb2+1]

#define _imp_declare_constboundsarraydecl_3(linkage, type, name, _lb1, _ub1, _lb2, _ub2, _lb3, _ub3) \
    linkage type name \
             [_ub1-_lb1+1]  \
             [_ub2-_lb2+1]  \
             [_ub3-_lb3+1]

#define _imp_declare_constboundsarraydecl_4(linkage, type, name, _lb1, _ub1, _lb2, _ub2, _lb3, _ub3, _lb4, _ub4) \
    linkage type name \
             [_ub1-_lb1+1]  \
             [_ub2-_lb2+1]  \
             [_ub3-_lb3+1]  \
             [_ub4-_lb4+1]

#define _imp_declare_constboundsarraydecl_5(linkage, type, name, _lb1, _ub1, _lb2, _ub2, _lb3, _ub3, _lb4, _ub4, _lb5, _ub5) \
    linkage type name \
             [_ub1-_lb1+1]  \
             [_ub2-_lb2+1]  \
             [_ub3-_lb3+1]  \
             [_ub4-_lb4+1]  \
             [_ub5-_lb5+1]

#define _imp_declare_constboundsarraydecl_6(linkage, type, name, _lb1, _ub1, _lb2, _ub2, _lb3, _ub3, _lb4, _ub4, _lb5, _ub5, _lb6, _ub6) \
    linkage type name \
             [_ub1-_lb1+1]  \
             [_ub2-_lb2+1]  \
             [_ub3-_lb3+1]  \
             [_ub4-_lb4+1]  \
             [_ub5-_lb5+1]  \
             [_ub6-_lb6+1]


#define _imp_create_constdv_1(linkage, type, name, _lb1, _ub1)     \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[1] - (_imp_address)&name[0], \
      .dims = { \
        1, \
        _lb1, _ub1,  /* bound 1 */  \
      } \
    }

#define _imp_create_constdv_2(linkage, type, name, _lb1, _ub1, _lb2, _ub2) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][1] - (_imp_address)&name[0][0], \
      .dims = { \
        2, \
        _lb1, _ub1,  /* bound 1 */  \
        _lb2, _ub2,  /* bound 2 */  \
      } \
    }

#define _imp_create_constdv_3(linkage, type, name, _lb1, _ub1, _lb2, _ub2, _lb3, _ub3) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][1] - (_imp_address)&name[0][0][0], \
      .dims = { \
        3, \
        _lb1, _ub1,  /* bound 1 */  \
        _lb2, _ub2,  /* bound 2 */  \
        _lb3, _ub3,  /* bound 3 */  \
      } \
    }

#define _imp_create_constdv_4(linkage, type, name, _lb1, _ub1, _lb2, _ub2, _lb3, _ub3, _lb4, _ub4) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][0][1] - (_imp_address)&name[0][0][0][0], \
      .dims = { \
        4, \
        _lb1, _ub1,  /* bound 1 */  \
        _lb2, _ub2,  /* bound 2 */  \
        _lb3, _ub3,  /* bound 3 */  \
        _lb4, _ub4,  /* bound 4 */  \
      } \
    }

#define _imp_create_constdv_5(linkage, type, name, _lb1, _ub1, _lb2, _ub2, _lb3, _ub3, _lb4, _ub4, _lb5, _ub5) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][0][0][1] - (_imp_address)&name[0][0][0][0][0], \
      .dims = { \
        5, \
        _lb1, _ub1,  /* bound 1 */  \
        _lb2, _ub2,  /* bound 2 */  \
        _lb3, _ub3,  /* bound 3 */  \
        _lb4, _ub4,  /* bound 4 */  \
        _lb5, _ub5,  /* bound 5 */  \
      } \
    }

#define _imp_create_constdv_6(linkage, type, name, _lb1, _ub1, _lb2, _ub2, _lb3, _ub3, _lb4, _ub4, _lb5, _ub5, _lb6, _ub6) \
    linkage _imp_dv name ## _dv = { \
      .base = name, \
      .aligned_element_size = (_imp_address)&name[0][0][0][0][0][1] - (_imp_address)&name[0][0][0][0][0][0], \
      .dims = { \
        6, \
        _lb1, _ub1,  /* bound 1 */  \
        _lb2, _ub2,  /* bound 2 */  \
        _lb3, _ub3,  /* bound 3 */  \
        _lb4, _ub4,  /* bound 4 */  \
        _lb5, _ub5,  /* bound 5 */  \
        _lb6, _ub6,  /* bound 6 */  \
      } \
    }

#define _imp_create_constdv(linkage, type, name, ...) _imp_GET_MACRO_PAIRS(__VA_ARGS__, \
    _imp_create_constdv_6, _imp_create_constdv_6, \
    _imp_create_constdv_5, _imp_create_constdv_5, \
    _imp_create_constdv_4, _imp_create_constdv_4, \
    _imp_create_constdv_3, _imp_create_constdv_3, \
    _imp_create_constdv_2, _imp_create_constdv_2, \
    _imp_create_constdv_1, _imp_create_constdv_1  \
    )(linkage, type, name, __VA_ARGS__)


#define _imp_declare_constboundsarraydecl(linkage, type, name, ...) _imp_GET_MACRO_PAIRS(__VA_ARGS__, \
    _imp_declare_constboundsarraydecl_6, _imp_declare_constboundsarraydecl_6, \
    _imp_declare_constboundsarraydecl_5, _imp_declare_constboundsarraydecl_5, \
    _imp_declare_constboundsarraydecl_4, _imp_declare_constboundsarraydecl_4, \
    _imp_declare_constboundsarraydecl_3, _imp_declare_constboundsarraydecl_3, \
    _imp_declare_constboundsarraydecl_2, _imp_declare_constboundsarraydecl_2, \
    _imp_declare_constboundsarraydecl_1, _imp_declare_constboundsarraydecl_1  \
    )(linkage, type, name, __VA_ARGS__)

// ===================================================================================

#endif // _ARRAYS_H_
