/*
 * File:    WaterRetentionSort.cpp
 * Author:  S Harry White
 * Created: 2011-08-14
 * Updated: 2020-10-22
 *   Add support of non-square rectangles.
 * Updated: 2021-05-21
 *   Improve store allocation.
 * Updated: 2022-02-05
 ^   Tidy code.
 * Updated: 2023-01-24
 *   Open output file in the current folder,
 *   Call caseError for switch default.
 *   Change fscanf int conversion specifications %ld to %d.
 *  Updated: 2023-02-07
 *    Change to compile with g++ for macOS.
 */

//#include <ctype.h>
#include <curses.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
//#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
//#include <sys/types.h>
#include <time.h>
#include <unistd.h>

#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)>(b))?(a):(b))
#define Sbyte  signed   char
#define Ubyte  unsigned char
#define Sshort signed   short
#define Ushort unsigned short
#define Uint   unsigned int

const bool F=false, T=true; bool signedNumbers, reportAlloc, bell;
const int byteBytes=1,
          shortBytes=sizeof(short),
          intBytes=sizeof(int),
          intShorts=sizeof(int)/sizeof(short),
          startNumBlocks=1,
          bufSize=1024,
          sortDown=1, sortUp=2,
          pointerSize=sizeof(void *);
int R, C,     // rectangle order: rows, columns
    Rm1, Cm1, // R-1, C-1
    RC,       // R*C
    dori,     // sort increasing or decreasing
    smallestRead, biggestRead,
    numberBytes, // 1, 2 or 4 byte integer for square numbers
    numBlocksAllocated, maxBlocksIncr, squaresPerBlock, squareSize, // RC*numberBytes
    numSquaresAllocated, sIndex, uIndex, allocatedQueueSize, qIndex,
    **xSquare=NULL, **water=NULL, *uniqueSquares=NULL;
const Uint oneB=1000000000, oneM=1000000, oneT=1000, maxSquares=UINT_MAX/pointerSize;
void **blocks=NULL, **squares=NULL; struct t_Cell { int priority, row, col; }; t_Cell *Queue=NULL;
const char *nameSq="squares", *nameRect="rectangles", *nameSR, *sortName[]={ NULL, "decreasing", "increasing", };

void caseError(const char *fn, const int cs) { printf("\aProgram error: function %s, case %d\n", fn, cs); }
//   ---------

void initGlobals(const int numBytes, const bool signedNums) {
//   -----------
  Rm1=R-1; Cm1=C-1; RC=R*C; reportAlloc=F; bell=T;dori=sortDown; nameSR=R==C?nameSq:nameRect;
  numBlocksAllocated=0; numSquaresAllocated=0; sIndex=0; uIndex=0; allocatedQueueSize=0; qIndex=0; 
  smallestRead=INT_MAX; biggestRead=INT_MIN; signedNumbers=signedNums;
  numberBytes=(numBytes==0) ? RC<=UCHAR_MAX  ? byteBytes
                            : RC<=USHRT_MAX  ? shortBytes : intBytes : numBytes;
  squareSize=intBytes+RC*numberBytes;
  const int OneHundredMillion=100000000, FiftyThousand=50000; squaresPerBlock=OneHundredMillion/squareSize;
  if (squaresPerBlock>FiftyThousand) squaresPerBlock=FiftyThousand;
  else if (squaresPerBlock<1) squaresPerBlock=1;
  maxBlocksIncr=oneB/(pointerSize+squaresPerBlock*(squareSize+pointerSize));
} // initGlobals
//=================================================== store ======================================================

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

void freeStore(const int allocated) {
//   ---------
  if (blocks!=NULL) {
    if (allocated!=0) { printf(".. freeing %s\n", nameSR);
      for (int i=0; i<allocated; i++) free(blocks[i]);
      if (squares!=NULL) { free(squares); squares=NULL; numSquaresAllocated=0; }
    }
    free(blocks); blocks=NULL;
  }
  numBlocksAllocated=0;
} // freeStore

bool allocateStore(int numNew) {
//   -------------
  bool ok=F; const int numOld=numBlocksAllocated; union ovly { Uint i; void *p; };
  if ((numNew-numOld)>maxBlocksIncr) numNew=numOld+maxBlocksIncr;
  const Uint numNewSquares=numNew*squaresPerBlock; // Uint for malloc(numNewSquares*pointerSize)
  if (numNewSquares<=maxSquares) {
    if (reportAlloc) {
      Uint nsq=numNewSquares, msq=nsq/oneM, tsq=nsq%oneM/oneT, rsq=nsq%oneT; printf("    .. increasing squares to ");
      if (msq==0) if (tsq==0) printf("%11d\n", rsq); else printf("%7d,%03d\n", tsq, rsq);
      else printf("%3d,%03d,%03d\n", msq, tsq, rsq);
    } else reportAlloc=T;
    void **tmp=(void **)malloc(numNew*pointerSize);
    if ((ok=(tmp!=NULL))) {
      int allocated=numNew; for (int i=0; i<numOld; i++) tmp[i]=blocks[i];
      for(int i=numOld; i<numNew; i++) {
        void *p=malloc(squaresPerBlock*squareSize); if (p==NULL) { ok=F; allocated=i; break; } tmp[i]=p;
      }
	    freeStore(0); blocks=tmp;
	    if (ok) {
        tmp=(void **)malloc(numNewSquares*pointerSize);
        if ((ok=(tmp!=NULL))) {
          for (int i=0; i<sIndex; i++) tmp[i]=squares[i]; free(squares); squares=tmp; int j=sIndex;
          for (int i=numOld; i<numNew; i++) {
            int k=0; void *p=blocks[i];
            while (k++<squaresPerBlock) { squares[j++]=p; ovly ip; ip.p=p; ip.i+=squareSize; p=ip.p; }
          }
	        numBlocksAllocated=numNew; numSquaresAllocated=numNewSquares;
        }
      } else freeStore(allocated);
    }
  }
  if (!ok) reportError(storeAllocFail);
  return ok;
} // allocateStore

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

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

