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

/* #define DEBUG */
#if defined(DEBUG)
#include "xmalloc.h"
#else
#define xmalloc(x, y) malloc(x)
#define xfree(x, y) free(x)
#define xrealloc(x, y, z) realloc(x, y)
#define xmallocdump()
#endif
/* for xmalloc.c */
#define IDRGB     1001
#define IDPALETTE 1002
#define IDBMP     1003

/*---------------------------------------------------------------------------*/
#define NBYTE  1
#define NWORD  2
#define NDWORD 4

typedef struct {
  unsigned long bfType1;
  unsigned long bfType2;
  unsigned long bfSize;
  unsigned long bfReserved1;
  unsigned long bfReserved2;
  unsigned long bfOffBits;
} BitmapFileHeader;

typedef struct {
  unsigned long biSize;
  long biWidth;
  long biHeight;
  unsigned long biPlanes;
  unsigned long biBitCount;
  unsigned long biCompression;
  unsigned long biSizeImage;
  long biXPixPerMeter;
  long biYPixPerMeter;
  unsigned long biClrUsed;
  unsigned long biClrImportant;
} BitmapInfoHeader;

void LittleEndianWrite(unsigned long *data, int size, FILE *fp) {
  unsigned char lsb;
  unsigned long msb;
  if (size == 0)
    return;
  lsb = (unsigned char)(*data & 0xff);
  fwrite(&lsb, 1, 1, fp);
  msb = *data >> 8;
  LittleEndianWrite(&msb, size - 1, fp);
}

long iabs(long n) {
  return (n > 0) ? n : -n;
}

void task_write_header(FILE *fp, BitmapFileHeader *bh) {
  assert(sizeof(unsigned long) >= 4);
  assert(sizeof(long) >= 4);
  LittleEndianWrite(&(bh->bfType1), NBYTE, fp);
  LittleEndianWrite(&(bh->bfType2), NBYTE, fp);
  LittleEndianWrite(&(bh->bfSize), NDWORD, fp);
  LittleEndianWrite(&(bh->bfReserved1), NWORD, fp);
  LittleEndianWrite(&(bh->bfReserved2), NWORD, fp);
  LittleEndianWrite(&(bh->bfOffBits), NDWORD, fp);
#if 0
  printf("write:bfType1: %c\n", (char)bh->bfType1);
  printf("write:bfType2: %c\n", (char)bh->bfType2);
  printf("write:bfSize: %lu\n", bh->bfSize);
  printf("write:bfReserved1: %lu\n", bh->bfReserved1);
  printf("write:bfReserved2: %lu\n", bh->bfReserved2);
  printf("write:bfOffBits: %lu\n", bh->bfOffBits);
#endif
}

void task_write_info(FILE *fp, BitmapInfoHeader *bi) {
  assert(sizeof(unsigned short) == 2);
  assert(sizeof(unsigned long) == 4);
  assert(sizeof(long) == 4);
  LittleEndianWrite(&(bi->biSize), NDWORD, fp);
  LittleEndianWrite((unsigned long *)&(bi->biWidth), NDWORD, fp);
  LittleEndianWrite((unsigned long *)&(bi->biHeight), NDWORD, fp);
  LittleEndianWrite(&(bi->biPlanes), NWORD, fp);
  LittleEndianWrite(&(bi->biBitCount), NWORD, fp);
  LittleEndianWrite(&(bi->biCompression), NDWORD, fp);
  LittleEndianWrite(&(bi->biSizeImage), NDWORD, fp);
  LittleEndianWrite((unsigned long *)&(bi->biXPixPerMeter), NDWORD, fp);
  LittleEndianWrite((unsigned long *)&(bi->biYPixPerMeter), NDWORD, fp);
  LittleEndianWrite(&(bi->biClrUsed), NDWORD, fp);
  LittleEndianWrite(&(bi->biClrImportant), NDWORD, fp);
#if 0
  printf("write:biSize: %lu\n", bi->biSize);
  printf("write:biWidth: %ld\n", bi->biWidth);
  printf("write:biHeight: %ld\n", bi->biHeight);
  printf("write:biPlanes: %lu\n", bi->biPlanes);
  printf("write:biBitcount: %lu\n", bi->biBitCount);
  printf("write:biCompression: %lu\n", bi->biCompression);
  printf("write:biSizeImage: %lu\n", bi->biSizeImage);
  printf("write:biXPixPerMeter %ld\n", bi->biXPixPerMeter);
  printf("write:biYPixPerMeter %ld\n", bi->biYPixPerMeter);
  printf("write:biClrUsed: %lu\n", bi->biClrUsed);
  printf("write:biClrImporant: %lu\n", bi->biClrImportant);
#endif
}

