/*
 *  File:    WaterRetention3D.cpp
 *  Author:  S Harry White
 *  Created: 2011-03-01
 *  Updated: 2022-02-05
 *    Tidy code.
 *  Updated: 2023-02-22
 *    Improve openInput and openOutput.
 * Updated: 2023-03-28
 *   Fix store allocation. 
 *   Remove allocatedR=0, allocatedC=0 and allocatedQueueSize=0 from initGlobals().
 */

#include "stdafx.h"
#include <assert.h>
#include <conio.h>
#include <direct.h>
#include <errno.h>
#include <io.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <Windows.h>

const int startSize=28, maxRC=250000;
int R, C, // The order of the rectangle.
    N,    // R==C ? R : sqrt(RC)
    Rm1,  // R-1
    Cm1,  // C-1
    RC,   // R*C
    RCp1, // RC+1
    **xRectangle=NULL, **bRectangle=NULL, allocatedR, allocatedC,
    inRectNumber, smallestNumber, biggestNumber, allocatedQueueSize, qIndex;
#define Uint unsigned int
Uint rowConstant, // C*(RC+1)/2
     colConstant; // R*(RC+1)/2 for 1 base
const bool F=false, T=true; bool **isSpillWay=NULL, *numUsed=NULL;
struct t_Cell { int priority, row, col; }; t_Cell *Queue=NULL;
enum magicType {notMagic, notNormal, Normal};
typedef magicType (*t_mtcb)(const bool); t_mtcb isMagic;
//============================================== store =================================================

char *storeAllocFail="Storage allocation failed";
bool reportError(char *msg) { printf("\a\nError: %s.\n", msg); return F; }
//   -----------

void freeRectangle(int*** rectangle, const int size) {
//   -------------
  if (*rectangle!=NULL) {
    for(int i=0; i<size; i++) free((*rectangle)[i]); free(*rectangle); *rectangle=NULL;
  }
} // freeRectangle

void freeIsSpillWay(const int size) {
//   --------------
  if (isSpillWay!=NULL) {
    for(int i=0; i<size; i++) free(isSpillWay[i]); free(isSpillWay); isSpillWay=NULL;
  }
} // freeIsSpillWay

void freeUsed() { if (numUsed!=NULL) { free(numUsed); numUsed=NULL; } }
//   --------

void freeQueue() {
//   ---------
  if (Queue!=NULL) { free(Queue); Queue=NULL; allocatedQueueSize=0; }
}

void freeStore() {
//   ---------
  freeRectangle(&bRectangle, allocatedR); freeRectangle(&xRectangle, allocatedR);
  freeIsSpillWay(allocatedR); allocatedR=0; allocatedC=0; freeUsed(); freeQueue();
}

bool allocateRectangle(int*** rectangle, const int nR, const int nC) {
//   -----------------
  bool ok; *rectangle=(int**) malloc(nR*sizeof(int*)); ok=(*rectangle!=NULL);
  if (ok) {
    int numAllocated=0;
    for(int i=0; i<nR; i++) {
      int* p=(int*) malloc(nC*sizeof(int)); (*rectangle)[i]=p;if (p==NULL) { numAllocated=i; ok=F; break; }
    }
    if (!ok) freeRectangle(rectangle, numAllocated);
  }
  return ok;
} // allocateRectangle 

bool allocateIsSpillWay(const int nR, const int nC) {
//   ------------------{
  bool ok; isSpillWay=(bool**) malloc(nR*sizeof(bool*)); ok=(isSpillWay!=NULL);
  if (ok) {
    int numAllocated=0;
    for(int i=0; i<nR; i++) {
      bool *p=(bool*) malloc(nC*sizeof(bool)); isSpillWay[i]=p; if (p==NULL) { numAllocated=i; ok=F; break; }
    }
    if (!ok) freeIsSpillWay(numAllocated);
  }
  return ok;
} // allocateIsSpillWay 

bool allocateUsed(const int size) { return ( (numUsed=(bool*) malloc(size*sizeof(bool)))!=NULL); }
//   ------------

bool allocateQueue(const int size) {
//   -------------
  bool ok=T;
  if (allocatedQueueSize<size) {
	  freeQueue(); Queue=(t_Cell *) malloc(size*sizeof(t_Cell));if (ok=(Queue!=NULL)) allocatedQueueSize=size;
  }
  return ok;
} // allocateQueue