void freeStoreWR() {
//   -----------
  freeSquare(&water, R); freeSquare(&xSquare, R); freeQueue();
  if (uniqueSquares!=NULL) { free(uniqueSquares); uniqueSquares=NULL; }
} // freeStoreWR

bool allocateSquare(int*** square) {
//   --------------
  bool ok; *square=(int**) malloc(R*sizeof(int*)); ok=(*square!=NULL);
  if (ok) {
    int numAllocated=0;
    for(int i=0; i<R; i++) {
      int *p=(int*) malloc(C*sizeof(int)); (*square)[i]=p; if (p==NULL) { numAllocated=i; ok=F; break; }
    }
    if (!ok) freeSquare(square, numAllocated);
  }
  return ok;
} // allocateSquare 

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) return reportError(storeAllocFail); for (int i=0; i<qIndex; i++) tmp[i]=Queue[i];
  freeQueue(); Queue=tmp; allocatedQueueSize=size; return T;
} // increaseQueue;

bool allocateStoreWR() {
//   ---------------
  bool ok=T; if ((ok=allocateSquare(&xSquare))) if ((ok=allocateSquare(&water))) ok=allocateQueue(RC);
  if (!ok) freeStoreWR(); return ok;
} // allocateStoreWR
//================================================ input =====================================================

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

int getInt() { int n=0; scanf("%d", &n); clearLine(getchar()); return n; }
//  -------

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 adjustName(char *buf) {
//   ----------
  char *s=buf; bool txt=F;
  if ((*s!='.')&(*s!='/')) { // Assume in current folder.
    char tmp[bufSize]; snprintf(tmp, bufSize, "./%s", buf); strcpy(buf, tmp);
  }
  if (access(buf, R_OK)!=0) {
    while (*s++!='\0')
      ; --s;  if ((strlen(buf)>6)&&(*s--=='t')&&(*s--=='x')&&(*s--=='t')&&(*s=='.')) txt=T;
    if (!txt) strcat(buf, ".txt"); // no .txt entered, add it
  }
} // adjustName

FILE *openInput(char buf[bufSize]) {
//    ---------
  char *rFname=NULL; FILE *rfp=NULL;
  do {
    printf("\nEnter the name of the %s file: ", nameSR);
    if (getFileName(buf, bufSize-6)) { rFname=buf; break; }
    else { printf("\a\nCan't read the file name. Try again? y (yes) or n (no) "); if (!getY()) break; }
  } while (T);
  if (rFname!=NULL) {
    adjustName(buf);
    if ((rfp=fopen(buf, "r"))==NULL) {
      const int msgSize=bufSize+50; char msg[msgSize];
      snprintf(msg, msgSize, "\a\nCan't open for read %s", buf); perror(msg);
    }
  }
  return rfp;
} // openInput

