/*
 * File:    SquareSort.cpp
 * Author:  S Harry White
 * Created: 2017-04-16
 * Updated: 2020-10-12
 *   Add support for non-square rectangles. Add descending order sort.
 * Updated: 2021-05-15
 *   Include extra code for x64 build, commented out for Win32.
 * Updated: 2022-01-29
 *   Tidy code.
 * Updated: 2022-02-17
 *   Change start read at Ubyte for N < 255, (improvement for Latin squares).
 * Updated: 2023-01-23
 *   Open output file in the current folder,
 *   Replace asserts with calls of caseError.
 *   Change fscanf_s int conversion specifications %ld to %d.
 * Updated: 2023-02-22
 *   Improve openInput and openOutput.
 */


/*
 * Sorts squares in ascending or descending numerical sequence order removing duplicate squares.
 * Square aspect is not changed, so duplicates with different aspect are not removed.
 */

#include "stdafx.h"
#include <conio.h>
#include <direct.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <share.h>
#include <stdio.h>
#include <string.h>
#include <sys\stat.h>
#include <time.h>
#include <Windows.h>

typedef signed   char  Sbyte;
typedef unsigned char  Ubyte;
typedef signed   short Sshort;
typedef unsigned short Ushort;
typedef unsigned int   Uint;
typedef unsigned long long Uint64; // x64

const bool F=false, T=true;
const int byteBytes=1,
          shortBytes=sizeof(short)/sizeof(char),
          intBytes=sizeof(int)/sizeof(char),
          startNumBlocks=1,
          bufSize=1024, outSize=bufSize+50,
          writeSize=100000000, noFD=-1, writeErr=-1,
          sortUp=1, sortDown=2,
          pointerSize=sizeof(void *);
const Uint oneB=1000000000, oneM=1000000, oneT=1000, maxSquares=MAXUINT/pointerSize;

int R, C, // The order of the rectangle.
    RC,   // R*C
    aord, // sort ascending or descending
    smallestRead, biggestRead, numberBytes, // 1, 2 or 4 byte integer for square numbers
    outBytes, squareBytes,
    numBlocksAllocated, maxBlocksIncr, squaresPerBlock, squareSize, // RC*numberBytes
    numSquaresAllocated, sIndex;
bool signedNumbers, reportAlloc; void **blocks=NULL, **squares=NULL;
char outBuffer[writeSize], *nameSq="square", *nameRect="rectangle", *nameSR,
     *sortName[]={ NULL, "ascending", "descending" };

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

const Uint64 oneBB=1000000000000000000; // x64
char fbuf[bufSize];
char *formatN(Uint64 n) {
//    -------
  const int nbufSize=20; char nbuf[nbufSize]; Uint bb=n/oneBB; n%=oneBB; Uint b=n/oneB;  n%=oneB;
  if (bb==0) sprintf_s(fbuf, bufSize, "%u", b);
  else {
    sprintf_s(fbuf, bufSize, "%u", bb);
    sprintf_s(nbuf, nbufSize, ",%03u,%03u,%03u", b/oneM, (b%oneM)/oneT, b%oneT);
    strcat_s(fbuf, bufSize, nbuf);
  }
  sprintf_s(nbuf, nbufSize, ",%03lu,%03lu,%03lu", n/oneM, (n%oneM)/oneT, n%oneT);
  strcat_s(fbuf, bufSize, nbuf); return fbuf;
} // formatN