bool increaseQueue() {
//   -------------
  const int size=allocatedQueueSize+allocatedQueueSize; t_Cell *tmp=(t_Cell *) malloc(size*sizeof(t_Cell));
  if (tmp==NULL) { reportError(storeAllocFail); return F; } for (int i=0; i<qIndex; i++) tmp[i]=Queue[i];
  freeQueue(); Queue=tmp; allocatedQueueSize=size; return T;
} // increaseQueue;

bool allocateStore() {
//   -------------
  bool ok=T; const int nR=(R<startSize) ? startSize : R, nC=(C<startSize) ? startSize : C;
  if ((nR>allocatedR)|(nC>allocatedC)) {
    const int nRnC=nR*nC; freeStore();
    if (ok=allocateRectangle(&xRectangle, nR, nC)) if (ok=allocateRectangle(&bRectangle, nR, nC))
      if (ok=allocateIsSpillWay(nR, nC)) if (ok=allocateQueue(nRnC))
        if (ok=allocateUsed(nRnC+1)) { allocatedR=nR; allocatedC=nC; }
    if (!ok) { reportError(storeAllocFail); freeStore(); }
  }
  return ok;
} // allocateStore
//==================================================== input ================================================

const int bufSize=1024;
void clearLine(int c) { while (c!='\n') c=getchar(); }
//   ---------

bool getY() {
//   ----
  int c; do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n')); clearLine(c); return (c=='Y')|(c=='y');
} // getY

bool getYorOrder(int *n) { // 'y' or 'n' or the order
//   -----------
  bool result=F; int c; *n=0; do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n') );
  if ( (c=='Y')|(c=='y') ) result=T;
  else if ( (c!='N')&(c!='n') ) if ( ('1'<=c)&(c<='9') ) {
    int i=c-'0'; while ( ('0'<=(c=getchar()))&&(c<='9') ) i=i*10+c-'0'; *n=i; result=T;
  }   
  clearLine(c); return result;
} // getYorOrder

bool getInts(int *p, int *q, int c) {
//   -------
  bool ok=F; *p=*q=0; if (c<0) do { c=getchar(); } while ((c==' ')|(c=='\t'));
  if ( ('1'<=c)&(c<='9') ) {
    int i=c-'0'; while ( ('0'<=(c=getchar()))&&(c<='9') ) i=i*10+c-'0'; *p=i;

    if ((c==' ')|(c=='\t')) { do { c=getchar(); } while ((c==' ')|(c=='\t'));
      if ( ('1'<=c)&(c<='9') ) {
        int i=c-'0'; while ( ('0'<=(c=getchar()))&&(c<='9') ) i=i*10+c-'0'; *q=i;
      }
    }
    if (*q==0) *q=*p; ok=T;
  } 
  clearLine(c); if (!ok) return reportError("Invalid input"); return T;
} // getInts

bool getYorInts(int *p, int *q) {
//   ----------
  bool ok=F; int c; *p=-1; do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n') );
  if ( (c=='Y')|(c=='y') ) ok=T; else if ( (c!='N')&(c!='n') ) return getInts(p, q, c);  
  clearLine(c); return ok;
} // getYorInts

bool getFileName(char *buf, const int size) {
//   -----------
  int c, i=0; char *s=buf; do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n') ); *s=c;
  while (i++<size) if ( (*++s=getchar())=='\n') break;
  if (*s!='\n') { printf("\nFile name too long.\n"); clearLine(*s); return F; }
  *s='\0'; return T;
} // getFileName

void check_txt(char *buf) {
//   ---------
  char *s=buf; bool txt=F; while (*s++!='\0');
  while (--s!=buf) if (*s=='.') { txt=(*++s=='t')&&(*++s=='x')&&(*++s=='t')&&(*++s=='\0'); break; }
  if (!txt) strcat_s(buf, bufSize, ".txt");
} // check_txt