enum readStatus { readAllocFail, readError, readOORM, readOORP, readOK };
const char *readErrorMsg="\a\nError reading from file.\n";
readStatus readSquaresSbyte(FILE *rfp, const int incr, const bool unique) {
//         ----------------
  int count=0;
  do {
    if ((sIndex==numSquaresAllocated)&&
        (!allocateStore(numBlocksAllocated+numBlocksAllocated))) return readAllocFail;
    int rv=0; Sbyte *p=(Sbyte *)squares[sIndex]; *(int*)(p)=sIndex; p+=intBytes; void *startp=p;
    if (unique) { if ((sIndex==uIndex)||(count++==uniqueSquares[sIndex])) ++sIndex; } else ++sIndex;
    if (incr==0) {
      for (int i=0; i<RC; i++) { int v; rv=fscanf(rfp, "%d", &v);
        if (rv!=1) { if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf("%s",readErrorMsg); return readError; }}
        if (v<smallestRead) if ((smallestRead=v)<SCHAR_MIN) return readOORM; 
        if (v>biggestRead) if ((biggestRead=v)>SCHAR_MAX) return readOORP; *p++=v;
      }
    } else { int count=0, v[7];
      while (count<RC) {
        switch (incr) {
          case 7:
            if ((rv=fscanf(rfp, "%d %d %d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6]))==incr) 
              for (int a=0; a<=6; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SCHAR_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SCHAR_MAX) return readOORP; p[a]=x;
              } break;
          case 6:
            if ((rv=fscanf(rfp, "%d %d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]))==incr)
              for (int a=0; a<=5; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SCHAR_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SCHAR_MAX) return readOORP; p[a]=x;
              } break;
          case 5:
            if ((rv=fscanf(rfp, "%d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4]))==incr)
              for (int a=0; a<=4; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SCHAR_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SCHAR_MAX) return readOORP; p[a]=x;
              } break;
          case 4:
            if ((rv=fscanf(rfp, "%d %d %d %d", &v[0], &v[1], &v[2], &v[3]))==incr)
              for (int a=0; a<=3; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SCHAR_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SCHAR_MAX) return readOORP; p[a]=x;
              } break;
          case 3:
            if ((rv=fscanf(rfp, "%d %d %d", &v[0], &v[1], &v[2]))==incr)
              for (int a=0; a<=2; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SCHAR_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SCHAR_MAX) return readOORP; p[a]=x;
              } break;
          case 2:
            if ((rv=fscanf(rfp, "%d %d", &v[0], &v[1]))==incr)
              for (int a=0; a<=1; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SCHAR_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SCHAR_MAX) return readOORP; p[a]=x;
              } break;
          default: caseError("readSquaresSbyte", incr); return readError;
        }
        if (rv!=incr) { if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf("%s",readErrorMsg); return readError; }} count+=incr; p+=incr;
      } 
    }
  } while (T); return readOK;
} // readSquaresSbyte

readStatus readSquaresUbyte(FILE *rfp, const int incr, const bool unique) {
//         ----------------
  int count=0;
  do {
    if ((sIndex==numSquaresAllocated)&&
       (!allocateStore(numBlocksAllocated+numBlocksAllocated))) return readAllocFail;
    int rv=0; Ubyte *p=(Ubyte *)squares[sIndex]; *(int*)(p)=sIndex; p+=intBytes; void *startp=p;
    if (unique) { if ((sIndex==uIndex)||(count++==uniqueSquares[sIndex])) ++sIndex; } else ++sIndex;
    if (incr==0) {
      for (int i=0; i<RC; i++) { int v; rv=fscanf(rfp, "%d", &v);
        if (rv!=1) { if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf("%s",readErrorMsg); return readError; }}
        if (v<smallestRead) if ((smallestRead=v)<0) return readOORM; 
        if (v>biggestRead) if ((biggestRead=v)>UCHAR_MAX) return readOORP; *p++=v;
      }
    } else { int count=0, v[7];
      while (count<RC) {
        switch (incr) {
          case 7:
           if ((rv=fscanf(rfp, "%d %d %d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6]))==incr) 
              for (int a=0; a<=6; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>UCHAR_MAX) return readOORP; p[a]=x;
              } break;
          case 6:
            if ((rv=fscanf(rfp, "%d %d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]))==incr)
              for (int a=0; a<=5; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>UCHAR_MAX) return readOORP;  p[a]=x;
              } break;
          case 5:
            if ((rv=fscanf(rfp, "%d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4]))==incr)
              for (int a=0; a<=4; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>UCHAR_MAX) return readOORP; p[a]=x;
              } break;
          case 4:
            if ((rv=fscanf(rfp, "%d %d %d %d", &v[0], &v[1], &v[2], &v[3]))==incr)
              for (int a=0; a<=3; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>UCHAR_MAX) return readOORP; p[a]=x;
              } break;
          case 3:
            if ((rv=fscanf(rfp, "%d %d %d", &v[0], &v[1], &v[2]))==incr)
              for (int a=0; a<=2; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>UCHAR_MAX) return readOORP; p[a]=x;
              } break;
          case 2:
            if ((rv=fscanf(rfp, "%d %d", &v[0], &v[1]))==incr)
              for (int a=0; a<=1; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>UCHAR_MAX) return readOORP; p[a]=x;
              } break;
          default: caseError("readSquaresUbyte", incr); return readError;
        }
        if (rv!=incr) { if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf("%s",readErrorMsg); return readError; }} count+=incr; p+=incr;
      } 
    }
  } while (T); return readOK;
} // readSquaresUbyte

readStatus readSquaresSshort(FILE *rfp, const int incr, const bool unique) {
//         -----------------
  int count=0;
  do {
    if ((sIndex==numSquaresAllocated)&&
        (!allocateStore(numBlocksAllocated+numBlocksAllocated))) return readAllocFail;
    int rv=0; Sshort *p=(Sshort *)squares[sIndex]; *(int*)(p)=sIndex; p+=intShorts; void *startp=p;
    if (unique) { if ((sIndex==uIndex)||(count++==uniqueSquares[sIndex])) ++sIndex; } else ++sIndex;
    if (incr==0) {
      for (int i=0; i<RC; i++) { int v; rv=fscanf(rfp, "%d", &v);
        if (rv!=1) { if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf("%s",readErrorMsg); return readError; }}
        if (v<smallestRead) if ((smallestRead=v)<SHRT_MIN) return readOORM; 
        if (v>biggestRead) if ((biggestRead=v)>SHRT_MAX) return readOORP; *p++=v;
      }
    } else { int count=0, v[7];
      while (count<RC) {
        switch (incr) {
          case 7:
            if ((rv=fscanf(rfp, "%d %d %d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6]))==incr) 
              for (int a=0; a<=6; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SHRT_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SHRT_MAX) return readOORP; p[a]=x;
              } break;
          case 6:
            if ((rv=fscanf(rfp, "%d %d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]))==incr)
              for (int a=0; a<=5; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SHRT_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SHRT_MAX) return readOORP; p[a]=x;
              } break;
          case 5:
            if ((rv=fscanf(rfp, "%d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4]))==incr)
              for (int a=0; a<=4; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SHRT_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SHRT_MAX) return readOORP; p[a]=x;
              } break;
          case 4:
            if ((rv=fscanf(rfp, "%d %d %d %d", &v[0], &v[1], &v[2], &v[3]))==incr)
              for (int a=0; a<=3; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SHRT_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SHRT_MAX) return readOORP; p[a]=x;
              } break;
          case 3:
              if ((rv=fscanf(rfp, "%d %d %d", &v[0], &v[1], &v[2]))==incr)
              for (int a=0; a<=2; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SHRT_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SHRT_MAX) return readOORP; p[a]=x;
              } break;
          case 2:
            if ((rv=fscanf(rfp, "%d %d", &v[0], &v[1]))==incr)
              for (int a=0; a<=1; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<SHRT_MIN) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>SHRT_MAX) return readOORP; p[a]=x;
              } break;
          default: caseError("readSquaresSshort", incr); return readError;
        }
        if (rv!=incr) { if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf("%s",readErrorMsg); return readError; }} count+=incr; p+=incr; 
      }
    }
  } while (T); return readOK;
} // readSquaresSshort

readStatus readSquaresUshort(FILE *rfp, const int incr, const bool unique) {
//         -----------------
  int count=0;
  do {
    if ((sIndex==numSquaresAllocated)&&
       (!allocateStore(numBlocksAllocated+numBlocksAllocated))) return readAllocFail;
    int rv=0; Ushort *p=(Ushort *)squares[sIndex]; *(int*)(p)=sIndex; p+=intShorts; void *startp=p;
    if (unique) { if ((sIndex==uIndex)||(count++==uniqueSquares[sIndex])) ++sIndex; } else ++sIndex;
    if (incr==0) {
      for (int i=0; i<RC; i++) { int v; rv=fscanf(rfp, "%d", &v);
        if (rv!=1) { if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf("%s",readErrorMsg); return readError; }}
        if (v<smallestRead) if ((smallestRead=v)<0) return readOORM; 
        if (v>biggestRead) if ((biggestRead=v)>USHRT_MAX) return readOORP; *p++=v;
      }
    } else { int count=0, v[7];
      while (count<RC) {
        switch (incr) {
          case 7:
            if ((rv=fscanf(rfp, "%d %d %d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5], &v[6]))==incr) 
              for (int a=0; a<=6; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>USHRT_MAX) return readOORP; p[a]=x;
              } break;
          case 6:
            if ((rv=fscanf(rfp, "%d %d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4], &v[5]))==incr)
              for (int a=0; a<=5; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>USHRT_MAX) return readOORP; p[a]=x;
              } break;
          case 5:
            if ((rv=fscanf(rfp, "%d %d %d %d %d", &v[0], &v[1], &v[2], &v[3], &v[4]))==incr)
              for (int a=0; a<=4; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>USHRT_MAX) return readOORP; p[a]=x;
              } break;
          case 4:
            if ((rv=fscanf(rfp, "%d %d %d %d", &v[0], &v[1], &v[2], &v[3]))==incr)
              for (int a=0; a<=3; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>USHRT_MAX) return readOORP; p[a]=x;
              } break;
          case 3:
            if ((rv=fscanf(rfp, "%d %d %d", &v[0], &v[1], &v[2]))==incr)
              for (int a=0; a<=2; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>USHRT_MAX) return readOORP;  p[a]=x;
              } break;
          case 2:
            if ((rv=fscanf(rfp, "%d %d", &v[0], &v[1]))==incr)
              for (int a=0; a<=1; a++) {
                const int x=v[a]; if (x<smallestRead) if ((smallestRead=x)<0) return readOORM; 
                if (x>biggestRead) if ((biggestRead=x)>USHRT_MAX) return readOORP; p[a]=x;
              } break;
          default: caseError("readSquaresUshort", incr); return readError;
        }
        if (rv!=incr) { if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf("%s",readErrorMsg); return readError; }} count+=incr; p+=incr; 
      }
    }
  } while (T); return readOK;
} // readSquaresUshort

readStatus readSquaresInt(FILE *rfp, const int incr, const bool unique) {
//         --------------
  int count=0;
  do {
    if ((sIndex==numSquaresAllocated)&&
        (!allocateStore(numBlocksAllocated+numBlocksAllocated))) return readAllocFail;
    int rv=0; int *p=(int *)squares[sIndex]; *(int*)(p)=sIndex; void *startp=++p;
    if (unique) { if ((sIndex==uIndex)||(count++==uniqueSquares[sIndex])) ++sIndex; } else ++sIndex;
    if (incr==0) {
      for (int i=0; i<RC; i++) { int v; rv=fscanf(rfp, "%d", &v);
        if (rv!=1) { if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf("%s",readErrorMsg); return readError; }} *p++=v;
        if (v<smallestRead) smallestRead=v; if (v>biggestRead)  biggestRead=v;
      }
    } else { int count=0;
      while (count<RC) {
        switch (incr) {
          case 7:
            if ((rv=fscanf(rfp, "%d %d %d %d %d %d %d", &p[0], &p[1], &p[2], &p[3], &p[4], &p[5], &p[6]))==incr)
              for (int a=0; a<=6; a++) {
                if (p[a]<smallestRead) smallestRead=p[a]; if (p[a]>biggestRead) biggestRead=p[a];
              } break;
          case 6:
            if ((rv=fscanf(rfp, "%d %d %d %d %d %d", &p[0], &p[1], &p[2], &p[3], &p[4], &p[5]))==incr)
              for (int a=0; a<=5; a++) {
                if (p[a]<smallestRead) smallestRead=p[a]; if (p[a]>biggestRead) biggestRead=p[a];
              } break;
          case 5:
            if ((rv=fscanf(rfp, "%d %d %d %d %d", &p[0], &p[1], &p[2], &p[3], &p[4]))==incr)
              for (int a=0; a<=4; a++) {
                if (p[a]<smallestRead) smallestRead=p[a]; if (p[a]>biggestRead) biggestRead=p[a];
              } break;
          case 4:
            if ((rv=fscanf(rfp, "%d %d %d %d", &p[0], &p[1], &p[2], &p[3]))==incr)
              for (int a=0; a<=3; a++) {
                if (p[a]<smallestRead) smallestRead=p[a]; if (p[a]>biggestRead) biggestRead=p[a];
              } break;
          case 3:
            if ((rv=fscanf(rfp, "%d %d %d", &p[0], &p[1], &p[2]))==incr)
              for (int a=0; a<=2; a++) {
                if (p[a]<smallestRead) smallestRead=p[a]; if (p[a]>biggestRead) biggestRead=p[a];
              } break;
          case 2:
            if ((rv=fscanf(rfp, "%d %d", &p[0], &p[1]))==incr)
              for (int a=0; a<=1; a++) {
                if (p[a]<smallestRead) smallestRead=p[a]; if (p[a]>biggestRead) biggestRead=p[a];
              } break;
          default: caseError("readSquaresInt", incr); return readError;
        }
        if (rv!=incr) { if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf("%s",readErrorMsg); return readError; }} count+=incr; p+=incr; 
      }
    }
  } while (T); return readOK;
} // readSquaresInt

