/*
 *  File:    Complement.cpp
 *  Author:  S Harry White
 *  Created: 2011-07-31
 *  Updated: 2021-12-17
 *    Strip input name to place output files in current folder. Tidy code.
 *  Updated: 2022-07-26
 *    Add support for non-square rectangles.
 *  Updated: 2023-01-24
 *    Tidy code with procedure check_txt.
 *  Updated: 2023-02-03
 *    Change to compile with g++ for macOS.
 *  Updated: 2023-03-28
 *   Fix store allocation. Remove allocatedSize=0 from initGlobals().
 */

/*
 * Makes complementary rectangles, replacing each number with its complementary number.
 * The complementary pair sum used is "the smallest number" + "the biggest number" in the rectangle.
 * This accomodates magic as well as other number rectangles including those that contain repeated numbers.
*/

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

#define min(a,b) (((a)<(b))?(a):(b))
const int startSize=1000, bufSize=1024, outSize=bufSize+50; const bool F=false, T=true;
bool readError, bell;
int R, C, Rm1, Cm1, Rm1C, Rm2, Rm2C, RC, RCm1, RCmC, RCmCm1, RCmCp1, RCm2, CpCm1,
    allocatedSize, numSquares, numSelfComplement, fieldWidth,
    smallestRead, biggestRead, complementSum, printNum, // print a message at this number of squares
    *iSquare=NULL, *oSquare=NULL, *tmpSquare=NULL, *iFrenicle=NULL, *oFrenicle=NULL;
char selfComplementFN[outSize];

void initGlobals() {
//  -----------
  Rm1=R-1; Cm1=C-1; Rm1C=Rm1*C; Rm2=R-2; Rm2C=Rm2*C; RC=R*C; RCm1=RC-1; RCmC=RC-C;
  RCmCm1=RCmC-1; RCmCp1=RCmC+1; RCm2=RC-2; CpCm1=C+C-1;
  numSquares=0; numSelfComplement=0; printNum=100000; readError=F; bell=T;
} // initGlobals

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

void freeSquares() {
//   -----------
  if (iSquare!=NULL) { free(iSquare); iSquare=NULL; }
  if (oSquare!=NULL) { free(oSquare); oSquare=NULL; }
  if (tmpSquare!=NULL) { free(tmpSquare); tmpSquare=NULL; }
  if (iFrenicle!=NULL) { free(iFrenicle); iFrenicle=NULL; }
  if (oFrenicle!=NULL) { free(oFrenicle); oFrenicle=NULL; }
  allocatedSize=0;
} // freeSquares

bool allocateSquare(int** square, const int size) {
//   --------------
  *square=(int*) malloc(size*sizeof(int)); return *square!=NULL;
} // allocateSquare 

bool allocateSquares() {
//   ---------------
  bool ok=T; int size=RC; if (size<startSize) size=startSize;
  if (size>allocatedSize) {
    freeSquares(); 
    if ((ok=allocateSquare(&iSquare, size)))
      if ((ok=allocateSquare(&oSquare, size)))
        if ((ok=allocateSquare(&tmpSquare, size)))
          if ((ok=allocateSquare(&iFrenicle, size)))
            if ((ok=allocateSquare(&oFrenicle, size))) allocatedSize=size;
    if (!ok) { freeSquares(); reportError(storeAllocFail); }
  }
  return ok;
} //allocateSquares
//=================================================== 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 getInts(int *p, int *q, int c) {
//   -------
  bool ok=F; *p=0; *q=0; if (c<0) do { c=getchar(); } while ((c==' ')|(c=='\t'));
  if ( ('1'<=c)&(c<='9') ) {
    int i=c-'0'; while ( ('0'<=(c=getchar()))&&(c<='9') ) i=i*10+c-'0'; *p=i;
    if ((c==' ')|(c=='\t')) {
      do { c=getchar(); } while ((c==' ')|(c=='\t'));
      if ( ('1'<=c)&(c<='9') ) {
        int i=c-'0'; while ( ('0'<=(c=getchar()))&&(c<='9') ) i=i*10+c-'0'; *q=i;
      }
    }
    if (*q==0) *q=*p; ok=T;
  }  
  clearLine(c); if (!ok) return reportError("Invalid input"); return T;
} // getInts

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

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

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