FILE *openInput(char buf[bufSize]) {
//    ---------
  char *rFname=NULL; FILE *rfp=NULL;
  do {
    printf("\nRectangles file name? ");
    if (getFileName(buf, bufSize-4)) { rFname=buf; break; }
    printf("\a\nCan't read the file name. Try again? y (yes) or n (no) "); if (!getY()) break;
  } while (T);
  if (rFname!=NULL) {
    check_txt(buf);   
    if (fopen_s(&rfp, buf, "r")!=0) {
      const int msgSize=bufSize+50; char msg[msgSize];
      sprintf_s(msg, msgSize, "\a\nCan't open for read %s", buf); perror(msg);
    }
  }
  return rfp;
} // openInput

bool inputRectangle(FILE *rfp) {
//   --------------
  bool zeroBase=F; smallestNumber=LONG_MAX; biggestNumber=LONG_MIN;
  for (int r=0; r<R; r++) for (int c=0; c<C; c++) {
	  int rv, tmp;
	  if ( (rv=fscanf_s(rfp, "%d", &tmp))!=1) {
      if ( (rv!=EOF)|(r!=0)|(c!=0) ) reportError("Reading rectangle from file"); return F;
   } else {
      if (tmp==0) zeroBase=T; if (tmp>biggestNumber) biggestNumber =tmp;
      if (tmp<smallestNumber) smallestNumber=tmp; xRectangle[r][c]=tmp;
    }
  }
  ++inRectNumber; magicType t=isMagic(zeroBase);
  if (t==notMagic) {
    printf("%s number %d is not magic.\n", (R==C) ? "Square" : "Rectangle", inRectNumber);
  } else if (t==notNormal) {
    printf("%s number %d is not normal magic.\n", (R==C) ? "Square" : "Rectangle", inRectNumber);
  }
  return T; 
} // inputRectangle
//================================================== output ==============================================

//FILE *Qfp=NULL;
//void printQueue() {
////   ----------
//  int i=0;
//  while (i<qIndex) { t_Cell *q=&Queue[i];
//    fprintf(Qfp,"priority %d row %d col %d\n",q->priority, q->row, q->col);	i++;
//  }
//  fprintf(Qfp,"\n");
//} // printQueue

const int outSize=bufSize+100;
void stripName(char *inFname, char *obuf) {
//   ---------
  char *s=inFname; while (*s++!='\0');
  while (--s!=inFname) // Remove .txt and any directory path.
    if (*s=='.') *s='\0';  else if ((*s=='\\')|(*s=='/')) { ++s; break; }
  strcpy_s(obuf, outSize, s); 
} // stripName

bool openDir(char *dir) {
//   -------
  int sub=0; char baseName[bufSize]; 
  sprintf_s(baseName, bufSize, "Water%dx%d_3D", R, C); strcpy_s(dir, bufSize, baseName);
  do {
    if (_mkdir(dir)!=-1) break;
    if (errno!=EEXIST) {
      sprintf_s(baseName, bufSize, "\a\nCan't make folder %s", dir); perror(baseName); return F;
    }
    sprintf_s(dir, bufSize, "%s_%d", baseName, ++sub);
  } while (T);
  printf("Output file(s) are in folder %s\n", dir); return T;
} // openDir

FILE *openOutput(char *dir, char *inFname) {
//    ----------
  FILE *wfp=NULL; int sub=0; const int baseSize=bufSize+50; char baseName[baseSize], buf[outSize];
  sprintf_s(baseName, baseSize, "%s\\%s_%d", dir, inFname, inRectNumber);
  sprintf_s(buf, outSize, "%s.svg", baseName);
  do {
    if (_access_s(buf, 00)==ENOENT) break; sprintf_s(buf, outSize, "%s%d.svg", baseName, ++sub);
  } while (T);
  if (fopen_s(&wfp, buf, "w")!=0) {
    char msg[outSize+50];
    sprintf_s(msg, outSize+50, "\a\nCan't open for write %s", buf); perror(msg);
  }
  return wfp;
} // openOutput
//================================================== check =================================================