bool readSquares(FILE *rfp) {
//   -----------
  char startMsg[50]; snprintf(startMsg, 50, ".. reading numbers as ");
  printf("\n%s%s\n", startMsg, numberBytes==byteBytes
         ? "unsigned bytes" : numberBytes==shortBytes ? "unsigned shorts" : "signed ints"); 
  readStatus rc=readError; bool signedNums=signedNumbers;
  do {
    enum retry_t {tryNone, trySbyte, tryUbyte, trySshort, tryUshort, tryInt}; retry_t retry=tryNone;
    int incr=0; for (int i=7; i>1; i--) if ((RC%i)==0) { incr=i; break; }
    switch (numberBytes) {
      case 1:
        if (signedNums) { rc=readSquaresSbyte(rfp, incr, F);
          if ((rc==readOORM)|(rc==readOORP)) {
            if ((smallestRead<SHRT_MIN)|(biggestRead>SHRT_MAX)) { retry=tryInt; numberBytes=intBytes; }
            else { retry=trySshort; numberBytes=shortBytes; }
          }
        } else { /* not signedNums */ rc=readSquaresUbyte(rfp, incr, F);
          if (rc==readOORM) { signedNums=T;
            if ((smallestRead>=SCHAR_MIN)&(biggestRead<=SCHAR_MAX)) { retry=trySbyte; numberBytes=1; 
            } else if ((smallestRead>=SHRT_MIN)&(biggestRead<=SHRT_MAX)) { retry=trySshort; numberBytes=shortBytes;
            } else { retry=tryInt; numberBytes=intBytes; }
          } else if (rc==readOORP) {
            if (biggestRead<=USHRT_MAX) { retry=tryUshort; numberBytes=shortBytes;
            } else { retry=tryInt; numberBytes=intBytes; signedNums=T; }
          }
        } break;
      case 2:
        if (signedNums) { rc=readSquaresSshort(rfp, incr, F);
          if ((rc==readOORM)|(rc==readOORP)) { retry=tryInt; numberBytes=intBytes; }
        } else { /* not signedNums */ rc=readSquaresUshort(rfp, incr, F); signedNums=T;
          if (rc==readOORM) {     
            if ((smallestRead>=SCHAR_MIN)&(biggestRead<=SCHAR_MAX)) { retry=trySbyte; numberBytes=1; 
            } else if ((smallestRead>=SHRT_MIN)&(biggestRead<=SHRT_MAX)) { retry=trySshort; numberBytes=shortBytes;
            } else { retry=tryInt; numberBytes=intBytes; }
          } else if (rc==readOORP) { retry=tryInt; numberBytes=intBytes; }
        } break;
      case 4: rc=readSquaresInt(rfp, incr, F); break;
      default: caseError("readSquares", numberBytes); return F;
    }
    if (retry==tryNone) break; else {
      printf("number out of range\n"); freeStore(numBlocksAllocated); initGlobals(numberBytes, signedNums);
      if ( allocateStore(startNumBlocks)) { rewind(rfp);
        switch (retry) {
          case trySbyte: printf("%ssigned bytes\n", startMsg); break;
          case trySshort: printf("%ssigned shorts\n", startMsg); break;
          case tryUshort: printf("%sunsigned shorts\n", startMsg); break;
          case tryInt: printf("%ssigned ints\n", startMsg); break;
          default: caseError("readSquares", retry); return F;
        }
      } else { rc=readAllocFail; break; }
    }
  } while (T); return rc==readOK;
} // readSquares