void task_write24(FILE *fp,
                  BitmapFileHeader *bh, BitmapInfoHeader *bi,
                  unsigned char *dataR,
                  unsigned char *dataG,
                  unsigned char *dataB) {
  int x, y;
  int c;
  unsigned char dummy = '\0';
  task_write_header(fp, bh);
  task_write_info(fp, bi);
  for (y = 0; y < iabs(bi->biHeight); y++) {
    c = 0;
    for (x = 0; x < iabs(bi->biWidth); x++) {
      fwrite(&dataB[y * iabs(bi->biWidth) + x], 1, 1, fp);
      fwrite(&dataG[y * iabs(bi->biWidth) + x], 1, 1, fp);
      fwrite(&dataR[y * iabs(bi->biWidth) + x], 1, 1, fp);
      c += 3;
    }
    while (c % 4 != 0) {
      fwrite(&dummy, 1, 1, fp);
      c++;
    }
  }
}
/*---------------------------------------------------------------------------*/
struct BMP24 {
  BitmapFileHeader bh;
  BitmapInfoHeader bi;
  unsigned char *dataR;
  unsigned char *dataG;
  unsigned char *dataB;
  unsigned char *dataPalette;
};

void BMP24_write(FILE *fp, struct BMP24 *bmp) {
  task_write24(fp, &bmp->bh, &bmp->bi,
               bmp->dataR, bmp->dataG, bmp->dataB);
}

void _BMP24_allocation(struct BMP24 **w_bmp, long width, long height) {
  int n;
  *w_bmp = xmalloc(sizeof(struct BMP24), IDBMP);
  if (*w_bmp == NULL) {
    fprintf(stderr, "error(bmp24)\n");
    return;
  }
  n = width * 3;
  if (n % 4 > 0)
    n += 4 - (n % 4);
  (*w_bmp)->bi.biSizeImage = n * height;
  (*w_bmp)->bi.biSize = 40;
  (*w_bmp)->bh.bfSize = (*w_bmp)->bi.biSizeImage + (*w_bmp)->bi.biSize + 14;

  (*w_bmp)->bh.bfType1 = 'B';
  (*w_bmp)->bh.bfType2 = 'M';
  (*w_bmp)->bh.bfReserved1 = 0;
  (*w_bmp)->bh.bfReserved2 = 0;
  (*w_bmp)->bh.bfOffBits = 54;

  (*w_bmp)->bi.biPlanes = 1;
  (*w_bmp)->bi.biBitCount = 24;
  (*w_bmp)->bi.biCompression = 0;
  (*w_bmp)->bi.biHeight = height;
  (*w_bmp)->bi.biWidth = width;  
  (*w_bmp)->bi.biClrUsed = 0;
  (*w_bmp)->bi.biClrImportant = 0;
  (*w_bmp)->bi.biXPixPerMeter = 30;
  (*w_bmp)->bi.biYPixPerMeter = 30;
  (*w_bmp)->dataR = (*w_bmp)->dataG = (*w_bmp)->dataB = NULL;
  (*w_bmp)->dataPalette = NULL;
  for (;;) {
    if (((*w_bmp)->dataR = xmalloc(width * height, IDRGB)) == 0)
      break;
    if (((*w_bmp)->dataG = xmalloc(width * height, IDRGB)) == 0)
      break;
    if (((*w_bmp)->dataB = xmalloc(width * height, IDRGB)) == 0)
      break;
    /* OK finished */
    return;
  }
  /* exception */
  xfree((*w_bmp)->dataR, IDRGB);
  xfree((*w_bmp)->dataG, IDRGB);  
  xfree((*w_bmp)->dataB, IDRGB);
  xfree(*w_bmp, IDBMP);
  *w_bmp = 0;
  return;
}

void BMP24_release(struct BMP24 *bmp) {
  if (bmp->dataR) xfree(bmp->dataR, IDRGB);
  if (bmp->dataG) xfree(bmp->dataG, IDRGB);
  if (bmp->dataB) xfree(bmp->dataB, IDRGB);
  if (bmp->dataPalette) xfree(bmp->dataPalette, IDPALETTE);
}

/*---------------------------------------------------------------------------*/
struct flist {
  double (*f)(double, double);
  double (*g)(double, double);
  double (*dfdx)(double, double);
  double (*dfdy)(double, double);
  double (*dgdx)(double, double);
  double (*dgdy)(double, double);
};

#define EPSILON 0.0000005
int newton(double x, double y, double *rx, double *ry, struct flist flist) {
  int rc;
  double h1, h2;
  double a, b, c, d, D;

  rc = 0;
  while (1) {
    a = flist.dfdx(x, y);
    b = flist.dfdy(x, y);
    c = flist.dgdx(x, y);
    d = flist.dgdy(x, y);
    D = a * d - b * c;
  
    if (fabs(D) <  EPSILON) {
      rc = -1;
      break;
    }

    h1 = - (d * flist.f(x, y) - b * flist.g(x, y)) / D;
    h2 = - ((-c) * flist.f(x, y) + a * flist.g(x, y)) / D;

    x = x + h1;
    y = y + h2;

    if (fabs(h1) < EPSILON && abs(h2) < EPSILON)
      break;
  }
  if (rc == 0) {
    *rx = x;
    *ry = y;
  } else {
    *rx = *ry = 0.0;
  }

  return rc;
}