void initGlobals(const int numBytes, bool signedNums) {
//   -----------
  RC=R*C; reportAlloc=F; squareBytes=0; outBytes=0; aord=sortUp; nameSR=R==C?nameSq:nameRect;
  numBlocksAllocated=0; numSquaresAllocated=0; sIndex=0;
  smallestRead=LONG_MAX; biggestRead=LONG_MIN; signedNumbers=signedNums;
  if (numBytes>0) numberBytes=numBytes; else
    if ((R==C)&(R<=UCHAR_MAX)) numberBytes=byteBytes; /* Latin squares? */ else
    numberBytes=RC<=UCHAR_MAX ? byteBytes : RC<=USHRT_MAX ? shortBytes : intBytes;
  squareSize=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 ======================================================

char *storeAllocFail="Storage allocation failed"; bool bell=T;
bool reportError(char *s) { printf("%sError: %s.\n", bell?"\a\n":"", s); if (bell) bell=F; return F; }
//   -----------
  
void freeStore(const int allocated) {
//   ---------
  if (blocks!=NULL) {
    if ((allocated!=0)) printf(".. freeing %ss\n", nameSR);   
    for (int i=0; i<allocated; i++) free(blocks[i]); free(blocks); blocks=NULL;
  }
  numBlocksAllocated=0;
} // freeStore

bool allocateStore(int numNew) {
//   -------------
  bool ok=F; int numOld=numBlocksAllocated;
  //union ovly { Uint i; void *p; }; // Win32
  union ovly { Uint64 i; void *p; }; // x64

  if ((numNew-numOld)>maxBlocksIncr) numNew=numOld+maxBlocksIncr;
  const Uint numNewSquares=numNew*squaresPerBlock; // Uint for malloc(numNewSquares*pointerSize)
  if (numNewSquares<=maxSquares) {
    if (reportAlloc) {
      int nsq=numNew*squaresPerBlock, msq=nsq/1000000, tsq=nsq%1000000/1000, rsq=nsq%1000;
      printf("    .. increasing %ss to ", nameSR);
      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+=RC*numberBytes; p=ip.p; }
          }
	        numBlocksAllocated=numNew; numSquaresAllocated=numNewSquares;
        }
      } else freeStore(allocated);
    }
  }
  if (ok) { //x64
    Uint64 numNew64=numNew,
           total=(numNew64*pointerSize)+                   // blocks (ptrs)
                   (numNew64*squaresPerBlock*squareSize)+  // blocks (guts)
                   (numNew64*squaresPerBlock*pointerSize); // squares (ptrs)
    printf("total allocated %s bytes\n", formatN(total));
  } else reportError(storeAllocFail);
  return ok;
} // allocateStore
//================================================ 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

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

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

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

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

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

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

FILE *openInput(char buf[bufSize]) {
//    ---------
  char *rFname=NULL; FILE *rfp=NULL;
  do {
    printf("\nEnter the name of the %ss 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, buf, "r")!=0) {
      const int msgSize=bufSize+50; char msg[msgSize];
      sprintf_s(msg, msgSize, "\a\nCan't open for read %s", buf); perror(msg);
    }
  }
  return rfp;
} // openInput

enum readStatus { readAllocFail, readError, readOORM, readOORP, readOK } ;
const char *readErrorMsg="\a\nError reading from file.\n";
readStatus readSquaresSbyte(FILE *rfp, const int incr) {
//         ----------------
  do {
    if ((sIndex==numSquaresAllocated)&&
       (!allocateStore(numBlocksAllocated+numBlocksAllocated))) return readAllocFail;
    int rv=0; Sbyte *p=(Sbyte *)squares[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==squares[--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==squares[--sIndex])) return readOK;
          else { printf(readErrorMsg); return readError; } count+=incr; p+=incr; 
      } 
    }
  } while (T); return readOK;
} // readSquaresSbyte

readStatus readSquaresUbyte(FILE *rfp, const int incr) {
//         ----------------
  do {
    if ((sIndex==numSquaresAllocated)&&
       (!allocateStore(numBlocksAllocated+numBlocksAllocated))) return readAllocFail;
    int rv=0; Ubyte *p=(Ubyte *)squares[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==squares[--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==squares[--sIndex])) return readOK;
          else { printf(readErrorMsg); return readError; } count+=incr; p+=incr; 
      } 
    }
  } while (T); return readOK;
} // readSquaresUbyte

readStatus readSquaresSshort(FILE *rfp, const int incr) {
//         -----------------
  do {
    if ((sIndex==numSquaresAllocated)&&
      (!allocateStore(numBlocksAllocated+numBlocksAllocated))) return readAllocFail;
    int rv=0; Sshort *p=(Sshort *)squares[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==squares[--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==squares[--sIndex])) return readOK;
          else { printf(readErrorMsg); return readError; } count+=incr; p+=incr; 
      }
    }
  } while (T); return readOK;
} // readSquaresSshort

readStatus readSquaresUshort(FILE *rfp, const int incr) {
//         -----------------
  do {
    if ((sIndex==numSquaresAllocated)&&
       (!allocateStore(numBlocksAllocated+numBlocksAllocated))) return readAllocFail;
    int rv=0; Ushort *p=(Ushort *)squares[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==squares[--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==squares[--sIndex])) return readOK;
          else { printf(readErrorMsg); return readError; } count+=incr; p+=incr; 
      }
    }
  } while (T); return readOK;
} // readSquaresUshort