bool readSquaresUnique(FILE *rfp) {
//   -----------------
  printf(".. reading unique %s again\n", nameSR);
  int incr=0; for (int i=7; i>1; i--) if ((RC%i)==0) { incr=i; break; }
  const bool signedNums=signedNumbers; sIndex=0; rewind(rfp);
  switch (numberBytes) {
    case 1:
      if (signedNums) { if (readSquaresSbyte(rfp, incr, T)!=readOK) return F;
      } else { if (readSquaresUbyte(rfp, incr, T)!=readOK) return F; } break;
    case 2:
      if (signedNums) { if (readSquaresSshort(rfp, incr, T)!=readOK) return F;
      } else { if (readSquaresUshort(rfp, incr, T)!=readOK) return F; } break;
    case 4:
      if (readSquaresInt(rfp, incr, T)!=readOK) return F; break;
    default: caseError("readSquaresUnique", numberBytes); return F;
  } return T;
} // readSquaresUnique
//============================================= sort ==========================================

int cmpSquaresSbyte(const void *pp, const void *pq) {
//  ---------------
  Sbyte *p=*(Sbyte **)pp+intBytes, *q=*(Sbyte **)pq+intBytes; int i=0;
  while(i++<RC) if (*p<*q) return -1; else if (*p++>*q++) return 1; return 0;
} // cmpSquaresSbyte

int cmpSquaresUbyte(const void *pp, const void *pq) {
//  ---------------
  Ubyte *p=*(Ubyte **)pp+intBytes, *q=*(Ubyte **)pq+intBytes; int i=0;
  while(i++<RC) if (*p<*q) return -1; else if (*p++>*q++) return 1; return 0;
} // cmpSquaresUbyte

int cmpSquaresSshort(const void *pp, const void *pq) {
//  ----------------
  Sshort *p=*(Sshort **)pp+intShorts, *q=*(Sshort **)pq+intShorts; int i=0;
  while(i++<RC) if (*p<*q) return -1; else if (*p++>*q++) return 1; return 0;
} // cmpSquaresSshort

int cmpSquaresUshort(const void *pp, const void *pq) {
//  ----------------
  Ushort *p=*(Ushort **)pp+intShorts, *q=*(Ushort **)pq+intShorts; int i=0;
  while(i++<RC) if (*p<*q) return -1; else if (*p++>*q++) return 1; return 0;
} // cmpSquaresUshort

int cmpSquaresInt(const void *pp, const void *pq) {
//  -------------
  int *p=*(int **)pp+1, *q=*(int **)pq+1, i=0;
  while(i++<RC) if (*p<*q) return -1; else if (*p++>*q++) return 1; return 0;
} // cmpSquaresInt

void sortSquares() {
//   -----------
  printf(".. sorting %s\n", nameSR);
  switch (numberBytes) {
    case 1:
      if (signedNumbers) qsort(squares, sIndex, pointerSize, cmpSquaresSbyte);
      else qsort(squares, sIndex, pointerSize, cmpSquaresUbyte); break;
    case 2:
      if (signedNumbers) qsort(squares, sIndex, pointerSize, cmpSquaresSshort);
      else qsort(squares, sIndex, pointerSize, cmpSquaresUshort); break;
    case 4: qsort(squares, sIndex, pointerSize, cmpSquaresInt); break;
    default: caseError("sortSquares", numberBytes);
  }
} // sortSquares
//=========================================== output ==========================================

int getUniqueSquaresSbyte() {
//  ---------------------
  int countDuplicates=0; uniqueSquares[uIndex++]=*((int**)squares)[0];
  for (int sq=1; sq<sIndex; sq++) {
    if (cmpSquaresSbyte(&squares[sq-1], &squares[sq])==0) { countDuplicates++;
      uniqueSquares[uIndex-1]=min(uniqueSquares[uIndex-1], *((int**)squares)[sq]);
    } else uniqueSquares[uIndex++]=*((int**)squares)[sq];
  }
  return countDuplicates;     
} // getUniqueSquaresSbyte