/*---------------------------------------------------------------------------*/
struct convlist {
  double x;
  double y;
  unsigned char colorR;
  unsigned char colorG;
  unsigned char colorB;
};

#define ALPHA (1.0)
void task(FILE *fp, double x_ll, double x_ul, double y_ll, double y_ul, int k,
          struct flist flist, struct convlist clist[], int args) {
  double x, y;
  double res_x, res_y;
  int i, j, idx, r;
  unsigned char cr, cg, cb;

  struct BMP24 *bmp;
  bmp = 0;
  _BMP24_allocation(&bmp, k, k);

  for (i = 0; i < k; i++) {
    x = (x_ul - x_ll) * i / (double)k + x_ll;
    for (j = 0; j < k; j++) {
      y = (y_ul - y_ll) * j / (double)k + y_ll;

      cr = cg = cb = 0; idx = -1;
      if ((r = newton(x, y, &res_x, &res_y, flist)) == 0) {
        for (idx = 0; idx < args; idx++) {
          if ((fabs(res_x - clist[idx].x) < ALPHA * EPSILON) && (fabs(res_y - clist[idx].y) < ALPHA * EPSILON)) {
            printf("(1):(%d)(%d, %d, %d)\n", idx, cr, cg, cb);
            cr = clist[idx].colorR; cg = clist[idx].colorG; cb = clist[idx].colorB; 
            printf("(2):(%d)(%d, %d, %d)\n", idx, cr, cg, cb);
            break;
          }
        }
        if (idx == args)
          cr = cg = cb = 255;
      }
/*      printf("(%f, %f)->(%f, %f) ", x, y, res_x, res_y); printf(" : <%d>(%d, %d, %d)\n", idx, cr, cg, cb); */
      bmp->dataR[j * k + i] = cr;
      bmp->dataG[j * k + i] = cg;
      bmp->dataB[j * k + i] = cb;      
    }
  }
  BMP24_write(fp, bmp);
  BMP24_release(bmp);
  xfree(bmp, IDBMP);
}

/*----------------------------------------------------------------*/
#define X_LL (-0.5)
#define Y_LL (-0.5)
#define X_UL 0.5
#define Y_UL 0.5
#define K    1000

double f1(double x, double y) { return x * x + y * y - 8.0; }
double g1(double x, double y) { return x + y; }
double dfdx1(double x, double y) { return 2.0 * x; }
double dfdy1(double x, double y) { return 2.0 * y; }
double dgdx1(double x, double y) { return 1.0; }
double dgdy1(double x, double y) { return 1.0; }

double f2(double x, double y) { return x * x * x - 3.0 * x * y * y  - 8.0; }
double g2(double x, double y) { return x * x * x - 3.0 * x * x * y; }
double dfdx2(double x, double y) { return 3.0 * x * x - 3.0 * y * y; }
double dfdy2(double x, double y) { return -6.0 * x * y; }
double dgdx2(double x, double y) { return 3.0 * x * x - 6.0 * x * y; }
double dgdy2(double x, double y) { return -3.0 * x * x; }

#define FILENAME1 "g1.bmp"
#define FILENAME2 "g2.bmp"
int main() {
  FILE *fp;
  int n;
  struct flist flist;
  struct convlist clist[3];
#if 1
  if ((fp = fopen(FILENAME1, "wb")) != 0) {
    n = 2;
    flist.f = f1; flist.g = g1; flist.dfdx = dfdx1; flist.dfdy = dfdy1; flist.dgdx = dgdx1; flist.dgdy = dgdy1;
    clist[0].x = -2.0; clist[0].y = +2.0; clist[0].colorR = 255; clist[0].colorG = clist[0].colorB = 0;
    clist[1].x = +2.0; clist[1].y = -2.0; clist[1].colorR = 0; clist[1].colorG = 255; clist[1].colorB = 0;
    task(fp, X_LL, X_UL, Y_LL, Y_UL, K, flist, clist, n);
    fclose(fp);
  }
#endif
  if ((fp = fopen(FILENAME2, "wb")) != 0) {
    n = 3;
    flist.f = f2; flist.g = g2; flist.dfdx = dfdx2; flist.dfdy = dfdy2; flist.dgdx = dgdx2; flist.dgdy = dgdy2;
    clist[0].x = 2.289428; clist[0].y = +0.763143; clist[0].colorR = 0;   clist[0].colorG = 0;   clist[0].colorB = 255;
    clist[1].x = 2.394859; clist[1].y = -0.798286; clist[1].colorR = 255; clist[1].colorG = 0;   clist[1].colorB = 0;
    clist[2].x = 2.394859; clist[2].y = +0.798286; clist[2].colorR = 0;   clist[2].colorG = 255; clist[2].colorB = 0;
    task(fp, X_LL, X_UL, Y_LL, Y_UL, K, flist, clist, n);
    fclose(fp);
  }




  return 0;
}

/* end */
#if 0
参考文献
http://s...content-available-to-author-only...c.jp/suuchi-pdf/2007-04-11.pdf
#endif