magicType isMagicSq(const bool zeroBase) { // R==C
//        ---------
  const int min=zeroBase ? 0 : 1, max=RC+min-1; const Uint magicSum=zeroBase ? rowConstant-R : rowConstant;
  Uint chkSum=0, sumX, sumY, sumXY=0, sumYX=0; for (int i=min; i<=max; i++) numUsed[i]=F;
  for (int r=0; r<R; r++) {
    sumX=0; sumY=0;
    for (int c=0; c<R; c++) {
	    const int tmp=xRectangle[r][c];
	    if ((min<=tmp)&(tmp<=max)) numUsed[tmp]=T; sumX+=tmp; sumY+=xRectangle[c][r];
    }
	  if (r==0) chkSum=sumX; if ((sumX!=chkSum)|(sumY!=chkSum)) return notMagic;
    sumXY+=xRectangle[r][Rm1-r]; sumYX+=xRectangle[r][r];
  }
  if ((sumXY!=chkSum)|(sumYX!=chkSum)) return notMagic; if (chkSum!=magicSum) return notNormal;
  for (int i=min; i<=max; i++) if (!numUsed[i]) return notNormal; return Normal;
} // isMagicSq

magicType isMagicRect(const bool zeroBase) {
//        -----------
  const int min=zeroBase ? 0 : 1, max=RC+min-1; Uint chkSumR, chkSumC;
  const Uint rowSum=zeroBase ? rowConstant-C : rowConstant,
             colSum=zeroBase ? colConstant-R : colConstant; for (int i=min; i<=max; i++) numUsed[i]=F;
  for (int r=0; r<R; r++) {
    Uint sum=0; for (int c=0; c<C; c++) {
	    const int tmp=xRectangle[r][c]; if ((min<=tmp)&(tmp<=max)) numUsed[tmp]=T; sum+=tmp;
    }
	  if (r==0) chkSumR=sum; else if (sum!=chkSumR) return notMagic;
  }
  for (int c=0; c<C; c++) {
    Uint sum=0; for (int r=0; r<R; r++) sum+=xRectangle[r][c];
	  if (c==0) chkSumC=sum; else if (sum!=chkSumC) return notMagic;
  }
  if ((chkSumR!=rowSum)|(chkSumC!=colSum)) return notNormal;
  for (int i=min; i<=max; i++) if (!numUsed[i]) return notNormal; return Normal;
} // isMagicRect
//============================================== make ============================================

void initBounds(const int maxNumber) {
//   ----------
  const int mR=Rm1, mC=Cm1; int **x=xRectangle;
  for (int r=0; r<R; r++) { bRectangle[r][0]=x[r][0]; bRectangle[r][mC]=x[r][mC]; }
  for (int c=1; c<mC; c++) { bRectangle[0][c]=x[0][c]; bRectangle[mR][c]=x[mR][c]; }
  for (int r=1; r<mR; r++) for (int c=1; c<mC; c++) bRectangle[r][c]=maxNumber; qIndex=0;
} // initBounds

void pushQueue(const int priority, const int row, const int col) {
//   ---------
  t_Cell cell={ priority, row, col }; Queue[qIndex++]=cell;
} // pushQueue

bool insertQueue(const int priority, const int row, const int col) {
//   -----------
  if ((qIndex>=allocatedQueueSize)&&!increaseQueue()) return F;
  t_Cell cell; cell.priority=priority; cell.row=row; cell.col=col; int i=qIndex-1;
  while (Queue[i].priority<priority) { Queue[i+1]=Queue[i]; if (--i<0) break; }
  Queue[++i]=cell; ++qIndex; /*printQueue();*/ return T;
} // insertQueue

void popQueue(t_Cell *cell) { *cell=Queue[--qIndex]; }
//   --------

bool queueBorder() {
//   -----------
  const int mR=Rm1, mC=Cm1; pushQueue(bRectangle[1][0], 1, 0);
  if (!insertQueue(bRectangle[1][mC], 1, mC)) return F;
  for (int r=2; r<mR; r++) {
	  if (!insertQueue(bRectangle[r][0], r, 0)) return F; if (!insertQueue(bRectangle[r][mC], r, mC)) return F;
  }
  for (int c=1; c<mC; c++) {
	  if (!insertQueue(bRectangle[0][c], 0, c)) return F; if (!insertQueue(bRectangle[mR][c], mR, c)) return F;
  }
  return T;
} // queueBorder

bool computeBounds(const int p, const int r, const int c) {
//   -------------
  if ( ( (0<r)&(r<Rm1) )&( (0<c)&(c<Cm1) ) ) {
    int *bp=&bRectangle[r][c]; const int tmp=max(xRectangle[r][c],p);
    if (tmp<*bp) { *bp=tmp; if (!insertQueue(tmp, r, c)) return F; }
  }
  return T;
} // computeBounds