int getUniqueSquaresUbyte() {
//  ---------------------
  int countDuplicates=0; uniqueSquares[uIndex++]=*((int**)squares)[0];
  for (int sq=1; sq<sIndex; sq++) {
    if (cmpSquaresUbyte(&squares[sq-1], &squares[sq])==0) { countDuplicates++;
      uniqueSquares[uIndex-1]=min(uniqueSquares[uIndex-1], *((int**)squares)[sq]);
    } else uniqueSquares[uIndex++]=*((int**)squares)[sq];
  }
  return countDuplicates;     
} // getUniqueSquaresUbyte

int getUniqueSquaresSshort() {
//  ----------------------
  int countDuplicates=0; uniqueSquares[uIndex++]=*((int**)squares)[0];
  for (int sq=1; sq<sIndex; sq++) {
    if (cmpSquaresSshort(&squares[sq-1], &squares[sq])==0) { countDuplicates++;
      uniqueSquares[uIndex-1]=min(uniqueSquares[uIndex-1], *((int**)squares)[sq]);
    } else uniqueSquares[uIndex++]=*((int**)squares)[sq];
  }
  return countDuplicates;
} // getUniqueSquaresSshort

int getUniqueSquaresUshort() {
//  ----------------------
  int countDuplicates=0; uniqueSquares[uIndex++]=*((int**)squares)[0];
  for (int sq=1; sq<sIndex; sq++) {
    if (cmpSquaresUshort(&squares[sq-1], &squares[sq])==0) { countDuplicates++;
      uniqueSquares[uIndex-1]=min(uniqueSquares[uIndex-1], *((int**)squares)[sq]);
    } else uniqueSquares[uIndex++]=*((int**)squares)[sq];
  }
  return countDuplicates;
} // getUniqueSquaresUshort

int getUniqueSquaresInt() {
//  -------------------
  int countDuplicates=0; uniqueSquares[uIndex++]=*((int**)squares)[0];
  for (int sq=1; sq<sIndex; sq++) {
    if (cmpSquaresInt(&squares[sq-1], &squares[sq])==0) { countDuplicates++;
      uniqueSquares[uIndex-1]=min(uniqueSquares[uIndex-1], *((int**)squares)[sq]);
    } else uniqueSquares[uIndex++]=*((int**)squares)[sq];
  }
  return countDuplicates;
} // getUniqueSquaresInt

int cmpIntsAscending(const void *vp, const void *vq) { int p=*(int*)vp, q=*(int*)vq; return p<q ? -1 : 1; }
//  ----------------

int getUniqueSquares() {
//   ----------------
  uniqueSquares=(int*)malloc(numSquaresAllocated*sizeof(int));
  if (uniqueSquares==NULL) return reportError(storeAllocFail); int countDuplicates=0;
  switch (numberBytes) {
    case 1:
      if (signedNumbers) countDuplicates=getUniqueSquaresSbyte();
      else countDuplicates=getUniqueSquaresUbyte(); break;
    case 2:
      if (signedNumbers) countDuplicates=getUniqueSquaresSshort();
      else countDuplicates=getUniqueSquaresUshort(); break;
    case 4: countDuplicates=getUniqueSquaresInt(); break;
    default: caseError("getUniqueSquares", numberBytes); return 0;
  }
  qsort(uniqueSquares, uIndex, intBytes, cmpIntsAscending);
  if (countDuplicates>0) printf("  .. the number of duplicate %s is %d\n\n", nameSR, countDuplicates);
  return countDuplicates;
} // getUniqueSquares

bool sortAndGetUniqueSquares(int *duplicates) {
//   -----------------------
  sortSquares(); *duplicates=getUniqueSquares(); return *duplicates>=0;
} // sortAndGetUniqueSquares
//================================================ output =================================================

int fieldWidth(int i) {
//  ----------
  const bool neg=i<0;  if (neg) i=-i; int width=1; while ((i=i/10)!=0) ++width;
  if (width>10) width=10; /* limited by fprintFW11 */ return neg ? width+1 : width;
} // fieldWidth

typedef bool (*t_fprintFW)(FILE *fp, const int i);

bool fprintFW1(FILE *fp, const int i) { return fprintf(fp, "%1d", i)>0; }
bool fprintFW2(FILE *fp, const int i) { return fprintf(fp, "%2d", i)>0; }
bool fprintFW3(FILE *fp, const int i) { return fprintf(fp, "%3d", i)>0; }
bool fprintFW4(FILE *fp, const int i) { return fprintf(fp, "%4d", i)>0; }
bool fprintFW5(FILE *fp, const int i) { return fprintf(fp, "%5d", i)>0; }
bool fprintFW6(FILE *fp, const int i) { return fprintf(fp, "%6d", i)>0; }
bool fprintFW7(FILE *fp, const int i) { return fprintf(fp, "%7d", i)>0; }
bool fprintFW8(FILE *fp, const int i) { return fprintf(fp, "%8d", i)>0; }
bool fprintFW9(FILE *fp, const int i) { return fprintf(fp, "%9d", i)>0; }
bool fprintFW10(FILE *fp, const int i) { return fprintf(fp, "%10d", i)>0; }
bool fprintFW11(FILE *fp, const int i) { return fprintf(fp, "%11d", i)>0; }

static t_fprintFW fprintFW[]=
  { NULL,      fprintFW1, fprintFW2, fprintFW3, fprintFW4, fprintFW5,
    fprintFW6, fprintFW7, fprintFW8, fprintFW9, fprintFW10, fprintFW11
  };

bool writeSquaresSbyte(FILE *wfp, const int fw) {
//  -----------------
  for (int sq=0; sq<sIndex; sq++) {
    int i=0, colCount=0; Sbyte *p=((Sbyte **)squares)[sq]+intBytes;
    while (i++<RC) { if (!fprintFW[fw](wfp, *p++)) return F;
      if (++colCount==C) { if (fputc('\n', wfp)==EOF) return F; colCount=0;
      } else if (fputc(' ', wfp)==EOF) return F;         
    }
    if (fputc('\n', wfp)==EOF) return F;
  } return T;     
} // writeSquaresSbyte