readStatus readSquaresInt(FILE *rfp, const int incr) {
//         --------------
  do {
    if ((sIndex==numSquaresAllocated)&&
       (!allocateStore(numBlocksAllocated+numBlocksAllocated))) return readAllocFail;
    int rv=0; int *p=(int *)squares[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==squares[--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==squares[--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 %s numbers as ", nameSR);
  printf("\n%s%s\n", startMsg, numberBytes==byteBytes ? "unsigned bytes"
        : numberBytes==shortBytes ? "unsigned shorts" : "signed ints");
  int incr=0; for (int i=7; i>1; i--) if ((C%i)==0) { incr=i; break; }  
  readStatus rc=readError; bool signedNums=signedNumbers;
  do {
    enum retry_t {tryNone, trySbyte, tryUbyte, trySshort, tryUshort, tryInt}; retry_t retry=tryNone;
    switch (numberBytes) {
      case 1:
        if (signedNums) { rc=readSquaresSbyte(rfp, incr);
          if ((rc==readOORM)|(rc==readOORP)) {
            if ((smallestRead<SHRT_MIN)|(biggestRead>SHRT_MAX)) { retry=tryInt; numberBytes=intBytes;
            } else { retry=trySshort; numberBytes=shortBytes; }
          }
        } else { rc=readSquaresUbyte(rfp, incr);
          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);
          if ((rc==readOORM)|(rc==readOORP)) { retry=tryInt; numberBytes=intBytes; }
        } else { rc=readSquaresUshort(rfp, incr);
          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); break;
      default: caseError("readSquares", numberBytes); return F;
    }
    if (retry==tryNone) break;
    else {
      printf("number out of range\n"); freeStore(numBlocksAllocated);
      free(squares); squares=NULL; 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);
  if ((rc==readError)&(sIndex>1)) { --sIndex; rc=readOK; } return rc==readOK;
} // readSquares
//============================================= sort ============================================

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

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

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

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

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

void sortSquaresAscending() {
//   --------------------
  printf(".. sorting %ss\n", nameSR);
  switch (numberBytes) {
    case 1:
      if (signedNumbers) qsort(squares, sIndex, pointerSize, cmpSquaresSbyteA);
      else qsort(squares, sIndex, pointerSize, cmpSquaresUbyteA); break;
    case 2:
      if (signedNumbers) qsort(squares, sIndex, pointerSize, cmpSquaresSshortA);
      else qsort(squares, sIndex, pointerSize, cmpSquaresUshortA); break;
    case 4: qsort(squares, sIndex, pointerSize, cmpSquaresIntA); break;
    default: caseError("sortSquaresAscending", numberBytes);
  }
} // sortSquaresAscending

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

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

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

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

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

void sortSquaresDecending() {
//   --------------------
  printf(".. sorting %ss\n", nameSR);
  switch (numberBytes) {
    case 1:
      if (signedNumbers) qsort(squares, sIndex, pointerSize, cmpSquaresSbyteD);
      else qsort(squares, sIndex, pointerSize, cmpSquaresUbyteD); break;
    case 2:
      if (signedNumbers) qsort(squares, sIndex, pointerSize, cmpSquaresSshortD);
      else qsort(squares, sIndex, pointerSize, cmpSquaresUshortD); break;
    case 4: qsort(squares, sIndex, pointerSize, cmpSquaresIntD); break;
    default: caseError("sortSquaresDecending", numberBytes);
  }
} // sortSquaresDecending

typedef int(*t_icvpcvp)(const void *, const void *); typedef void(*t_v)();
t_v sortSquares[]={ NULL, sortSquaresAscending, sortSquaresDecending };
t_icvpcvp cmpSquaresSbyte[]= { NULL, cmpSquaresSbyteA,  cmpSquaresSbyteD },
          cmpSquaresUbyte[]= { NULL, cmpSquaresUbyteA,  cmpSquaresUbyteD },
          cmpSquaresSshort[]={ NULL, cmpSquaresSshortA, cmpSquaresSshortD },
          cmpSquaresUshort[]={ NULL, cmpSquaresUshortA, cmpSquaresUshortD },
          cmpSquaresInt[]=   { NULL, cmpSquaresIntA,    cmpSquaresIntD };
//============================================= 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

int openOutput(char *inFname) {
//  ----------
  int wfd=noFD; int sub=0; const int baseSize=bufSize+25; char baseName[baseSize], buf[outSize];
  stripName(inFname, buf); sprintf_s(baseName, baseSize, "%sSorted%c", buf, aord==sortUp?'A':'D');
  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 (_sopen_s(&wfd, buf, _O_CREAT|_O_WRONLY, _SH_DENYNO, _S_IWRITE)==0) {
     printf(".. writing %ss to file %s\n", nameSR, buf);
  } else {
    char msg[outSize+50]; sprintf_s(msg, outSize+50, "\a\nCan't open for write %s", buf); perror(msg);
  }
  return wfd;
} // 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 sprintFW11 */ return neg ? width+1 : width;
} // fieldWidth

typedef void (*t_sprintFW)(char *s, const int i);
void sprintFW1 (char *s, const int i) { sprintf_s(s,  2, "%1d",  i); }
void sprintFW2 (char *s, const int i) { sprintf_s(s,  3, "%2d",  i); }
void sprintFW3 (char *s, const int i) { sprintf_s(s,  4, "%3d",  i); }
void sprintFW4 (char *s, const int i) { sprintf_s(s,  5, "%4d",  i); }
void sprintFW5 (char *s, const int i) { sprintf_s(s,  6, "%5d",  i); }
void sprintFW6 (char *s, const int i) { sprintf_s(s,  7, "%6d",  i); }
void sprintFW7 (char *s, const int i) { sprintf_s(s,  8, "%7d",  i); }
void sprintFW8 (char *s, const int i) { sprintf_s(s,  9, "%8d",  i); }
void sprintFW9 (char *s, const int i) { sprintf_s(s, 10, "%9d",  i); }
void sprintFW10(char *s, const int i) { sprintf_s(s, 11, "%10d", i); }
void sprintFW11(char *s, const int i) { sprintf_s(s, 12, "%11d", i); }
static t_sprintFW sprintFW[]={
  NULL,      sprintFW1, sprintFW2, sprintFW3, sprintFW4, sprintFW5,
  sprintFW6, sprintFW7, sprintFW8, sprintFW9, sprintFW10, sprintFW11
};

int writeSquaresSbyte(const int wfd, const int fw) {
//  -----------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresSbyte[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
      int i=0, colCount=0; Sbyte *p=((Sbyte **)squares)[sq];
      if (squareBytes>writeSize) {
        while (i++<RC) {
          if ((outBytes+fw+2)>=writeSize) {
            if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
          }
          sprintFW[fw](s, *p++); s+=fw;
          if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' '; outBytes+=fw+1;
        }
        *s++='\n'; ++outBytes;
      } else {
        if ((outBytes+squareBytes)>=writeSize) {
          if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
        }
        while (i++<RC) {
          sprintFW[fw](s, *p++); s+=fw; if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';         
        }
        *s++='\n'; outBytes+=squareBytes;
      }
    }
  }
  return countDuplicates;     
} // writeSquaresSbyte

int writeSquaresUbyteFW3(const int wfd) {
//  --------------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresUbyte[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
      if ((outBytes+squareBytes)>=writeSize) {
        if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
      }
      int i=0, colCount=0; Ubyte *p=((Ubyte **)squares)[sq];
      while (i++<RC) { int w=*p++;
        if (w<10) { *s++=' '; *s++=' '; *s++='0'+w;
        } else {
          int v=w/10;
          if (w<100) { *s++=' '; w-=v*10; } else { const int u=v/10;  w-=v*10; v-=u*10; *s++='0'+u; }
          *s++='0'+v; *s++='0'+ w;
        }
        if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';
      }
      *s++='\n'; outBytes+=squareBytes;
    }
  }
  return countDuplicates;     
} // writeSquaresUbyteFW3