void computeCapacity() {
//   ---------------
  const Uint b=1000000000; Uint count=0, unitsB=0, units=0;
  for (int r=0; r<R; r++) for (int c=0; c<C; c++) {
	  const int delta=bRectangle[r][c]-xRectangle[r][c];
    if (delta!=0) { ++count; units+=delta; if (units>=b) { ++unitsB; units-=b; }}
 	}
  printf("%s number %d: units retained ", (R==C) ? "Square" : "Rectangle", inRectNumber);
  if ((unitsB==0)&(units==0)) printf("0\n\n");
  else {
    if (unitsB==0) printf("%u", units); else printf("%u%09u", unitsB, units);
    printf(", cells retaining %u\n\n", count);
  }
} // computeCapacity

const int di[]={1, 0, -1, 0}, dj[]={0, 1, 0, -1};
void getSpillWays() {
//   ------------
  for (int j=0; j<R; ++j) for (int i=0; i<C; ++i) isSpillWay[j][i]=F;
  int **x=xRectangle, **b=bRectangle;
  for (int j=1; j<Rm1; ++j) for (int i=1; i<Cm1; ++i) {
    if (b[j][i]>x[j][i]) for (int d=0; d<4; ++d) {
      const int li=di[d]+i, lj=dj[d]+j; if (x[lj][li]==b[j][i]) isSpillWay[lj][li]=T;
    }
  }
} // getSpillWays

/*
 *  Based on an algorithm of Gareth McCaughan supplied by Craig Knecht.
 */
bool getWater(const int base1factor) {
//   --------
//fopen_s(&Qfp, "C:\\QueueTrace.txt", "w");
  for (int j=0; j<R; ++j) for (int i=0; i<C; ++i) xRectangle[j][i]-=base1factor;
  initBounds(biggestNumber-base1factor); if (!queueBorder()) return F;
  while (qIndex!=0) {
  //printQueue();
	  t_Cell cell; popQueue(&cell); const int p=cell.priority, r=cell.row, c=cell.col;
	  if (!computeBounds(p, r-1, c)) return F; if (!computeBounds(p, r+1, c)) return F;
	  if (!computeBounds(p, r, c-1)) return F; if (!computeBounds(p, r, c+1)) return F;
  }
  computeCapacity(); getSpillWays(); /*fclose(Qfp);*/ return T;
} // getWater

double dim3D(const int n) {
//     -----
  return n==1 ? 200. : n<20 ? 300.0*log((double)n) : n<32 ? 42.0*n : n<100 ? 45.0*n : 50.0*n;
} // dim3D

int getMinMax(int *minDepth, int *maxDepth) {
//  ---------
  int **x=xRectangle, **b=bRectangle, maxStep=0, min=LONG_MAX, max=0;
  for (int j=0; j<R; ++j) for (int i=0; i<C; ++i) { const int d=b[j][i]-x[j][i]; if (min>d) min=d; if (max<d) max=d; }
  *minDepth=min; *maxDepth=max;
  for (int j=0; j<R; ++j) for (int i=0; i<Cm1; ++i) { const int d=x[j][i+1]-x[j][i]; if (maxStep<d) maxStep=d; }
  for (int j=0; j<C; ++j) for (int i=0; i<Rm1; ++i) { const int d=x[i+1][j]-x[i][j]; if (maxStep<d) maxStep=d; }
  return maxStep;
} // getMinMax

bool writeHead(FILE *sfp, const double dimR, const double dimC) {
//   ------------
  return
    fprintf(sfp,
      "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<svg\n"
      "xmlns:svg=\"http://www.w3.org/2000/svg\"\n"
      "xmlns=\"http://www.w3.org/2000/svg\"\n"
      "xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n"
      "version=\"1.0\"\nwidth=\"%g\"\nheight=\"%g\"\nid=\"magic_s\"\n>\n", dimC, dimR)>0;
} // writeHead