FILE *openInput(char buf[bufSize]) {
//    ---------
  char *rFname=NULL; FILE *rfp=NULL;
  do {
    printf("\nEnter the name of the %ss file: ", R==C ? "square" : "rectangle");
    if (getFileName(buf, bufSize-6)) { 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) {
    adjustName(buf);
    if ((rfp=fopen(buf, "r"))==NULL) {
      const int msgSize=bufSize+50; char msg[msgSize];
      snprintf(msg, msgSize, "\a\nCan't open for read %s", buf); perror(msg);
    }
  }
  return rfp;
} // openInput

int getWidth(int i) {
//  --------
  bool isSigned=i<0; int width=1; while ((i=i/10)!=0) ++width; if (width>10) width=10;
  return isSigned ? width+1 : width;
} // getWidth

bool readSquare(FILE *rfp) {
//   ----------
  int smallest=INT_MAX, biggest=INT_MIN;
  for (int i=0; i<RC; i++) {
    int tmp, rv;
    if ( (rv=fscanf(rfp, "%d", &tmp))==1) {
      if (tmp<smallest) smallest=tmp; if (tmp>biggest) biggest=tmp; iSquare[i]=tmp;
    } else {
      if ( (rv!=EOF)|(i!=0)) { readError=T; printf("\a\nError reading from file.\n"); }
      return F;
    }
  }
  const int sWidth=getWidth(smallest), bWidth=getWidth(biggest);
  fieldWidth=sWidth>bWidth ? sWidth : bWidth;
  smallestRead=smallest; biggestRead=biggest; complementSum=biggestRead+smallestRead;
  return T;
} // readSquare
//=================================================== 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(obuf, s); 
} // stripName

FILE *openOutput(const char *inFname, const bool selfComplement) {
//    ----------
  FILE *wfp=NULL; int sub=0; const int baseSize=bufSize+25; char baseName[baseSize], buf[outSize];
  snprintf(baseName, baseSize, "./%s%s", inFname, selfComplement ? "SelfComplement" : "Complement");
  snprintf(buf, outSize, "%s.txt", baseName);
  do {
    if (access(buf, F_OK)!=0) break;
    snprintf(buf, outSize, "%s_%d.txt", baseName, ++sub);
  } while (T);
  if ((wfp=fopen(buf, "w"))==NULL) {
    char msg[outSize+50];
    snprintf(msg, outSize+50, "\a\nCan't open for write %s", buf); perror(msg);
  } else {
    if (selfComplement) strcpy(selfComplementFN, buf);
    else printf(".. writing %ss to file %s\n", R==C ? "square" : "rectangle", buf);
  }
  return wfp;
} // openOutput

void writeNumberOfSquares(const char *s, const int num) {
//   --------------------
  const int msq=num/1000000, tsq=num%1000000/1000, rsq=num%1000;
  if (msq==0) if (tsq==0) printf("%s %d", s, rsq); else printf("%s %d,%03d", s, tsq, rsq);
  else printf("%s %d,%03d,%03d", s, msq, tsq, rsq);
} // writeNumberOfSquares

