/*
 * File:    ReversibleMost-perfect.cpp
 * Author:  S Harry White
 * Created: 2014-03-17
 * Updated: 2020-10-17
 *   Add option to create unique most-perfect squares: http://budshaw.ca/addenda/XFormNotes.html#rmpnotes
 *   With this option the most-perfect square and its reversible square aspect are outout.
 *  Updated: 2023-02-03
 *    Change to compile with g++ for macOS.
 * Updated: 2023-03-28
 *   Fix store allocation. Remove allocatedSize=0 from initGlobals().
 */

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

#define min(a,b) (((a)<(b))?(a):(b))
#define max(a,b) (((a)>(b))?(a):(b))
int N,               // The order of the square.
    M,               // N-1
    L,               // N-2
    G,               // N/2
    E,               // N/2-1 
    N3d4,            // 3*N/4
    N3m2d2,          // (3*N-2)/2
    NN,              // N*N
    NNm1,            // NN-1
    NNp1,            // NN+1

    magicConstant,   // 1 .. NN
    magicSum,        // 0 .. NN-1 or 1 .. NN
    Sum2,            //          "
    Sum4,            //          "
    allocatedSize, squareNum, squareType, fieldWidth, numMP, numR,
    **xSquare=NULL, **tSquare=NULL;
const int startSize=32, bufSize=1024, outSize=bufSize+50,
          sqOther=0, sqReversible=1, sqMostPerfect=2;
const bool F=false, T=true;
bool zeroBase, *numberUsed=NULL, readError, writeError, bell, unique; 

void initGlobals() {
//  -----------
    M=N-1; L=N-2; G=N/2; E=G-1; N3d4=3*N/4;
    N3m2d2=(3*N-2)/2; NN=N*N; NNm1=NN-1; NNp1=NN+1;
    numMP=0; numR=0; bell=T; unique=F; readError=F; writeError=F; 
    magicConstant=G*(NN+1); magicSum=magicConstant; Sum2=NNp1; Sum4=Sum2+Sum2;
    squareNum=0; zeroBase=F; fieldWidth=1; squareType=sqOther;
} // initGlobals
//======================================= store allocation ==========================================

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

bool programError(const char *s) {
//   ------------
  snprintf(fmt, bufSize,
    "%sProgram error: %s.\nPlease report by e-mail.\n", bell ? "\a" : "", s); 
  printf(fmt, squareNum); return bell=F;
} // programError

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

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

void freeStore() {
//   --------- 
  freeSquare(&xSquare, allocatedSize); freeSquare(&tSquare, allocatedSize);
  freeUsed(); allocatedSize=0;
} // freeStore

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

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

bool allocateStore(int size) {
//   -------------
  bool ok=T; if (size<startSize) size=startSize;
  if (size>allocatedSize) { freeStore();
    if ((ok=allocateSquare(&tSquare, size)))
      if ((ok=allocateSquare(&xSquare, size)))
        if ((ok=allocateUsed(size*size+1))) allocatedSize=size;
    if (!ok) { reportError(storeAllocFail); freeStore(); }
  }
  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');
}

