#include <math.h>
#include <stdio.h>
#include <stdlib.h>

double simple_linear_regression(const double *ys, int n) {
  double xbar = 0;
  double ybar = 0;
  for (int x = 0; x < n; ++x) {
    xbar += x;
    ybar += ys[x];
  }
  xbar /= n;
  ybar /= n;
  double num = 0;
  double den = 0;
  for (int x = 0; x < n; ++x) {
    double dx = x - xbar;
    double dy = ys[x] - ybar;
    num += dx * dy;
    den += dx * dx;
  }
  return num / den;
}

void hsv2rgb(double h, double s, double v, int *red, int *grn, int *blu) {
  double i, f, p, q, t, r, g, b;
  int ii;
  if (s == 0.0) { r = g = b = v; } else {
    h = 6 * (h - floor(h));
    ii = i = floor(h);
    f = h - i;
    p = v * (1 - s);
    q = v * (1 - (s * f));
    t = v * (1 - (s * (1 - f)));
    switch(ii) {
      case 0: r = v; g = t; b = p; break;
      case 1: r = q; g = v; b = p; break;
      case 2: r = p; g = v; b = t; break;
      case 3: r = p; g = q; b = v; break;
      case 4: r = t; g = p; b = v; break;
      default:r = v; g = p; b = q; break;
    }
  }
  *red = fmin(fmax(255 * r + 0.5, 0), 255);
  *grn = fmin(fmax(255 * g + 0.5, 0), 255);
  *blu = fmin(fmax(255 * b + 0.5, 0), 255);
}

int main(int argc, char **argv) {
  if (! (argc > 2)) { return 1; }
  int mlevel = atoi(argv[1]);
  int jlevel = atoi(argv[2]);
  int msize = 1 << mlevel;
  int bytes = msize * msize/2 * (jlevel + 1) * sizeof(double);
  double *raw = malloc(bytes);
  if (! raw) { return 1; }
  char filename[1024];
  snprintf(filename, 1000, "j-dim-%02d-%02d.raw", mlevel, jlevel);
  FILE *f = fopen(filename, "rb");
  if (! f) { free(raw); return 1; }
  if (1 != fread(raw, bytes, 1, f)) { fclose(f); free(raw); return 1; }
  fclose(f);
  unsigned char *ppm = malloc(3 * msize * msize);
  for (int l0 = jlevel - 1; l0 >= 0; --l0) {
    for (int l1 = jlevel; l1 > l0; --l1) {
      #pragma omp parallel for
      for (int j = 0; j < msize / 2; ++j) {
        for (int i = 0; i < msize; ++i) {
          double dim = fabs(simple_linear_regression(&raw[(jlevel + 1) * (msize * j + i) + l0], 1 + l1 - l0));
          double hue = dim < 1
            ? -1.0/12.0 * (1 - dim) +  dim      *  1.0/ 6.0
            :  1.0/ 6.0 * (2 - dim) + (dim - 1) * 11.0/12.0;
          double sat = dim / 2;
          double val = 0 <= dim && dim <= 2;
          int red, grn, blu;
          hsv2rgb(hue, sat, val, &red, &grn, &blu);
          int k = 3 * (msize * j + i);
          ppm[k + 0] = red;
          ppm[k + 1] = grn;
          ppm[k + 2] = blu;
          k = 3 * ((msize - 1 - j) * msize + i);
          ppm[k + 0] = red;
          ppm[k + 1] = grn;
          ppm[k + 2] = blu;
        }
      }
      snprintf(filename, 1000, "j-dim-%02d-%02d-%02d-%02d.ppm", mlevel, jlevel, l0, l1);
      f = fopen(filename, "wb");
      fprintf(f, "P6\n%d %d\n255\n", msize, msize);
      fwrite(ppm, 3 * msize * msize, 1, f);
      fclose(f);
    }
  }
  free(ppm);
  free(raw);
  return 0;
}