const char black[]="000000", white[]="ffffff";
void getColors
//   ---------
       (const int j, const int i, const int minDepth, const int maxDepth,
       int *r, int *g, int *b, const char **textColor) {
  if (bRectangle[j][i]==xRectangle[j][i]) {
    *textColor=black;
    if (isSpillWay[j][i]) { /* yellow */ *r=224; *g=224; *b=0; }
    else { // green
      int xji=xRectangle[j][i]; if (xji>RC) xji=RC; *r=0; *g=128+xji*127/RC; *b=0; //*r=0; *g=255; *b=0;
    }
  } else { // blue
    const int d=bRectangle[j][i]-xRectangle[j][i]; *r=0; *g=0;
    *b=255-(d-minDepth)*128/(maxDepth-minDepth); *textColor=white;
  }
} // getColors

/*
 *  Adapted from code written in 2010 by Claudio Rocchini supplied by Craig Knecht.
 *
 *  Changes:
 *    . code the water retention calculations using an algorithm
 *      of Gareth McCaughan, (handles repeated rectangle numbers)
 *    . normalize the cell numbers wrt the smallest number
 *    . adjust the green color calculation for large relative numbers
 */
bool drawRetained(FILE *sfp, bool *writeError) {
//   ------------
  const int n=min(R,C), base1Factor=smallestNumber-1; const bool view3D=n>1; int **x=xRectangle;
  if (!getWater(base1Factor)) { *writeError=F; return F; }
  int minDepth=RC, maxDepth=0; const int maxStep=getMinMax(&minDepth, &maxDepth);
  const double dim=dim3D(n), /* drawing size */ dimR=(R<=C) ? dim : dim*R/C, dimC=(C<=R) ? dim : dim*C/R;
  if (!writeHead(sfp, dimR, dimC)) return F;
  const double B=64, /* white border */ S=(dim-B*2.)/(n==1 ? 2 : n), /* tile size */
               fontSize=S/3., DZ=view3D ? (S/3.)/maxStep : 0.; /* deltaz */ *writeError=T;
  for (int j=0; j<R; ++j) for (int i=0; i<C; ++i) {	  
   	int red, green, blue; const char *textColor;
    getColors(j, i, minDepth, maxDepth, &red, &green, &blue, &textColor); const double z=DZ*(x[j][i]-1);
	  if (fprintf(sfp,
		      "<path d=\"M %5.1lf,%5.1lf L %5.1lf,%5.1lf L %5.1lf,%5.1lf L %5.1lf,%5.1lf Z\" "
		      "style=\"fill:#%02X%02X%02X;stroke:#000000;stroke-width:1;stroke-opacity:1\" />\n",
		      B+(i+0)*S-z, B+(j+0)*S-z, B+(i+1)*S-z, B+(j+0)*S-z, B+(i+1)*S-z, B+(j+1)*S-z,
		      B+(i+0)*S-z, B+(j+1)*S-z, red, green, blue)<0) return F;
    const int xji= x[j][i]+base1Factor;
    if (fprintf(sfp,
		      "<text x=\"%5.1f\" y=\"%5.1f\" font-size=\"%gpx\" "
		      "text-anchor=\"middle\" fill=\"#%s\">%d</text>\n",
	        B+(i+0.5)*S-z, B+(j+0.5)*S-z+fontSize/2, fontSize, textColor, xji)<0) return F;
    const double zr=DZ*(i==Cm1 ? 0 : x[j][i+1]-1);
    if (view3D&(zr<z))
      if (fprintf(sfp,
	        "<path d=\"M %5.1f,%5.1f L %5.1f,%5.1f L %5.1f,%5.1f L %5.1f,%5.1f Z\" "
	        "style=\"fill:#808080;stroke:#000000;stroke-width:1;stroke-opacity:1\" />\n",
	        B+(i+1)*S-z,  B+(j+0)*S-z, B+(i+1)*S-zr, B+(j+0)*S-zr,
	        B+(i+1)*S-zr, B+(j+1)*S-zr, B+(i+1)*S-z,  B+(j+1)*S-z)<0) return F;
    const double zb=DZ*(j==Rm1 ? 0 : x[j+1][i]-1);
	   if (view3D&(zb<z))
	     if (fprintf(sfp,
			     "<path d=\"M %5.1f,%5.1f L %5.1f,%5.1f L %5.1f,%5.1f L %5.1f,%5.1f Z\" "
			     "style=\"fill:#404040;stroke:#000000;stroke-width:1;stroke-opacity:1\" />\n",
			     B+(i+1)*S-z,  B+(j+1)*S-z, B+(i+1)*S-zb, B+(j+1)*S-zb,
			     B+(i+0)*S-zb, B+(j+1)*S-zb, B+(i+0)*S-z,  B+(j+1)*S-z)<0) return F;
    if (view3D&(x[j][i]<bRectangle[j][i])) {
      const double z=DZ*(bRectangle[j][i]-1);
	       if (fprintf(sfp,
			     "<path d=\"M %5.1f,%5.1f L %5.1f,%5.1f L %5.1f,%5.1f L %5.1f,%5.1f Z\" "
			     "style=\"fill:#0080FF;stroke:none;fill-opacity:0.4\" />\n",
			     B+(i+0)*S-z, B+(j+0)*S-z, B+(i+1)*S-z, B+(j+0)*S-z,
			     B+(i+1)*S-z, B+(j+1)*S-z, B+(i+0)*S-z, B+(j+1)*S-z)<0) return F;
	   }
  }
  *writeError=F; return fprintf(sfp, "</svg>\n")>0;
} // drawRetained