bool getYorInt(int *n) { // Yes or No or integer
//   ---------
  bool result=F; int c; *n=-1; do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n') );
  if ( (c=='Y')|(c=='y') ) result=T;
  else if ( (c!='N')&(c!='n') )
    if ( ('0'<=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;
} // getYorInt

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

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 squares file: ");
    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

bool allNumbersUsed(int **x) {
//   --------------
  for (int i=0; i<=NN; ++i) numberUsed[i]=F;
  for (int r=0; r<N; ++r) for (int c=0; c<N; ++c) {
    const int t=x[r][c]; if ((t>=0)&(t<=NN)) numberUsed[t]=T;
  }
  for (int i=1; i<NN; ++i) if (!numberUsed[i]) return F;
  if (numberUsed[0]==numberUsed[NN]) return F;
  return T;
} // allNumbersUsed

enum magicType { notMagic, normalSemiMagic, otherSemiMagic, normalMagic, otherMagic };
const magicType basicType[2][2]={{otherSemiMagic, normalSemiMagic}, {otherMagic, normalMagic}};

magicType isNormal(int **x) {
//        --------
  int sumX, sumY, sumXY=0, sumYX=0, chkSum;

  for (int r=0; r<N; ++r) {
    sumX=0; sumY=0; for (int c=0; c<N; ++c) { sumX+=x[r][c]; sumY+=x[c][r]; }
    if (r==0) chkSum=sumX; if ((sumX!=chkSum)|(sumY!=chkSum)) return notMagic;
    sumXY+=x[r][M-r]; sumYX+=x[r][r];
  }
  return basicType[(sumXY==chkSum)&&(sumYX==chkSum)][allNumbersUsed(x)&&(chkSum==magicSum)];
} // isNormal

bool isReversible(int **x) {
//   ------------
  if (!allNumbersUsed(x)) return F;
  
  // Check reverse similarity of half rows.
  for (int r=0; r<N; ++r) {
    const int t=x[r][0]+x[r][M]; for (int c=1; c<G; ++c) if ((x[r][c]+x[r][M-c])!=t) return F;
  }
  // Check reverse similarity of half columns.
  for (int c=0; c<N; ++c) {
    const int t=x[0][c]+x[M][c]; for (int r=1; r<G; ++r) if ((x[r][c]+x[M-r][c])!=t) return F;
  }
  // Check cross-corner sum equality.
  for (int r=0; r<N; ++r) for (int c=0; c<N; ++c)
    for (int i=0; i<(N-r); ++i) for (int j=0; j<(N-c); ++j)
      if ((x[r][c]+x[r+i][c+j])!=(x[r][c+j]+x[r+i][c])) return F; return T;
} // isReversible

bool isCompact(int **x) {
//   ---------
  for (int r=0; r<M; ++r) {
    for (int c=0; c<M; ++c) { if ((x[r][c]+x[r][c+1]+x[r+1][c]+x[r+1][c+1])!=Sum4) return F; }
    if ((x[r][M]+x[r][0]+x[r+1][M]+x[r+1][0])!=Sum4) return F;
    if ((x[M][r]+x[0][r]+x[M][r+1]+x[0][r+1])!=Sum4) return F;
  } return T;
} // isCompact

bool isComplete(int **x) {
//   ----------
  for (int r=0; r<E; ++r) { // no need to check last pair in each diag
    const int i=r+G;
    for (int c=0; c<N; ++c) { int j=c+G; j=j<N ? j : j-N; if ((x[r][c]+x[i][j])!=Sum2) return F; }
  } return T;
} // isComplete

bool isPandiagonal(int **x) {
//   -------------
  // Main diagonals already checked in isNormal.
  for (int i=1; i<N; ++i) {
    int sumYX=0, sumXY=0;
    for (int r=0, c=i; r<N; ++r, ++c) {
      const int cModn=c<N ? c : c-N; sumYX+=x[r][cModn]; sumXY+=x[r][M-cModn];
    }
    if ((sumYX!=magicSum)|(sumXY!=magicSum)) return F;
  } return T;
} // isPandiagonal

bool isMostPerfect(int **x) {
//   -------------
  return (isNormal(x)==normalMagic)&&isPandiagonal(x)&&isComplete(x)&&isCompact(x);     
} // isMostPerfect

int getWidth(const int number) {
//  --------
  int width=1, i=number; while ((i=i/10)!=0) ++width; return width;
} // getWidth

void assignGlobals(const int smallest, const int biggest) {
//   -------------
  if (zeroBase) { magicSum=magicConstant-N; Sum2=NNm1;
  } else { magicSum=magicConstant; Sum2=NNp1; } Sum4=Sum2+Sum2;
  int fws, fwb;
  if (smallest<0) fws=getWidth(-smallest)+1; else fws=getWidth(smallest);
  if (biggest<0) fwb=getWidth(-biggest)+1; else fwb=getWidth(biggest); fieldWidth=max(fws,fwb);
} // assignGlobals

void checkSquare(const int smallest, const int biggest) {
//   -----------
  squareType=sqOther;
  if ((smallest<0)|(biggest>NN)) {
    printf("\aError: Invalid number %d in square %d.\n", smallest<0 ? smallest : biggest, squareNum);
  } else {
    if (isReversible(xSquare)) squareType=sqReversible;
    else if (isMostPerfect(xSquare)) squareType=sqMostPerfect;
    else printf("\aError: Square %d is neither reversible nor most-perfect.\n", squareNum);
  }
} // checkSquare

bool readSquare(FILE *rfp) {
//   ----------
  const int n=N; int smallest=INT_MAX, biggest=INT_MIN;
  for (int r=0; r<n; r++) {
    for (int c=0; c<n; c++) {
	    int tmp, rv;
      if ( (rv=fscanf(rfp, "%d", &tmp))==1) {
        if (tmp<smallest) smallest=tmp; if (tmp>biggest) biggest=tmp; xSquare[r][c]=tmp;
      } else {
        if ( (rv!=EOF)|(r!=0)|(c!=0) ) {
          readError=T; printf("\a\nError reading square %d from file.\n", squareNum+1);
        } return F;
      }
    }
  }
  ++squareNum; zeroBase=(smallest==0); assignGlobals(smallest, biggest);
  checkSquare(smallest, biggest); return T;
} // readSquare
//============================================ output =============================================

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 fprintFWa(FILE *fp, const int i) { return fprintf(fp, "%10d", i)>0; }

static t_fprintFW fprintFW[]={ NULL,
  fprintFW1, fprintFW2, fprintFW3, fprintFW4, fprintFW5,
  fprintFW6, fprintFW7, fprintFW8, fprintFW9, fprintFWa
};
const int maxFieldWidth=10;

bool writeSquare(FILE *wfp, int **x) {
//   -----------
  const int n=N, fw0=fieldWidth, fw=fw0+1; if (fw>maxFieldWidth) return F;
  for (int r=0; r<n; r++) {
    if (!fprintFW[fw0](wfp, x[r][0])) return F;
    for (int c=1; c<n; c++) if (!fprintFW[fw](wfp, x[r][c])) return F;
    if (fputc('\n', wfp)==EOF) return F;
  }
  return fputc('\n', wfp)!=EOF;
} // writeSquare

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

char fnMP[outSize], fnR[outSize];
FILE *openOutput(const char *inFname, const char *add) {
//    ----------
  FILE *wfp=NULL; int sub=0; const int baseSize=bufSize+25; char baseName[baseSize], buf[outSize];
  snprintf(baseName, baseSize, "./%s%s", inFname, add); 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 strcpy(add[0]=='M' ? fnMP : fnR, buf);
  return wfp;
} // openOutput
//============================================ make squares ==========================================

void reverseRowsRightHalf(int **x) {
//    -------------------
  for (int r=0; r<N; ++r) for (int c=G; c<N3d4; ++c) {
    const int t=x[r][c]; x[r][c]=x[r][N3m2d2-c]; x[r][N3m2d2-c]=t;
  }
} // reverseRowsRightHalf

void  reverseColumnsBottomHalf(int **x) {
//    ------------------------
  for (int c=0; c<N; ++c) for (int r=G; r<N3d4; ++r) {
    const int t=x[r][c]; x[r][c]=x[N3m2d2-r][c]; x[N3m2d2-r][c]=t;
  }
} // reverseColumnsBottomHalf

#define xForm(iX, jX) for (int i=0; i<N; ++i) for (int j=0; j<N; ++j) \
        t[iX][jX]=x[i][j]
#define xFormT(iX, jX) for (int i=0; i<N; ++i) for (int j=0; j<N; ++j) \
        t[iX][jX]=x[i][j]; found=T
#define xCopy() for (int i=0; i<N; ++i) for (int j=0; j<N; ++j) \
        x[i][j]=t[i][j]

void applyTransform(int **x) { int **t=tSquare, k=G, l=k+1; xForm((i+k*j)%N, (k*i+l*j)%N); xCopy(); }
//   --------------

void putInFrenicleForm(int **x) {
//   -----------------
  bool inFrenicleForm=F; int **t=tSquare,
       minC=min(min(x[0][0], x[0][M]), min(x[M][0], x[M][M]));

  if (x[0][0]==minC) {
    if (x[0][1]<x[1][0]) inFrenicleForm=T;
    else xForm(j,i);                     // YX
  } else if (x[0][M]==minC) {
    if (x[1][M]<x[0][L]) xForm(M-j,i);   // -90
    else xForm(i,M-j);                   // Y   
  } else if (x[M][M]==minC) {
    if (x[M][L]<x[L][M]) xForm(M-i,M-j); // 180
    else xForm(M-j,M-i);                 // XY
  } else { // x[M][0]==minC
    if (x[L][0]<x[M][1]) xForm(j,M-i);   // 90
    else xForm(M-i,j);                   // X
  }
  if (!inFrenicleForm) xCopy();
} // putInFrenicleForm
//--------------------------------------------------------------------------

// In the square below:
//
//   a, b, c, d are the square corners, e is (N/2-1, N/2), f is (N/2-1, N/2-1)
//
//            a . . b
//            . f e .
//            . . . .
//            c . . d
//
// Orients the square corners as above where a<b<c<d.
// Then, if a>e and b>f inverts the square,
// else, if a>e and b<f mirrors the square.
//
void orientRSquare(int **x) {
//   -------------
  bool change=T; int **t=tSquare, minC=min(min(x[0][0], x[0][M]), min(x[M][0], x[M][M]));
  
  if (x[0][0]==minC) {
    if (x[0][M]<x[M][0]) change=F;
    else xForm(j,i);                     // YX
  } else if (x[0][M]==minC) {
    if (x[M][M]<x[0][0]) xForm(M-j,i);   // -90
    else xForm(i,M-j);                   // Y   
  } else if (x[M][M]==minC) {
    if (x[M][0]<x[0][M]) xForm(M-i,M-j); // 180
    else xForm(M-j,M-i);                 // XY
  } else { // x[M][0]==minC
    if (x[0][0]<x[M][M]) xForm(j,M-i);   // 90
    else xForm(M-i,j);                   // X
  }
  if (change) xCopy();

  if (x[0][0]>x[E][G]) {
    if (x[0][M]>x[E][E]) xForm(M-i,j);  // X
    else xForm(i,M-j);                  // Y
    xCopy();
  }
}  // orientRSquare

bool getMostPerfect(FILE *wfp, const bool unique) {
//   --------------
  bool ok=F; int **x=xSquare; if (unique) orientRSquare(x);
  reverseRowsRightHalf(x); reverseColumnsBottomHalf(x); applyTransform(x);
  if (unique) putInFrenicleForm(x);
  ok=writeSquare(wfp, x); if (ok) ++numMP; else writeError=T; return ok;
} // getMostPerfect

// The mirrored reversible square:          transforms to:
//                                 b . . a                  b . a .
//                                 . e f .                  . . . .
//                                 . . . .                  d . c .
//                                 d . . c                  . e . f
//              
// Orients the square as transformed where a<b<c<d, a>e, b<f.
bool orientMP3(int **x) { // MP from mirrored oriented R.
//   ---------
  int **t=tSquare, a, b, c, d, e, f; bool change=T, found=F;

  b=x[0][0]; f=x[M][M];
  if (b<f) { a=x[0][G]; d=x[G][0]; c=x[G][G];
    if ((a<b)&(b<c)&(c<d)) {
      e=x[M][E]; if (a>e) { change=F; found=T; }
    } else if ((d<b)&(b<c)&(c<a)) {
      e=x[E][M]; if (d>e) { xFormT(j,i); } // YX
    }
  }

  if (!found) { b=x[0][M]; f=x[M][0];
    if (b<f) { a=x[G][M]; d=x[0][E]; c=x[G][E];
      if ((a<b)&(b<c)&(c<d)) {
        e=x[E][0]; if (a>e) { xFormT(M-j,i); } // -90
      } else if ((d<b)&(b<c)&(c<a)) {
        e=x[M][G]; if (d>e) { xFormT(i,M-j); } // Y
      }
    }
  }

  if (!found) { b=x[M][M]; f=x[0][0];
    if (b<f) { a=x[M][E]; d=x[E][M]; c=x[E][E];
      if ((a<b)&(b<c)&(c<d)) {
        e=x[0][G]; if (a>e) { xFormT(M-i,M-j); } // 180
      } else if ((d<b)&(b<c)&(c<a)) {
        e=x[G][0]; if (d>e) { xFormT(M-j,M-i); } // XY
      }
    }
  }

  if (!found) { b=x[M][0]; f=x[0][M];
    if (b<f) { a=x[E][0]; d=x[M][G]; c=x[E][G];
      if ((a<b)&(b<c)&(c<d)) {
        e=x[G][M]; if (a>e) { xFormT(j,M-i); } // 90
      } else if ((d<b)&(b<c)&(c<a)) {
        e=x[0][E]; if (d>e) { xFormT(M-i,j); } // X
      }
    }
  }

  if (found&change) xCopy(); return found;
} // orientMP3

// The inverted reversible square:          transforms to:
//                                 c . . d                  c . d .
//                                 . . . .                  . f . e
//                                 . f e .                  a . b .
//                                 a . . b                  . . . .
//              
// Orients the square as transformed where a<b<c<d, a>e, b>f.
bool orientMP2(int **x) { // MP from inverted oriented R.
//   ---------
  int **t=tSquare, a, b, c, d, e, f; bool change=T, found=F;

  b=x[G][G]; f=x[E][E];
  if (b>f) { a=x[G][0]; c=x[0][0]; d=x[0][G];
    if ((a<b)&(b<c)&(c<d)) {
      e=x[E][M]; if (a>e) { change=F; found=T; }
    } else if ((d<b)&(b<c)&(c<a)) {
      e=x[M][E]; if (d>e) { xFormT(j,i); } // YX
    }
  }

  if (!found) { b=x[G][E]; f=x[E][G];
    if (b>f) { a=x[0][E]; c=x[0][M]; d=x[G][M];
      if ((a<b)&(b<c)&(c<d)) {
        e=x[M][G]; if (a>e) { xFormT(M-j,i); } // -90
      } else if ((d<b)&(b<c)&(c<a)) {
        e=x[E][0]; if (d>e) { xFormT(i,M-j); } // Y
      }
    }
  }

  if (!found) { b=x[E][E]; f=x[G][G];
    if (b>f) { a=x[E][M]; c=x[M][M]; d=x[M][E];
      if ((a<b)&(b<c)&(c<d)) {
        e=x[G][0]; if (a>e) { xFormT(M-i,M-j); } // 180
      } else if ((d<b)&(b<c)&(c<a)) {
        e=x[0][G]; if (d>e) { xFormT(M-j,M-i); } // XY
      }
    }
  }

  if (!found) { b=x[E][G]; f=x[G][E];
    if (b>f) { a=x[M][G]; c=x[M][0]; d=x[E][0];
      if ((a<b)&(b<c)&(c<d)) {
        e=x[0][E]; if (a>e) { xFormT(j,M-i); } // 90
      } else if ((d<b)&(b<c)&(c<a)) {
        e=x[G][M]; if (d>e) { xFormT(M-i,j); } // X
      }
    }
  }

  if (found&change) xCopy(); return found;
} // orientMP2

// The oriented reversible square:          transforms to:
//                                 a . . b                 a . b .
//                                 . . e .                 . . . .
//                                 . . . .                 c . d .
//                                 c . . d                 . . . e
//              
// Orients the square as transformed where a<b<c<d, a<e.
//
bool orientMP1(int **x) { // MP from oriented R.
//   ---------
  int **t=tSquare, a, b, c, d, e; bool change=T, found=F;

  a=x[0][0]; e=x[M][M];
  if (a<e) { b=x[0][G]; c=x[G][0]; d=x[G][G];
    if ((a<b)&(b<c)&(c<d)) { change=F; found=T; }
    else if ((a<c)&(c<b)&(b<d)) { xFormT(j,i); }      // YX
  }

  if (!found) { a=x[0][M]; e=x[M][0];
    if (a<e) { b=x[G][M]; c=x[0][E]; d=x[G][E];
      if ((a<b)&(b<c)&(c<d)) { xFormT(M-j,i); }      // -90 
      else if ((a<c)&(c<b)&(b<d)) { xFormT(i,M-j); } // Y
    }
  }

  if (!found) { a=x[M][M]; e=x[0][0];
    if (a<e) { b=x[M][E]; c=x[E][M]; d=x[E][E];
      if ((a<b)&(b<c)&(c<d)) { xFormT(M-i,M-j); }      // 180
      else if ((a<c)&(c<b)&(b<d)) { xFormT(M-j,M-i); } // XY
    }
  }

  if (!found) { a=x[M][0]; e=x[0][M];
    if (a<e) { b=x[E][0]; c=x[M][G]; d=x[E][G];
      if ((a<b)&(b<c)&(c<d)) { xFormT(j,M-i); }       // 90
      else if ((a<c)&(c<b)&(b<d)) { xFormT(M-i,j); }  // X
    }
  }
  if (found&change) xCopy(); return found;  
}  // orientMP1

void orientMPSquare(int **x) {
//   --------------
  if (!(orientMP1(x)||orientMP2(x)||orientMP3(x))) {
    programError("Can't orient most-perfect square %d");
  }
}  // orientMPSquare

bool getReversible(FILE *wfp, const bool unique) {
//   -------------
  bool ok=F; int **x=xSquare; if (unique) orientMPSquare(x); applyTransform(x);
  reverseColumnsBottomHalf(x); reverseRowsRightHalf(x);
  ok=writeSquare(wfp, x); if (ok) ++numR; else writeError=T; return ok;
} // getReversible

bool getSquare(FILE *wfpMP, FILE *wfpR) {
//   ---------
  if (squareType==sqOther) return F;
  if (unique) {
    if (squareType==sqReversible) {
      return getMostPerfect(wfpMP, T)&&getReversible(wfpR, F);
    } else {
      return getReversible(wfpR, T)&&getMostPerfect(wfpMP, F);
    }
  } else return squareType==sqReversible ? getMostPerfect(wfpMP, F) : getReversible(wfpR, F);
} // getSquare
//================================================== main =============================================

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

void reportElapsedTime(time_t startTime) {
//   -----------------
  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

bool checkN() {
//   ------
  if ((N<=0)|((N&3)!=0)) {
    printf("\aOrder %d is not a positive doubly even order.\n\n", N); return F; }
  return T;
} // checkN

void checkFilesWritten() {
//   -----------------
  if (numMP==0) remove(fnMP); else printf("%s: %d most-perfect squares\n", fnMP, numMP);
  if (numR==0) remove(fnR); else printf("%s: %d reversible squares", fnR, numR);
} // checkFilesWritten

bool doAnother(bool *inputSize) {
//   ---------
  if (!writeError) {
    printf("\nContinue? input y (yes) or n (no) or the order of the squares: ");
    if (getYorInt(&N)) { *inputSize=(N<0); return T; }
  }
  freeStore(); return F;
} // doAnother

int main() {
//  ----
  bool another=T, inputSize=T, getError=F, ok=F; allocatedSize=0; outputLocalTime();
  do { // for each input file
    if (inputSize) { printf("\nSquare order? "); N=getInt(); }
    if (checkN()) {
      initGlobals(); printf("\nMake unique squares? "); unique=getY();
      if (allocateStore(N)) {
        char ibuf[bufSize], obuf[outSize]; FILE *rfp=openInput(ibuf);
        if (rfp!=NULL) {
          time_t startTime=time(NULL); stripName(ibuf, obuf); FILE *wfpMP=openOutput(obuf, "MP");
          if (wfpMP!=NULL) {
            FILE *wfpR=openOutput(obuf, "R");
            if (wfpR!=NULL) {
              while (readSquare(rfp)) if (getSquare(wfpMP, wfpR)) ok=T; else if (writeError) break;
              fclose(wfpR); 
            } // if (wfpR!=NULL)       
            fclose(wfpMP); checkFilesWritten();
          } // if (wfpMP!=NULL)
          reportElapsedTime(startTime); fclose(rfp);
        } // (rfp!=NULL)
      } // (allocateStore())
    } // (checkN
    another=doAnother(&inputSize);
  } while (another);
  printf("\nHit return to close the console.");
  while (T) if (getchar()=='\n') break; return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main