bool writeSquaresOrder5(FILE *wfp) {
//   ------------------
  for (int sq=0; sq<sIndex; sq++) {
    char squareString[100], *s=squareString;
    int i=0, colCount=0; Ubyte *p=((Ubyte **)squares)[sq]+intBytes;
    while (i++<RC) { const int v=*p++;
      if (v<10) { *s++=' '; *s++='0'+v; } else if (v<20) { *s++='1'; *s++='0'-10+v; }
      else { *s++='2'; *s++='0'-20+v; } if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';
    }
    *s++='\n'; *s++='\0'; if (fputs(squareString, wfp)==EOF) return F;
  } return T;     
} // writeSquaresOrder5

bool writeSquaresUbyte(FILE *wfp, const int fw) {
//   -----------------
  for (int sq=0; sq<sIndex; sq++) {
    int i=0, colCount=0; Ubyte *p=((Ubyte **)squares)[sq]+intBytes;
    while (i++<RC) {
      if (!fprintFW[fw](wfp, *p++)) return F;
      if (++colCount==C) { if (fputc('\n', wfp)==EOF) return F; colCount=0;
      } else if (fputc(' ', wfp)==EOF) return F;         
    }
    if (fputc('\n', wfp)==EOF) return F;
  } return T;       
} // writeSquaresUbyte

bool writeSquaresSshort(FILE *wfp, const int fw) {
//   ------------------
  for (int sq=0; sq<sIndex; sq++) {
    int i=0, colCount=0; Sshort *p=((Sshort **)squares)[sq]+intShorts;
    while (i++<RC) {
      if (!fprintFW[fw](wfp, *p++)) return F;
      if (++colCount==C) { if (fputc('\n', wfp)==EOF) return F; colCount=0;
      } else if (fputc(' ', wfp)==EOF) return F;         
    }
    if (fputc('\n', wfp)==EOF) return F;
  } return T;     
} // writeSquaresSshort

bool writeSquaresUshort(FILE *wfp, const int fw) {
//   ------------------
  for (int sq=0; sq<sIndex; sq++) {
    int i=0, colCount=0; Ushort *p=((Ushort **)squares)[sq]+intShorts;
    while (i++<RC) {
      if (!fprintFW[fw](wfp, *p++)) return F;
      if (++colCount==C) { if (fputc('\n', wfp)==EOF) return F; colCount=0;
      } else if (fputc(' ', wfp)==EOF) return F;         
    }
    if (fputc('\n', wfp)==EOF) return F;
  } return T;     
} // writeSquaresUshort

bool writeSquaresInt(FILE *wfp, const int fw) {
//   ---------------
  for (int sq=0; sq<sIndex; sq++) {
    int i=0, colCount=0; int *p=((int **)squares)[sq]+1;
    while (i++<RC) {
      if (!fprintFW[fw](wfp, *p++)) return F;
      if (++colCount==C) { if (fputc('\n', wfp)==EOF) return F; colCount=0;
      } else if (fputc(' ', wfp)==EOF) return F;         
    }
    if (fputc('\n', wfp)==EOF) return F;
  } return T;     
} // writeSquaresInt

bool writeSummary(FILE *wfp) {
//   ------------
  if (fputs("            Units Retained          Number of Squares\n", wfp)==EOF) return F;
  if (fputs("          ------------------        -----------------\n", wfp)==EOF) return F;
  int count=1, lines=0, prev=*((int **)squares)[0];
  for (int sq=1; sq<sIndex; sq++) {
    const int wr=*((int **)squares)[sq];
    if (wr==prev) ++count;
    else {
      if (fprintf(wfp, "              %10d               %10d\n", prev, count)<0) return F;
      if (++lines==5) { if (fputc('\n', wfp)==EOF) return F; lines=0; } prev=wr; count=1;
    }
  }
  if (fprintf(wfp, "              %10d               %10d\n", prev, count)<0) return F;
  if (fprintf(wfp, "                                    -----------------\n")<0) return F;
  if (fprintf(wfp, "                                       %10d\n", sIndex)<0) return F;
  return T;
} // writeSummary

bool writeSquares(FILE *wfp) {
//   ------------
  const int sW=fieldWidth(smallestRead), bW=fieldWidth(biggestRead), fw=sW>bW ? sW : bW;
  switch (numberBytes) {
    case 1:
      if (signedNumbers) return writeSquaresSbyte(wfp, fw);
      else
        if ((R==5)&(C==5)&(biggestRead<=RC)) return writeSquaresOrder5(wfp);
        else return writeSquaresUbyte(wfp, fw);
    case 2:
      if (signedNumbers) return writeSquaresSshort(wfp, fw);
      else return writeSquaresUshort(wfp, fw);
    case 4:
      return writeSquaresInt(wfp, fw);
    default: caseError("writeSquares", numberBytes); return F;
  } return T;
} // writeSquares

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(obuf, s); 
} // stripName

enum t_ofile { WRsort, WRsummary };
FILE *openOutput(char *inFname, t_ofile which) {
//    ----------
  FILE *wfp=NULL; int sub=0; const int baseSize=bufSize+25, outSize=bufSize+50;
  char baseName[baseSize], buf[outSize]; stripName(inFname, buf);
  snprintf(baseName, baseSize, "./%s%s%c", buf,
           which==WRsort ? "WRsort" : "WRsummary", dori==sortDown?'D':'I');
  snprintf(buf, outSize, "%s.txt", baseName);
  do {
    if (access(buf, F_OK)!=0) break; snprintf(buf, outSize, "%s_%d.txt", baseName, ++sub);
  } while (T);
  if ((wfp=fopen(buf, "w"))==NULL) {
    char msg[outSize+50]; snprintf(msg, outSize+50, "\a\nCan't open for write %s", buf); perror(msg);
  } else {
    printf(".. writing %s to file %s\n", which==WRsort ? nameSR : "summary", buf);
  } return wfp;
} // openOutput
//=================================================== get water ================================================

