// CelerisLab – streaming/esopull_single_buffer.cuh
// Esoteric-Pull single-buffer streaming  (Geier & Schönherr, 2017).
//
// Uses ONE DDF array (half the memory of double-buffer).
// Relies on paired direction ordering: (i, i+1) are opposites.
//
// Key idea: load_f and store_f alternate read/write positions
// based on timestep parity, so they never conflict in a single step.
//
// Even step (t%2 == 0):
//   Read:   f[i]   ← fi[n,    i+1]   (local, reverse slot)
//           f[i+1] ← fi[j[i], i  ]   (neighbor, forward slot)
//   Write:  fi[j[i], i+1] ← f[i]     (neighbor, reverse slot)
//           fi[n,    i  ] ← f[i+1]   (local, forward slot)
//
// Odd step (t%2 == 1):
//   Read:   f[i]   ← fi[n,    i  ]   (local, forward slot)
//           f[i+1] ← fi[j[i], i+1]   (neighbor, reverse slot)
//   Write:  fi[j[i], i+1] ← f[i]     (neighbor, forward slot → stored in reverse)
//           fi[n,    i  ] ← f[i+1]   (local, reverse slot → stored in forward)
//
// Wait, let me use the exact FluidX3D convention:
//   Odd step read:  f[i] ← fi[n, i],   f[i+1] ← fi[j[i], i+1]
//   Odd step write: fi[j[i], i+1] ← f[i],  fi[n, i] ← f[i+1]
// ============================================================================

#ifndef CELERIS_STREAMING_ESOPULL_SINGLE_BUFFER_CUH
#define CELERIS_STREAMING_ESOPULL_SINGLE_BUFFER_CUH

// ---------------------------------------------------------------------------
// load_f_esopull:  Esoteric-Pull read phase (streaming second half)
// ---------------------------------------------------------------------------
__device__ inline void load_f_esopull(unsigned long n,
                                      float* __restrict__ f,
                                      const fpxx* __restrict__ fi,
                                      const unsigned long* __restrict__ j,
                                      unsigned long t)
{
    // i=0: rest particle always from self
    f[0] = load_ddf(fi, index_f(n, 0u));

    for (unsigned int i = 1; i < NQ; i += 2) {
        if (t & 1) {
            // Odd step
            f[i]     = load_ddf(fi, index_f(n,    i));
            f[i + 1] = load_ddf(fi, index_f(j[i], i + 1));
        } else {
            // Even step
            f[i]     = load_ddf(fi, index_f(n,    i + 1));
            f[i + 1] = load_ddf(fi, index_f(j[i], i));
        }
    }
}

// ---------------------------------------------------------------------------
// store_f_esopull:  Esoteric-Pull write phase (streaming first half)
// ---------------------------------------------------------------------------
__device__ inline void store_f_esopull(unsigned long n,
                                       const float* __restrict__ f,
                                       fpxx* __restrict__ fi,
                                       const unsigned long* __restrict__ j,
                                       unsigned long t)
{
    store_ddf(fi, index_f(n, 0u), f[0]);

    for (unsigned int i = 1; i < NQ; i += 2) {
        if (t & 1) {
            // Odd step
            store_ddf(fi, index_f(j[i], i + 1), f[i]);
            store_ddf(fi, index_f(n,    i),     f[i + 1]);
        } else {
            // Even step
            store_ddf(fi, index_f(j[i], i),     f[i]);
            store_ddf(fi, index_f(n,    i + 1), f[i + 1]);
        }
    }
}

#endif // CELERIS_STREAMING_ESOPULL_SINGLE_BUFFER_CUH