bool writeSquare1(FILE *wfp, int *square) {
//   ------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%1d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare1

bool writeSquare2(FILE *wfp, int *square) {
//   ------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%2d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare2

bool writeSquare3(FILE *wfp, int *square) {
//   ------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%3d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare3

bool writeSquare4(FILE *wfp, int *square) {
//   ------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%4d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare4

bool writeSquare5(FILE *wfp, int *square) {
//   ------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%5d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare5

bool writeSquare6(FILE *wfp, int *square) {
//   ------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%6d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare6

bool writeSquare7(FILE *wfp, int *square) {
//   ------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%7d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare7

bool writeSquare8(FILE *wfp, int *square) {
//   ------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%8d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare8

bool writeSquare9(FILE *wfp, int *square) {
//   ------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%9d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare9

bool writeSquare10(FILE *wfp, int *square) {
//   -------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%10d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare10

bool writeSquare11(FILE *wfp, int *square) {
//   -------------
  int c, count=0;
  for (int i=0; i<RC; i++) {
    if (fprintf(wfp, "%11d", square[i])<0) return F;
    if (++count==C) { c='\n'; count=0; } else c=' ';
    if (fputc(c, wfp)==EOF) return F;  
  }
  return T;
} // writeSquare11

typedef bool (*t_Write)(FILE *wfp, int *square);
static t_Write writeSquareFW[] =
  { NULL,         writeSquare1, writeSquare2, writeSquare3, writeSquare4,  writeSquare5,
    writeSquare6, writeSquare7, writeSquare8, writeSquare9, writeSquare10, writeSquare11 };

bool writeSquareOrder5(FILE *wfp, int *square) {
//   -----------------
  char squareString[100], *s=squareString; int count=0;
  for (int i=0; i<RC; i++) {
    const int v=square[i];
    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 (++count==C) { *s++='\n'; count=0; } else *s++=' ';
  }
  *s++='\n'; *s++='\0'; return fputs(squareString, wfp)!=EOF;   
} // writeSquareOrder5

bool writeComplementSquare(FILE *wfp) {
//   ---------------------
  if (++numSquares==printNum) { writeNumberOfSquares("..", numSquares); putchar('\n'); printNum+=printNum; }
  if ((R==5)&(C==5)&(smallestRead>=0)&(biggestRead<=RC)) { if (!writeSquareOrder5(wfp, oSquare)) return F; }
  else { if (!writeSquareFW[fieldWidth](wfp, oSquare)) return F; if (fputc('\n', wfp)==EOF) return F; }
  return T;
} // writeComplementSquare

bool writeSelfComplementSquare(FILE *wfp) {
//   -------------------------
  if (numSelfComplement==0)
    printf(".. writing self-complement %ss to file %s\n", R==C ? "square" : "rectangle", selfComplementFN);

  ++numSelfComplement;
  if ((R==5)&(C==5)&(smallestRead>=0)&(biggestRead<=RC)) { if (!writeSquareOrder5(wfp, iSquare)) return F; }
  else { if (!writeSquareFW[fieldWidth](wfp, iSquare)) return F; if (fputc('\n', wfp)==EOF) return F; }
  return T;
} // writeSelfComplementSquare

//void consolePrint(int *x) { writeSquareFW[fieldWidth](stdout, x); putchar('\n'); }
//   ------------
//==================================================== make =================================================

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

bool rotate(int *p, const int corner) { // R==C
//   ------
  switch (corner) {
    case 0: // NW
      for (int r=0; r<R; r++) for (int c=r+1; c<R; c++) {
        const int i=r*R+c, j=c*R+r; if (p[i]==p[j]) continue; return p[i]<p[j];
      } break;
    case 1: // NE
      for (int r=0; r<R; r++) for (int c=Rm2-r; c>=0; c--) {
        const int i=r*R+c, j=R*(R-c)-r-1; if (p[i]==p[j]) continue; return p[i]>p[j];
      } break;
    case 2: // SE
      for (int r=Rm1; r>=0; r--) for (int c=r-1; c>=0; c--) {
        const int i=r*R+c, j=c*R+r; if (p[i]==p[j]) continue; return p[i]<p[j];
      } break;
    case 3: // SW
      for (int r=Rm1; r>= 0; r--) for (int c=R-r; c<R; c++) {
        const int i=r*R+c, j=R*(R-c)-r-1; if (p[i]==p[j]) continue; return p[i]>p[j];
      } break;
    default: break;
  }
  return T;
} // rotate

bool uniqueCorner(int *p, int *q, const int minC) { // R==C
//   ------------
  int *x=NULL, *min=tmpSquare, numMinCorners=0; bool prev=F;
  if (p[0]   ==minC) ++numMinCorners; if (p[Rm1] ==minC) ++numMinCorners;
  if (p[RCm1]==minC) ++numMinCorners; if (p[RCmC]==minC) ++numMinCorners;

  if (numMinCorners>1) {
    if (p[0]==minC) { prev=T;
      if ((p[1]<p[R])||((p[1]==p[R])&&rotate(p, 0)))
        for (int i=0; i<RC; i++) min[i]=p[i]; // in Frenicle form
      else                   
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) min[c*R+r]=p[r*R+c]; // reflect in YX
    }
    if (p[Rm1]==minC) { x=prev ? q : min;
      if ((p[CpCm1]<p[Rm2])||((p[CpCm1]==p[Rm2])&&rotate(p, 1)))
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) x[(Rm1-c)*R+r]=p[r*R+c];  // rotate -90
      else for (int r=0; r<R; r++) for (int c=0; c<R; c++) x[r*R+Rm1-c]=p[r*R+c]; // reflect in Y
      if (prev) { if (cmpSquares(q, min)<0) for (int i=0; i<RC; i++) min[i]=q[i]; } else prev=T;
    }
    if (p[RCm1]==minC) { x=prev ? q : min;
      if ((p[RCm2]<p[RCmCm1])||((p[RCm2]==p[RCmCm1])&&rotate(p, 2)))
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) x[(Rm1-r)*R+Rm1-c]=p[r*R+c];    // rotate 180
      else for (int r=0; r<R; r++) for (int c=0; c<R; c++) x[(Rm1-c)*R+Rm1-r]=p[r*R+c]; // reflect in XY
      if (prev) { if (cmpSquares(q, min)<0) for (int i=0; i<RC; i++) min[i]=q[i]; } else prev=T;
    }
    if (p[RCmC]==minC ) {
      if ((p[Rm2C]<p[RCmCp1])||((p[Rm2C]==p[RCmCp1])&&rotate(p, 3)))
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) q[c*R+Rm1-r]=p[r*R+c];      // rotate 90
      else for (int r=0; r<R; r++) for (int c=0; c<R; c++) q[(Rm1-r)*R+c]=p[r*R+c]; // reflect in X
      if (cmpSquares(q, min)<0) for (int i=0; i<RC; i++) min[i]=q[i];
    }
    for (int i=0; i<RC; i++) q[i]=min[i]; return F;
  }
  return T;
} // uniqueCorner

void putInFrenicleForm(int *p, int *q) {
//   -----------------
  const int min1=min(p[0], p[Rm1]), min2=min(p[RCmC], p[RCm1]), minC=min(min1, min2);

  if (uniqueCorner(p, q, minC)) {
    if (p[0]==minC) {
      if ((p[1]<p[R])||((p[1]==p[R])&&rotate(p, 0)))
        for (int i=0; i<RC; i++) q[i]=p[i]; // in Frenicle form
      else                   
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) q[c*R+r]=p[r*R+c]; // reflect in YX
    } else if (p[Rm1]==minC) {
      if ((p[CpCm1]<p[Rm2])||((p[CpCm1]==p[Rm2])&&rotate(p, 1)))
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) q[(Rm1-c)*R+r]=p[r*R+c]; // rotate -90
      else  
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) q[r*R+Rm1-c]=p[r*R+c];   // reflect in Y  
    } else if (p[RCm1]==minC) {
      if ((p[RCm2]<p[RCmCm1])||((p[RCm2]==p[RCmCm1])&&rotate(p, 2)))
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) q[(Rm1-r)*R+Rm1-c]=p[r*R+c]; // rotate 180
      else
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) q[(Rm1-c)*R+Rm1-r]=p[r*R+c]; // reflect in XY
    } else { // p[RCmC]==minC 
      if ((p[Rm2C]<p[RCmCp1])||((p[Rm2C]==p[RCmCp1])&&rotate(p, 3)))
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) q[c*R+Rm1-r]=p[r*R+c];   // rotate 90
      else                         
        for (int r=0; r<R; r++) for (int c=0; c<R; c++) q[(Rm1-r)*R+c]=p[r*R+c]; // reflect in X
    }
  }
} // putInFrenicleForm

