CarpetX: a Cactus driver for the Einstein Toolkit based on AMReX
#ifndef LOOPCONTROL_H
#define LOOPCONTROL_H

#include <cctk_Loop.h>

#ifdef __cplusplus
extern "C" {
#endif

// This file must mimic the GridDescBase in loop.hxx.

#define LC_DIM 3

typedef struct {
  int gsh[LC_DIM];
  int lbnd[LC_DIM], ubnd[LC_DIM];
  int lsh[LC_DIM];
  int ash[LC_DIM];
  int bbox[2 * LC_DIM];
  int nghostzones[LC_DIM];
  int tmin[LC_DIM], tmax[LC_DIM];
  CCTK_REAL x0[LC_DIM];
  CCTK_REAL dx[LC_DIM];
} GridDescBase_t;

GridDescBase_t LC_CreateGridDesc(const cGH *cctkGH);

// Replicate looping constructs in loop.hxx as good as we can, given that there
// is only one CCTK_LOOP macro that cannot distinguish between cell- and
// vertex-centred variables.
//
// This is not quite the way LoopControl does it, which would involve changing
// LC_LOOP3STROFF_NORMAL, which no longer knows what it needs to loop over and
// is only given the imin/imax ranges that Cactus thinks are required, but has
// no idea about tiles.

#define LC_LOOP3_BOX(name, i, j, k, grid, imin, imax, inormal)                 \
  do {                                                                         \
    for (int k = imin[2]; k < imax[2]; ++k) {                                  \
      for (int j = imin[1]; j < imax[1]; ++j) {                                \
        for (int i = imin[0]; i < imax[0]; ++i) {

#define LC_ENDLOOP3_BOX(name)                                                  \
  }                                                                            \
  }                                                                            \
  }                                                                            \
  }                                                                            \
  while (0)

// Replace CCTK_LOOP macros
#if !defined CCTK_LOOP3STROFF_NORMAL || !defined CCTK_ENDLOOP3STROFF_NORMAL
#error "internal error"
#endif
#undef CCTK_LOOP3STROFF_NORMAL
#undef CCTK_ENDLOOP3STROFF_NORMAL

#undef CCTK_LOOP3_ALL
#undef CCTK_ENDLOOP3_ALL

#define CCTK_LOOP3_ALL(name, cctki3_cctkGH_, i, j, k)                          \
  do {                                                                         \
    const GridDescBase_t grid = LC_CreateGridDesc(cctki3_cctkGH_);             \
    int imin[LC_DIM] CCTK_ATTRIBUTE_UNUSED,                                    \
        imax[LC_DIM] CCTK_ATTRIBUTE_UNUSED,                                    \
        inormal[LC_DIM] CCTK_ATTRIBUTE_UNUSED;                                 \
    const int offset[LC_DIM] = {1, 1, 1};                                      \
    for (int d = 0; d < LC_DIM; ++d) {                                         \
      imin[d] = grid.tmin[d];                                                  \
      imax[d] = grid.tmax[d] + (grid.tmax[d] >= grid.lsh[d] ? offset[d] : 0);  \
      if (imax[d] > grid.lsh[d] + offset[d])                                   \
        imax[d] = grid.lsh[d] + offset[d];                                     \
      inormal[d] = 0;                                                          \
    }                                                                          \
    LC_LOOP3_BOX(name, i, j, k, grid, imin, imax, inormal) {

#define CCTK_ENDLOOP3_ALL(name)                                                \
  }                                                                            \
  LC_ENDLOOP3_BOX(name);                                                       \
  }                                                                            \
  while (0)

#ifdef __cplusplus
}
#endif

#endif // LOOPCONTROL_H