bool make3D(FILE *sfp, bool *writeError) {
//   ------
  if (drawRetained(sfp, writeError)) return T;
  if (*writeError) reportError("problem writing svg file"); return F;
} // make3D

bool make3Drectangle(char *dir, char *inFname, bool *writeError) {
//   ---------------
  FILE *wfp=openOutput(dir, inFname); if (wfp==NULL) return F;
  const bool ok=make3D(wfp, writeError); fclose(wfp); return ok;
} // make3Drectangle
//==================================================== main ===============================================

bool checkSize() {
//   ---------
  if (R*C>maxRC) {
    char msg[100]; sprintf_s(msg, 100, "Order limit RxC is set at %d", maxRC); reportError(msg); return F;
  } return T;
} // checkSize

void reportElapsedTime(time_t startTime) {
//   -----------------
  const int et=(int)difftime(time(NULL), startTime);
  if (et>0) printf("\nelapsed time %d:%02d:%02d\n", et/3600, et%3600/60, et%60);
} // reportElapsedTime

void outputLocalTime() {
//   ---------------
  time_t startTime=time(NULL); tm local; localtime_s(&local, &startTime); char dateTime[100];
  size_t slen=strftime(dateTime, 100, "%A %Y-%m-%d %X %Z\n\n\0", &local); printf(dateTime);
} // outputLocalTime

bool doAnother(bool *inputOrder, const bool writeError) {
//   ---------
  if (writeError) return F; printf("\nContinue? input y (yes) or n (no) or the order: ");
  if (getYorInts(&R, &C)) { *inputOrder=(R<0); return T; } else { freeStore(); return F; }
} // doAnother

void initGlobals() {
//   -----------
  Rm1=R-1; Cm1=C-1; RC=R*C; N=R==C ? R : (int)sqrt((double)RC);
  const int RCp1=RC+1; rowConstant=C&1 ? RCp1/2*C : C/2*RCp1; colConstant=R&1 ? RCp1/2*R : R/2*RCp1; 
  qIndex=0; isMagic=R==C ? isMagicSq : isMagicRect;
} // initGlobals

int main() {
//  -----
  outputLocalTime(); bool another=T, inputOrder=T, ok=F; allocatedR=0; allocatedC=0; allocatedQueueSize=0;
  do { // for each input file
    bool writeError=F;
    if (inputOrder) { printf("\nOrder? "); if (!getInts(&R, &C, -1)) break;}
    if (checkSize()) {
      initGlobals();
      if (allocateStore()) {
        char dir[bufSize];
        if (openDir(dir)) {
          char ibuf[bufSize], obuf[bufSize]; FILE *rfp=openInput(ibuf); stripName(ibuf, obuf);
          if (rfp!=NULL) {
            time_t startTime=time(NULL); inRectNumber=0;
            while (inputRectangle(rfp)) { if (!make3Drectangle(dir, obuf, &writeError)) break; }
            if (!writeError) ok=T; fclose(rfp); reportElapsedTime(startTime);
          } // (rfp!=NULL
        } // if (openDir
      } // (allocateStore
    } // (checkSize
    another=doAnother(&inputOrder, writeError);
  } while (another);
  printf("\nPress a key to close the console");
  while (!_kbhit()) Sleep(250); return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main