bool uniqueCornerRect(int *p, int *q, const int minC) { // R!=C
//   ----------------
  int *x=NULL, *min=tmpSquare, numMinCorners=0; bool prev=F;
  if (p[0]   ==minC) ++numMinCorners; if (p[Cm1] ==minC) ++numMinCorners;
  if (p[RCm1]==minC) ++numMinCorners; if (p[RCmC]==minC) ++numMinCorners;

  if (numMinCorners>1) {
    if (p[0]==minC) { for (int i=0; i<RC; i++) min[i]=p[i]; prev=T; }
    if (p[Cm1]==minC) { x=prev ? q : min;
      for (int r=0; r<R; r++) for (int c=0; c<C; c++) x[r*C+Cm1-c]=p[r*C+c]; // reflect in Y
      if (prev) { if (cmpSquares(q, min)<0) for (int i=0; i<RC; i++) min[i]=q[i]; } else prev=T;
    }
    if (p[RCm1]==minC) { x=prev ? q : min;
      for (int r=0; r<R; r++) for (int c=0; c<C; c++) x[(Rm1-r)*C+Cm1-c]=p[r*C+c]; // rotate 180
      if (prev) { if (cmpSquares(q, min)<0) for (int i=0; i<RC; i++) min[i]=q[i]; } else prev=T;
    }
    if (p[RCmC]==minC ) {
      for (int r=0; r<R; r++) for (int c=0; c<C; c++) q[(Rm1-r)*C+c]=p[r*C+c]; // reflect in X 
      if (cmpSquares(q, min)<0) for (int i=0; i<RC; i++) min[i]=q[i];
    }
    for (int i=0; i<RC; i++) q[i]=min[i]; return F;
  }
  return T;
} // uniqueCornerRect