int writeSquaresUbyteFW2(const int wfd) {
//  --------------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresUbyte[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
      if ((outBytes+squareBytes)>=writeSize) {
        if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
      }
      int i=0, colCount=0; Ubyte *p=((Ubyte **)squares)[sq];
      while (i++<RC) {
        int v=*p++;
        if (v<10) { *s++=' '; *s++='0'+v; } else { const int u=v/10; v-=u*10; *s++='0'+u; *s++='0'+ v; }
        if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';
      }
      *s++='\n'; outBytes+=squareBytes;
    }
  }
  return countDuplicates;     
} // writeSquaresUbyteFW2

int writeSquaresLT10(const int wfd) {
//  ----------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresUbyte[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
      if ((outBytes+squareBytes)>=writeSize) {
        if (_write(wfd, outBuffer, outBytes)!=outBytes) return -1; outBytes=0; s=outBuffer;
      }
      int i=0, colCount=0; Ubyte *p=((Ubyte **)squares)[sq];
      while (i++<RC) {
        const int v=*p++; *s++='0'+v; if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';
      }
      *s++='\n'; outBytes+=squareBytes;
    }
  }
  return countDuplicates;     
} // writeSquaresLT10

int writeSquaresLT20(const int wfd) {
//  ----------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresUbyte[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
      if ((outBytes+squareBytes)>=writeSize) {
        if (_write(wfd, outBuffer, outBytes)!=outBytes) return -1; outBytes=0; s=outBuffer;
      }
      int i=0, colCount=0; Ubyte *p=((Ubyte **)squares)[sq];
      while (i++<RC) {
        const int v=*p++;
        if (v<10) { *s++=' '; *s++='0'+v; } else { *s++='1'; *s++='0'-10+v; }
        if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';
      }
      *s++='\n'; outBytes+=squareBytes;
    }
  }
  return countDuplicates;     
} // writeSquaresLT20

