// Spatiotemporal blue noise by void-and-cluster (Ulichney's method with the energy of Wolfe et al., EGSR 2022):
// two cells interact only if they share a frame (a Gaussian in space, sigma 1.9) or share a pixel (a Gaussian in
// time, sigma 1.9), so every frame is blue noise and every pixel's sequence over time is blue noise.
// Writes the ranks as text: T frames of n x n integers, threshold = (rank + 1/2) / (T n n).
//   cc -O2 -o stbn stbn.c -lm && ./stbn 64 32 1 > stbn64x32.txt
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
static int n, T, R = 6; static double *E, ks[13][13], kt[13];
static unsigned char *on; static int *rank_;
static unsigned long long s = 88172645463325252ULL;
static double rnd(void) { s ^= s << 13; s ^= s >> 7; s ^= s << 17; return (s >> 11) * (1.0 / 9007199254740992.0); }
static void put(int c, double sign) {
  int t = c / (n * n), y = (c / n) % n, x = c % n;
  for (int dy = -R; dy <= R; dy++) for (int dx = -R; dx <= R; dx++)
    E[t * n * n + ((y + dy + n) % n) * n + (x + dx + n) % n] += sign * ks[dy + R][dx + R];
  for (int dt = -R; dt <= R; dt++) if (dt) E[((t + dt + T) % T) * n * n + y * n + x] += sign * kt[dt + R];
}
static int extreme(int want_on, int maxi) {  // the tightest cluster among on cells (maxi) or the largest void among off
  int best = -1; double b = maxi ? -1e300 : 1e300;
  for (int c = 0; c < T * n * n; c++) if (on[c] == want_on) {
    double e = E[c] + 1e-9 * rnd();
    if (maxi ? e > b : e < b) { b = e; best = c; }
  }
  return best;
}
int main(int argc, char **argv) {
  n = atoi(argv[1]); T = atoi(argv[2]); s += atoi(argv[3]) * 0x9E3779B97F4A7C15ULL;
  double sg = 1.9; int M = T * n * n;
  for (int i = -R; i <= R; i++) { kt[i + R] = exp(-i * i / (2 * sg * sg)); for (int j = -R; j <= R; j++) ks[i + R][j + R] = exp(-(i * i + j * j) / (2 * sg * sg)); }
  E = calloc(M, sizeof *E); on = calloc(M, 1); rank_ = malloc(M * sizeof *rank_);
  int ones = M / 10;
  for (int k = 0; k < ones;) { int c = (int)(rnd() * M); if (!on[c]) { on[c] = 1; put(c, 1); k++; } }
  for (;;) {  // relax: move the tightest cluster to the largest void until it's where it started
    int a = extreme(1, 1); on[a] = 0; put(a, -1);
    int b = extreme(0, 0); on[b] = 1; put(b, 1);
    if (a == b) break;
  }
  unsigned char *keep = malloc(M); for (int c = 0; c < M; c++) keep[c] = on[c];
  for (int r = ones - 1; r >= 0; r--) { int a = extreme(1, 1); on[a] = 0; put(a, -1); rank_[a] = r; }
  for (int c = 0; c < M; c++) if (keep[c]) { on[c] = 1; put(c, 1); }
  for (int r = ones; r < M; r++) {  // then fill: each new cell in the largest void
    int b = extreme(0, 0); on[b] = 1; put(b, 1); rank_[b] = r;
    if (r % 16384 == 0) fprintf(stderr, "%d/%d\n", r, M);
  }
  printf("%d %d\n", T, n);
  for (int c = 0; c < M; c++) printf("%d%c", rank_[c], (c % n == n - 1) ? '\n' : ' ');
  return 0;
}
