/*
 *  File:    HighlightSort.cpp
 *  Author:  S Harry White
 *  Created: 2020-10-08
 *  Updated: 2020-10-23
 *    Add support for non-square rectangles.
 *  Updated: 2020-11-20
 *    Code improvements.
 * Updated: 2021-05-21
 *   Improve store allocation.
 * Updated: 2021-09-13
 *   Add "number range" feature.
 * Updated: 2022-07-09
 *   Fix writeSquares for 5xC for non-square.
 * Updated: 2023-01-24
 *   Open output file in the current folder,
 *   Change fscanf_s int conversion specifications %ld to %d.
 *   Replace asserts with calls of caseError.
 * Updated: 2023-02-20
 *   Improve openInput and openOutput.
 */

/*
 *  Sorts rectangles by number of groups of connected highlighted cells.
 *  Highlighted feature choices are:
 *
 *     non-prime numbers
 *     prime numbers
 *     odd numbers
 *     even numbers
 *     singly-even numbers
 *     doubly-even numbers
 *     numbers in the natural position
 *     prime numbers in the natural position
 *     number range
 */

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

#define Sbyte  signed   char
#define Ubyte  unsigned char
#define Sshort signed   short
#define Ushort unsigned short
#define Uint   unsigned long int

const bool F=false, T=true; bool **highLight=NULL, signedNumbers, reportAlloc, bell;

char *hiName[]={
        NULL, "non-prime", "prime", "odd", "even", "natural", "singly-even",
              "natural-prime", "doubly-even", "number range" },
     *nameSq="squares", *nameRect="rectangles", *nameSR,
     *sortName[]={ NULL, "descending", "ascending" };

const int byteBytes =1, shortBytes=sizeof(short), intBytes=sizeof(int),
          intShorts=sizeof(int)/sizeof(short), pointerSize=sizeof(void *),
          startNumBlocks=1, startSize=25, bufSize=1024, outSize=bufSize+50, sortDown=1, sortUp=2,
          non_prime=1, prime=2, odd=3, even=4, natural=5, singly_even=6,
          natural_prime=7, doubly_even=8, num_range=9,
          firstKind=non_prime, lastKind=num_range, numKinds=lastKind-firstKind+1;
const Uint oneB=1000000000, oneM=1000000, oneT=1000, maxSquares=MAXUINT/pointerSize;
int R, C,           // The order of the rectangle.
    RC,             // R*C
    lo, hi,         // number range
    smallestRead, biggestRead,
    numberBytes,    // 1, 2 or 4 byte integer for numbers
    squareSize,     // intBytes+RC*numberBytes
    numBlocksAllocated, maxBlocksIncr, squaresPerBlock, numSquaresAllocated,
    sIndex, uIndex, allocatedQueueSize, qIndex,
    **xSquare=NULL, **group=NULL, *uniqueSquares=NULL;

struct t_Cell { int group, row, col; }; t_Cell *Queue=NULL; void **blocks=NULL, **squares=NULL;

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

void initGlobals(int numBytes, bool signedNums) {
//   -----------
  RC=R*C; qIndex=0; sIndex=0; uIndex=0; reportAlloc=F; bell=T;
  numBlocksAllocated=0; numSquaresAllocated=0; allocatedQueueSize=0;
  smallestRead=LONG_MAX; biggestRead=LONG_MIN; signedNumbers=signedNums;
  numberBytes=(numBytes==0) ? (RC<=UCHAR_MAX ? byteBytes
                            : RC<=USHRT_MAX ? shortBytes : intBytes) : numBytes;
  squareSize=intBytes+RC*numberBytes; nameSR=R==C ? nameSq : nameRect;

  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
//========================================== allocate store ============================================

char *storeAllocFail="Storage allocation failed";
bool reportError(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 freeIntSquare(int*** square, const int size) {
//   -------------
  if (*square!=NULL) {
    for (int i=0; i<size; i++) free((*square)[i]); free(*square); *square=NULL;
  }
} // freeIntSquare

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

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

void freeStoreHi() {
//   -----------
  freeIntSquare(&xSquare, R); freeIntSquare(&group, R);
  freeBoolSquare(&highLight, R); freeQueue();
  if (uniqueSquares!=NULL) { free(uniqueSquares); uniqueSquares=NULL; }
}

bool allocateIntSquare(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) freeIntSquare(square, numAllocated);
  }
  return ok;
} // allocateIntSquare

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

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 allocateStoreHi() {
//   ---------------
  bool ok=T;

  if (ok=allocateIntSquare(&xSquare))
	  if (ok=allocateIntSquare(&group))
      if (ok=allocateBoolSquare(&highLight))
        ok=allocateQueue(RC);
  if (!ok) freeStoreHi();
  return ok;
} // allocateStoreHi
//========================================== 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');
}