int writeSquaresLT30(const int wfd) {
//  ----------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresUbyte[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
      if ((outBytes+squareBytes)>=writeSize) {
        if (_write(wfd, outBuffer, outBytes)!=outBytes) return -1; outBytes=0; s=outBuffer;
      }
      int i=0, colCount=0; Ubyte *p=((Ubyte **)squares)[sq];
      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'; outBytes+=squareBytes;
    }
  }
  return countDuplicates;     
} // writeSquaresLT30

int writeSquaresUbyte(const int wfd, const int fw) {
//  -----------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresUbyte[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
      int i=0, colCount=0; Ubyte *p=((Ubyte **)squares)[sq];
      if (squareBytes>writeSize) {
        while (i++<RC) {
          if ((outBytes+fw+2)>=writeSize) {
            if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
          }
          sprintFW[fw](s, *p++); s+=fw;
          if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' '; outBytes+=fw+1;
        }
        *s++='\n'; ++outBytes;
      } else {
        if ((outBytes+squareBytes)>=writeSize) {
          if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
        }
        while (i++<RC) {
          sprintFW[fw](s, *p++); s+=fw; if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';         
        }
        *s++='\n'; outBytes+=squareBytes;
      }
    }
  }
  return countDuplicates;     
} // writeSquaresUbyte

int writeSquaresSshort(const int wfd, const int fw) {
//  ------------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresSshort[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
      int i=0, colCount=0; Sshort *p=((Sshort **)squares)[sq];
      if (squareBytes>writeSize) {
        while (i++<RC) {
          if ((outBytes+fw+2)>=writeSize) {
            if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
          }
          sprintFW[fw](s, *p++); s+=fw;
          if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' '; outBytes+=fw+1;
        }
        *s++='\n'; ++outBytes;
      } else {
        if ((outBytes+squareBytes)>=writeSize) {
          if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
        }
        while (i++<RC) {
          sprintFW[fw](s, *p++); s+=fw; if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';         
        }
        *s++='\n'; outBytes+=squareBytes;
      }
    }
  }
  return countDuplicates;
} // writeSquaresSshort

int writeSquaresUshortFW3(const int wfd) {
//  ---------------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresUshort[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
      if ((outBytes+squareBytes)>=writeSize) {
        if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
      }
      int i=0, colCount=0; Ushort *p=((Ushort **)squares)[sq];
      while (i++<RC) {
        int w=*p++;
        if (w<10) { *s++=' '; *s++=' '; *s++='0'+w; } else { int v=w/10;
          if (w<100) { *s++=' '; w-=v*10; } else { const int u=v/10; w-=v*10; v-=u*10; *s++='0'+u; }
          *s++='0'+v; *s++='0'+ w;
        }
        if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';
      }
      *s++='\n'; outBytes+=squareBytes;
    }
  }
  return countDuplicates;     
} // writeSquaresUshortFW3