void initBounds() {
//   ----------
  for (int r=0; r<R; r++) { water[r][0]=xSquare[r][0]; water[r][Cm1]=xSquare[r][Cm1]; }
  for (int c=1; c<Cm1; c++) { water[0][c]=xSquare[0][c]; water[Rm1][c]=xSquare[Rm1][c]; }
  for (int r=1; r<Rm1; r++) for (int c=1; c<Cm1; c++) water[r][c]=biggestRead; 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; return T;
} // insertQueue

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

bool queueBorder() {
//   -----------
  if ((R>2)&(C>2)) {
    pushQueue(water[1][0], 1, 0); if (!insertQueue(water[1][Cm1], 1, Cm1)) return F;
    for (int r=2; r<Rm1; r++) {
  	  if (!insertQueue(water[r][0], r, 0)) return F; if (!insertQueue(water[r][Cm1], r, Cm1)) return F;
    }
    for (int c=1; c<Cm1; c++) {
	    if (!insertQueue(water[0][c], 0, c)) return F; if (!insertQueue(water[Rm1][c], Rm1, 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=&water[r][c]; const int tmp=max(xSquare[r][c],p);
  	if (tmp<*bp) { *bp=tmp; if (!insertQueue(tmp, r, c)) return F; }
  } return T;
} // computeBounds

Uint computeCapacity() {
//   ---------------
  Uint units=0;
  for (int r=0; r<R; r++) for (int c=0; c<C; c++) {
	  const int delta=water[r][c]-xSquare[r][c]; if (delta!=0) units+=delta;
	} return units;
} // computeCapacity

void copySquare(const int sq) {
//   ----------
   const bool signedNums=signedNumbers; int i=0;
   switch (numberBytes) {
      case 1:
        if (signedNums) {
          Sbyte *p=(Sbyte *)squares[sq]+intBytes;
          for (int r=0; r<R; ++r) for (int c=0; c<C; ++c) xSquare[r][c]=p[i++];
        } else {
          Ubyte *p=(Ubyte *)squares[sq]+intBytes;
          for (int r=0; r<R; ++r) for (int c=0; c<C; ++c) xSquare[r][c]=p[i++];
        } break;
      case 2:
        if (signedNums) {
          Sshort *p=(Sshort *)squares[sq]+shortBytes;
          for (int r=0; r<R; ++r) for (int c=0; c<C; ++c) xSquare[r][c]=p[i++];
        } else {
          Ushort *p=(Ushort *)squares[sq]+shortBytes;;
          for (int r=0; r<R; ++r) for (int c=0; c<C; ++c) xSquare[r][c]=p[i++];
        } break;
      case 4: { int *p=(int *)squares[sq]+1;
        for (int r=0; r<R; ++r) for (int c=0; c<C; ++c) xSquare[r][c]=p[i++];
      } break;
      default: caseError("copySquare", numberBytes);
  }
} // copySquare

/*
 *  Based on an algorithm of Gareth McCaughan supplied by Craig Knecht.
 */
bool getWater(const int sq) {
//   --------
  copySquare(sq); initBounds(); if (!queueBorder()) return F;
  while (qIndex!=0) {
  	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;
  }
  *(Uint *)squares[sq]=computeCapacity(); return T;
} // getWater

int cmpUintsDecreasing(const void *pp, const void *pq) {
//  ------------------
  Uint *p=*(Uint **)pp, *q=*(Uint **)pq; return *p<*q ? 1 : *p>*q ? -1 : 0;
} // cmpUintsDecreasing

int cmpUintsIncreasing(const void *pp, const void *pq) {
//  ------------------
  Uint *p=*(Uint **)pp, *q=*(Uint **)pq; return *p>*q ? 1 : *p<*q ? -1 : 0;
} // cmpUintsIncreasing

typedef int(*t_icvpcvp)(const void *, const void *);
t_icvpcvp cmpUints[]={ NULL, cmpUintsDecreasing, cmpUintsIncreasing };
bool sortByWaterRetention(const int dori) {
//   --------------------
   printf(".. computing water retention\n"); if (!allocateStoreWR()) return F;
   for (int sq=0; sq<sIndex; ++sq) { if (!getWater(sq)) { freeStoreWR(); return F; }}
   printf(".. sorting %s by water retention\n", nameSR);
   qsort(squares, sIndex, pointerSize, cmpUints[dori]); freeStoreWR(); return T;
} // sortByWaterRetention
//============================================ main ============================================

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

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

int getUpDown() {
//  ---------
  printf("\nSort: %d %s or %d %s? ", sortDown, sortName[sortDown], sortUp, sortName[sortUp] );
  dori=getInt(); if (dori<sortDown) dori=sortDown; if (dori>sortUp) dori=sortUp; return dori;
} // getUpDown

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; }
  return F;
} // doAnother

int main() {
//  ----
  bool another=T, inputOrder=T, writeError=F, ok=F; char buf[bufSize]; outputLocalTime();
  do {
    if (inputOrder) { printf("\nOrder? "); if (!getInts(&R, &C, -1)) break;}
    initGlobals(0, (R*C)>USHRT_MAX); FILE *rfp=openInput(buf);
    if (rfp!=NULL) {
      time_t startTime=time(NULL);
      if (allocateStore(startNumBlocks)) {
        if (readSquares(rfp)) { int duplicates=0;
          if (sortAndGetUniqueSquares(&duplicates)) {
            if ((duplicates==0)||readSquaresUnique(rfp)) {
              if (sortByWaterRetention(getUpDown())) {
                FILE *wfp=openOutput(buf, WRsort), *wfps=openOutput(buf, WRsummary);
                if ((wfp!=NULL)&(wfps!=NULL)) {
                  writeError=!(writeSquares(wfp)&&writeSummary(wfps)); fclose(wfp); fclose(wfps);
                  if (!writeError) ok=T; // at least one success loop
                }
              }
            }
          }
        }
        freeStore(numBlocksAllocated);
      }
      fclose(rfp); reportElapsedTime(startTime);
    }
    another=doAnother(&inputOrder, writeError);
  } while (another);
  printf("\nHit return to close the console.");
  while (T) if (getchar()=='\n') break; return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main