int getInt() { int n=0; scanf_s("%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 getRange(int *p, int *q) {
//   --------
  bool ok=F; int c; *p=*q=0;

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

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("\nEnter the name of the %s file: ", nameSR);
    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, rFname, "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

enum readStatus { readAllocFail, readError, readOORM, readOORP, readOK} ;
const char *readErrorMsg="\a\nError reading file.\n";
readStatus readSquaresSbyte(FILE *rfp, const int incr, 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_s(rfp, "%d", &v);
        if (rv!=1)
          if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf(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_s(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_s(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_s(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_s(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_s(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_s(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(readErrorMsg); return readError; } count+=incr; p+=incr;
      } 
    }
  } while (T); return readOK;
} // readSquaresSbyte

readStatus readSquaresUbyte(FILE *rfp, const int incr, 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_s(rfp, "%d", &v);
        if (rv!=1)
          if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf(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_s(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_s(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_s(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_s(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_s(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_s(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(readErrorMsg); return readError; } count+=incr; p+=incr;
      } 
    }
  } while (T); return readOK;
} // readSquaresUbyte

readStatus readSquaresSshort(FILE *rfp, const int incr, 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_s(rfp, "%d", &v);
        if (rv!=1)
          if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf(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_s(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_s(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_s(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_s(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_s(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_s(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(readErrorMsg); return readError; } count+=incr; p+=incr; 
      }
    }
  } while (T); return readOK;
} // readSquaresSshort

readStatus readSquaresUshort(FILE *rfp, const int incr, 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_s(rfp, "%d", &v);
        if (rv!=1)
          if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf(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_s(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_s(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_s(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_s(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_s(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_s(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(readErrorMsg); return readError; } count+=incr; p+=incr; 
      }
    }
  } while (T); return readOK;
} // readSquaresUshort

readStatus readSquaresInt(FILE *rfp, const int incr, 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_s(rfp, "%d", &v);
        if (rv!=1)
          if ((rv==EOF)&(p==startp)) { --sIndex; return readOK; }
          else { printf(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_s(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_s(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_s(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_s(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_s(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_s(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(readErrorMsg); return readError; } count+=incr; p+=incr; 
      }
    }
  } while (T); return readOK;
} // readSquaresInt

bool readSquares(FILE *rfp) {
//   -----------
  char startMsg[50]; sprintf_s(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 tryUbyte: caseError("readSquares", tryUbyte); return F;
          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; }
  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
//============================================== output ================================================

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

enum t_ofile { hiSort, hiSummary };
FILE *openOutput(char *inFname, t_ofile which, const int kind, const int dora) {
//    ----------
  FILE *wfp=NULL; int sub=0; const int baseSize=bufSize+25;
  char baseName[baseSize], buf[outSize]; stripName(inFname, buf);
  const char *nwhich=which==hiSort ? "Sort" : "Summary", du=dora==sortDown?'D':'A';
  sprintf_s(baseName, baseSize, "%s-%s%s%c", buf, hiName[kind], nwhich, du);
  sprintf_s(buf, outSize, "%s.txt", baseName);
  do {
    if (_access_s(buf, 00)==ENOENT) break; sprintf_s(buf, outSize, "%s_%d.txt", baseName, ++sub);
  } while (T);
  if (fopen_s(&wfp, buf, "w")==0)
    printf(".. writing %s to file %s\n", which==hiSort ? nameSR : "summary", buf);
  else {
    char msg[outSize+50]; sprintf_s(msg, outSize+50, "\a\nCan't open for write %s", buf); perror(msg);
  } return wfp;
} // openOutput

int fieldWidth(int i) {
//  ----------
  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("           Number of Groups         Number of Squares\n", wfp)==EOF)
    return F;
  if (fputs("          ------------------        -----------------\n", wfp)==EOF)
    return F;
  int count=1, prev=*((int **)squares)[0], lines=0;;
  for (int sq=1; sq<sIndex; sq++) {
    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)&(R==C)&(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
//==================================== sort and get unique ========================================

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

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;
} // cmpIntsAscending

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
//=========================================== highlight ============================================

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

bool insertQueue(const int group, const int row, const int col) {
//   -----------
  if ((qIndex >= allocatedQueueSize)&&!increaseQueue()) return F;
  t_Cell cell; cell.group=group; cell.row=row; cell.col=col;

  int i=qIndex-1; while (Queue[i].group<group) { 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 checkGroups(const int g, const int r, const int c) {
//   -----------
  if ((0<=r)&(r<R)&(0<=c)&(c<C)) {
    bool hi=highLight[r][c], notVisited=group[r][c]==0;
    if (hi&notVisited) {
      if (qIndex==0) pushQueue(g, r, c); else if (!insertQueue(g, r, c)) return F;
      group[r][c]=g;
    }
  }
  return T;
} // checkGroups

void copySquare(const int sq) {
//   ----------
   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

bool isPrime(const int v, const int r, const int c) {
//   -------
  int i=2; if (v<=1) return F; while ((i*i)<=v) if ((v%i++)==0) return F; return T;
} // isPrime

bool isNotPrime(const int v, const int r, const int c) { return !isPrime(v, r, c); }
//   ----------

bool isOdd(const int v, const int r, const int c) { return (v&1)==1; }
//   -----

bool isEven(const int v, const int r, const int c) { return (v&1)==0; }
//   ------

bool isSinglyEven(const int v, const int r, const int c) { return (v&3)==2; }
//   ------------

bool isDoublyEven(const int v, const int r, const int c) { return (v&3)==0; }
//   ------------

bool isNatural(const int v, const int r, const int c) { return v==(r*C+c+1); }
//   ---------

bool isNaturalPrime(const int v, const int r, const int c) { return isNatural(v,r,c)&&isPrime(v,r,c); }
//   --------------

bool isInRange(const int v, const int r, const int c) { return (v>=lo)&(v<=hi); }
//   ---------

typedef bool(*t_bcIcIcI)(const int, const int, const int);
// non_prime=1, prime=2, odd=3, even=4, natural=5, singly_even=6, natural_prime=7, doubly_even=8
t_bcIcIcI highLightType[]={
  NULL, isNotPrime, isPrime, isOdd, isEven, isNatural, isSinglyEven,
  isNaturalPrime, isDoublyEven, isInRange
};

void hiLiteCells(int **X, t_bcIcIcI highLightType) {
//   -----------
  for (int r=0; r<R; ++r) for (int c=0; c<C; ++c) highLight[r][c]=highLightType(X[r][c], r, c);
} //hiLiteCells

bool getGroups(const int sq, const int kind) {
//   ---------
  copySquare(sq); hiLiteCells(xSquare, highLightType[kind]);
  for (int r=0; r<R; ++r) for (int c=0; c<C; ++c) group[r][c]=0;
  int gnum=0; qIndex=0;
  for (int r=0; r<R; ++r) for (int c=0; c<C; ++c) {
    bool hi=highLight[r][c], notVisited=group[r][c]==0;
    if (hi&notVisited) {
      pushQueue(++gnum, r, c); group[r][c]=gnum;
      while (qIndex!=0) {
   	    t_Cell cell; popQueue(&cell); int g=cell.group, cr=cell.row, cc=cell.col;
	      if (!checkGroups(g, cr-1, cc)) return F; if (!checkGroups(g, cr+1, cc)) return F;
   	    if (!checkGroups(g, cr, cc-1)) return F; if (!checkGroups(g, cr, cc+1)) return F;
      }
    }
  }
  *(Uint *)squares[sq]=gnum; return T;
} // getGroups

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

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

typedef int(*t_icvpcvp)(const void *, const void *);
t_icvpcvp cmpUints[]={ NULL, cmpUintsDescending, cmpUintsAscending };

bool sortByGroupCount(const int kind, const int dora) {
//   ----------------
   printf(".. counting highlighted groups\n");
   if (!allocateStoreHi()) return F;
   for (int sq=0; sq<sIndex; ++ sq) {
     if (!getGroups(sq, kind)) { freeStoreHi(); return F; }
   }
   printf(".. sorting %s by highlighted group count\n", nameSR);
   qsort(squares, sIndex, pointerSize, cmpUints[dora]); freeStoreHi(); return T;
} // sortByGroupCount
//============================================= main ================================================

void outputLocalTime() {
//   --------------
  time_t startTime=time(NULL); struct 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

void reportElapsedTime(time_t startTime) {
//   -----------------
  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 getKind() {
//  -------
  printf("\nHighlight: %d %s, %d %s, %d %s, %d %s, %d %s,\n"
         "           %d %s, %d %s, %d %s, %d %s? ",
         non_prime, hiName[non_prime], prime, hiName[prime],
         odd, hiName[odd], even, hiName[even], natural, hiName[natural],
         singly_even, hiName[singly_even], natural_prime, hiName[natural_prime],
         doubly_even, hiName[doubly_even], num_range, hiName[num_range]);
  int kind=getInt();
  if (kind<non_prime) kind=non_prime; if (kind>num_range) kind=num_range;
  if (kind==num_range) {
    printf("Number range, (low high)? "); if (!getRange(&lo, &hi)) kind=-1;
  }
  return kind;
} // getKind

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

bool doAnother(bool *inputOrder, 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)) {
              int kind=getKind();
              if (kind>=non_prime) {
                int dora=getDownUp();
                if (sortByGroupCount(kind, dora)) {
                  FILE *wfp=openOutput(buf, hiSort, kind, dora),
                        *wfps=openOutput(buf, hiSummary, kind, dora);
                  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("\n\nPress a key to close the console.");
  while (!_kbhit()) Sleep(250); return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main