void rotateRectangle(int *p, int *q) {
//   ---------------
  const int min1=min(p[0], p[Cm1]), min2=min(p[RCmC], p[RCm1]), minC=min(min1, min2);
  if (uniqueCornerRect(p, q, minC)) {
    if (p[0]==minC) { for (int i=0; i<RC; i++) q[i]=p[i]; }
    else if (p[Cm1]==minC) {
      for (int r=0; r<R; r++) for (int c=0; c<C; c++) q[r*C+Cm1-c]=p[r*C+c]; }       // reflect in Y  
    else if (p[RCm1]==minC) {
      for (int r=0; r<R; r++) for (int c=0; c<C; c++) q[(Rm1-r)*C+Cm1-c]=p[r*C+c]; } // rotate 180
    else { // p[RCmC]==minC                        
      for (int r=0; r<R; r++) for (int c=0; c<C; c++) q[(Rm1-r)*C+c]=p[r*C+c]; }     // reflect in X
  }
} // rotateRectangle

void orientRectangle(int *p, int *q) { if (R==C) putInFrenicleForm(p, q); else rotateRectangle(p, q); }
//   ---------------

bool checkSelfComplement(FILE *wfpSS) {
//   -------------------
  orientRectangle(iSquare, iFrenicle); orientRectangle(oSquare, oFrenicle);
  if (cmpSquares(iFrenicle, oFrenicle)==0) return writeSelfComplementSquare(wfpSS); return T;
} // checkSelfComplement

bool complementSquare(FILE *wfp, FILE *wfpSS) {
//   ----------------
  for (int i=0; i<RC; i++) oSquare[i]=complementSum-iSquare[i];
  if (!writeComplementSquare(wfp)) return F; return checkSelfComplement(wfpSS);
} // complementSquare
//================================================= main ===============================================

bool validOrder() {
//   ----------
  if ((R<=0)|(C<=0)) { printf("\aERROR: Order must be positive.\n"); return F; } return T;
} // validOrder

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

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

int main() {
//  ----
  bool another=T, inputOrder=T, writeError=F, ok=F; char ibuf[bufSize]; allocatedSize=0;
  outputLocalTime();
  do {
    if (inputOrder) { printf("\nOrder? "); if (!getInts(&R, &C, -1)) break; }
    if (validOrder()) {
      initGlobals(); 
      if ( allocateSquares() ) {
        FILE *rfp=openInput(ibuf);
        if (rfp!=NULL) {
          time_t startTime=time(NULL);
          char obuf[bufSize]; stripName(ibuf, obuf); FILE *wfp=openOutput(obuf, F);
          if (wfp!=NULL) {
            FILE *wfpSS=openOutput(obuf, T);
            if (wfpSS!=NULL) {
              numSquares=0; numSelfComplement=0;
              while (readSquare(rfp)) {
                writeError=!complementSquare(wfp, wfpSS); if (writeError) break;
              }
              fclose(wfpSS);
              if (!writeError) { ok=T; char msg[50];
                snprintf(msg, 50, "\nNumber of %ss: ", R==C ? "square" : "rectangle");
                writeNumberOfSquares(msg, numSquares);
                writeNumberOfSquares(", number self-complement: ", numSelfComplement); putchar('\n');
                if (numSelfComplement==0) remove(selfComplementFN);
              }
            }
            fclose(wfp);
          }
          if (writeError) { perror("\n\aError writing file"); } else printElapsedTime(stdout, startTime);
          fclose(rfp);
        } // rfp!=NULL
      } // allocateSquares
    } // if (validOrder
    if (writeError) another=F;
    else {
      printf("\nAnother order? input y (yes), n (no) or the order: ");
      if (getYorInts(&R, &C)) inputOrder=(R<0); else another=F;
    }
    if (!another) freeSquares();
  } while (another);
  printf("\nHit return to close the console.");
  while (T) if (getchar()=='\n') break; return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main