int writeSquaresUshort(const int wfd, const int fw) {
//  ------------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresUshort[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
     int i=0, colCount=0; Ushort *p=((Ushort **)squares)[sq];
      if (squareBytes>writeSize) {
        while (i++<RC) {
          if ((outBytes+fw+2)>=writeSize) {
            if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
          }
          sprintFW[fw](s, *p++); s+=fw;
          if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' '; outBytes+=fw+1;
        }
        *s++='\n'; ++outBytes;
      } else {
        if ((outBytes+squareBytes)>=writeSize) {
          if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
        }
        while (i++<RC) {
          sprintFW[fw](s, *p++); s+=fw; if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';         
        }
        *s++='\n'; outBytes+=squareBytes;
      }
    }
  }
  return countDuplicates;
} // writeSquaresUshort

int writeSquaresInt(const int wfd, const int fw) {
//  ---------------
  int countDuplicates=0; char *s=outBuffer;
  for (int sq=0; sq<sIndex; sq++) {
    if ((sq>0)&&(cmpSquaresInt[aord](&squares[sq-1], &squares[sq])==0)) countDuplicates++;
    else {
     int i=0, colCount=0; int *p=((int **)squares)[sq];
      if (squareBytes>writeSize) {
        while (i++<RC) {
          if ((outBytes+fw+2)>=writeSize) {
            if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
          }
          sprintFW[fw](s, *p++); s+=fw;
          if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' '; outBytes+=fw+1;
        }
        *s++='\n'; ++outBytes;
      } else {
        if ((outBytes+squareBytes)>=writeSize) {
          if (_write(wfd, outBuffer, outBytes)!=outBytes) return writeErr; outBytes=0; s=outBuffer;
        }
        while (i++<RC) {
          sprintFW[fw](s, *p++); s+=fw; if (++colCount==C) { *s++='\n'; colCount=0; } else *s++=' ';         
        }
        *s++='\n'; outBytes+=squareBytes;
      }
    }
  }
  return countDuplicates;
} // writeSquaresInt

bool writeSquares(const int wfd) {
//   ------------
  int countDuplicates=0;
  const int sW=fieldWidth(smallestRead), bW=fieldWidth(biggestRead), fw=sW>bW ? sW : bW;
  squareBytes=RC*(fw+1)+1; outBytes=0;
  switch (numberBytes) {
    case 1:
      if (signedNumbers) countDuplicates=writeSquaresSbyte(wfd, fw);
      else {
        if (squareBytes<=writeSize) {
          if (fw==1) countDuplicates=writeSquaresLT10(wfd);
          else if (fw==2) {
            if (biggestRead<20) countDuplicates=writeSquaresLT20(wfd);
            else if (biggestRead<30) countDuplicates=writeSquaresLT30(wfd);
            else countDuplicates=writeSquaresUbyteFW2(wfd);
          } else if (fw==3) countDuplicates=writeSquaresUbyteFW3(wfd);
          else countDuplicates=writeSquaresUbyte(wfd, fw);
        } else countDuplicates=writeSquaresUbyte(wfd, fw);
      }
      break;
    case 2:
      if (signedNumbers) countDuplicates=writeSquaresSshort(wfd, fw);
      else {
        if ((squareBytes<=writeSize)&(fw==3)) countDuplicates=writeSquaresUshortFW3(wfd);
        else countDuplicates=writeSquaresUshort(wfd, fw);
      }
      break;
    case 4: countDuplicates=writeSquaresInt(wfd, fw); break;
    default: caseError("writeSquares", numberBytes); return F;
  }
  if ((outBytes!=0)&&(_write(wfd, outBuffer, outBytes)!=outBytes)) return F;
  if (countDuplicates>0) printf("\nNumber of %ss %d minus %d duplicates: %d.\n",
                           nameSR, sIndex, countDuplicates, sIndex-countDuplicates);
  else if (countDuplicates==0) printf("\nNumber of %ss: %d.\n", nameSR, sIndex);
  return countDuplicates>=0;
} // writeSquares
//============================================ 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) {
//   -----------------
  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? ", sortUp, sortName[sortUp], sortDown, sortName[sortDown] );
  aord=getInt(); if (aord<sortUp) aord=sortUp; if (aord>sortDown) aord=sortDown; return aord;
} // getUpDown

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 *input_order="\nSquare order? ", 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)) {
          fclose(rfp); sortSquares[getUpDown()](); const int wfd=openOutput(buf);
          if (wfd!=noFD) {writeError=!writeSquares(wfd); _close(wfd); }
          if (!writeError) ok=T; // at least one success loop
        }
        freeStore(numBlocksAllocated); free(squares); squares=NULL;
      }
      reportElapsedTime(startTime);
    }
    if (writeError) perror("\a\nError writing file"); 
    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