/*
 * File:    Transforms1_2All.cpp
 * Author:  S Harry White
 * Created: 2011-02-20
 * Updated: 2014-12-18
 * Updated: 2020-11-23
 *   Increase maxCombos.
 * Updated 2022-02-01
 *   Tidy code.
 * Updated: 2023-02-22
 *   Improve openInput and openOutput.
 */

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

const int maxNMagicConstant=2047, // max N before 32-bit unsigned long overflow of MagicConstant
          startSquareSize=16, startRowOrdersSize=5000, startXQueueSize=10000,
          startSwapCountsSize=startRowOrdersSize, bufSize=1024, outSize=bufSize+50;
int N,    // The order of the square.
    M,    // N-1
    NN,   // N*N
    Half, // N/2
    Mid,  // (N+1)/2
    **squareA=NULL, **rowOrders=NULL, *rowOrderTmp=NULL, *swapCounts=NULL, squareSize, rowOrdersSize,
    swapCountsSize, xQueueSize, Cindex, Qindex, ROindex, numRFirstGroups, maxCombos, fieldWidth, inputSqNum,
    numSquares, smallestRead, biggestRead, printNum; // print a message at this number of squares
unsigned int maxPossibleSquaresI; double maxPossibleSquaresD;
unsigned long MagicConstant; // N*(NN+1)/2
const bool F=false, T=true;
bool zeroBased, isMagic, Odd, *numberUsed=NULL, *valueFree=NULL;
struct t_xform { int w, x; }; t_xform *xQueue=NULL;

void initGlobals() {
//  -----------
  M=N-1; NN=N*N; Half=N/2; Mid=(N+1)/2; Odd=(N&1)==1; MagicConstant=N*(NN+1)/2;
  printNum=100000; numSquares=0; Cindex=0; Qindex=0; ROindex=0;
  numRFirstGroups=N==1 ? 1 : Odd ? M/2 : N/2; maxCombos=MAXINT/NN;
  if (maxCombos>1000) maxCombos=(maxCombos/100)*100; 
  else if (maxCombos>10) maxCombos=(maxCombos/10)*10; else if (maxCombos<=1) maxCombos=2;
  unsigned int start=N/2, n_2=Odd ? N-3 : N-2;
  if (N<=21) {
    unsigned int num=start==0 ? 1 : start;
    if (N!=1) for (int i=n_2; i>=2; i-=2) num *= i; maxPossibleSquaresI=num; maxPossibleSquaresD=0;
  } else {
    if (N>301) maxPossibleSquaresD=-1; // overflow
    else {
      double num=start; for (int i=n_2; i>=2; i-=2) num *= i; maxPossibleSquaresI=0; maxPossibleSquaresD=num;
    }
  }
} // initGlobals
//================================================ store =====================================================

char *storeAllocFail="Storage allocation failed"; bool message;
void reportError(char *msg) {  printf("\a\nError: %s.\n", msg); }
//   -----------

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

void freeSquares() { freeSquare(&squareA, squareSize); squareSize=0; }
//   ----------- 

void freeOthers() {
//   ----------
  free(numberUsed); numberUsed=NULL; free(valueFree); valueFree=NULL;
  free(rowOrderTmp); rowOrderTmp=NULL; free(xQueue); xQueue=NULL; xQueueSize=0;
  free(swapCounts); swapCounts=NULL; swapCountsSize=0;
} // freeOthers

void freeRowOrders(const int number) {
//   -------------
  if (rowOrders!=NULL) {
    if (message) printf("  .. freeing combinations store\n"); int count=0;
    for (int i=0; i<number; i++) {
      if (message) if (++count==100000) { printf("    .. %d\n", i+1); count=0; } free(rowOrders[i]);
    }
    free(rowOrders); rowOrders=NULL;
  }
  rowOrdersSize=0;
} // freeRowOrders

void freeStore() {
//   --------- 
  if (message) printf(".. freeing store\n"); freeRowOrders(rowOrdersSize); freeOthers(); freeSquares();
} // freeStore

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

bool allocateSquares(const int size) {
//   ---------------
  const bool ok=allocateSquare(&squareA, size); if (ok) squareSize=size; return ok;
} //allocateSquares

bool allocateOthers() {
//   --------------
  const int size=squareSize;
  numberUsed=(bool *)malloc((size*size+1)*sizeof(bool)); if (numberUsed==NULL) return F;
  valueFree=(bool *)malloc(size*sizeof(bool)); if (valueFree==NULL) { freeOthers(); return F; }
  rowOrderTmp=(int *)malloc(size/2*sizeof(int)); if (rowOrderTmp==NULL) { freeOthers(); return F; }
  xQueue=(t_xform *)malloc(startXQueueSize*sizeof(t_xform)); if (xQueue==NULL) { freeOthers(); return F; }
  xQueueSize=startXQueueSize;
  swapCounts=(int *)malloc(startSwapCountsSize*sizeof(int)); if (swapCounts==NULL) { freeOthers(); return F; }
  swapCountsSize=startSwapCountsSize; return T;
} // allocateOthers

bool allocateRowOrders() {
//   -----------------
  const int size=startRowOrdersSize; bool ok=((rowOrders=(int**) malloc(size*sizeof(int*)))!=NULL);
  if (ok) {
    int numAllocated=size;
    for (int i=0; i<size; ++i) {
      int *p=(int*) malloc(squareSize/2*sizeof(int));
      if (p==NULL) { numAllocated=i; ok=F; break; } rowOrders[i]=p;
    }
    if (ok) rowOrdersSize=size; else { message=F; freeRowOrders(numAllocated);
    }
  }
  return ok;
} // allocateRowOrders

bool increaseRowOrders() {
//   -----------------
  const int size=rowOrdersSize+rowOrdersSize; int **tmp;
  bool ok=((tmp=(int**) malloc(size*sizeof(int*)))!=NULL);
  if (ok) {
    int numAllocated=size;
    for (int i=0; i<size; i++) {
      int *p=(int*) malloc(squareSize/2*sizeof(int)); tmp[i]=p;
      if (p==NULL) { numAllocated=i; ok=F; break; }
    }
    if (!ok) {
      int **save=rowOrders;
      rowOrders=tmp; freeRowOrders(numAllocated); rowOrders=save; rowOrdersSize=size/2;
    }
  }
  if (ok) {
    for (int i=0; i<ROindex; i++) tmp[i]=rowOrders[i]; 
    message=F; free(rowOrders); rowOrders=tmp; rowOrdersSize=size;
  }
 if (!ok) reportError(storeAllocFail);
 printf("  .. combinations store %d\n", rowOrdersSize); return ok;
} // increaseRowOrders

bool increaseSwapCounts() {
//   ------------------
  const int size=swapCountsSize+swapCountsSize; int *tmp;
  const bool ok=((tmp=(int *) malloc(size*sizeof(int)))!=NULL);
  if (ok) {
    for (int i=0; i<Cindex; i++) tmp[i]=swapCounts[i]; free(swapCounts); swapCounts=tmp; swapCountsSize=size;
  }
  if (!ok) reportError(storeAllocFail); //printf("  .. swaps counts store %d\n", swapCountsSize);
  return ok;
} // increaseSwapCounts

bool increaseXQueue() {
//   --------------
  const int size=xQueueSize+xQueueSize; t_xform *tmp;
  const bool ok=((tmp=(t_xform *) malloc(size*sizeof(t_xform)))!=NULL);
  if (ok) {
    for (int i=0; i<Qindex; i++) tmp[i]=xQueue[i]; free(xQueue); xQueue=tmp; xQueueSize=size;
  }
  if (!ok) reportError(storeAllocFail); printf("  .. swaps store %d\n", xQueueSize); return ok;
} // increaseXQueue

bool allocateStore() {
//   -------------
  bool ok=T; const int size=N<startSquareSize ? startSquareSize : N;
  if (size>squareSize) {
    if (squareSize!=0) freeStore();
    if (ok=allocateSquares(size)) {
      if (ok=allocateOthers()) if (!(ok=allocateRowOrders())) freeOthers(); if (!ok) freeSquares();
    }
    if (!ok) reportError(storeAllocFail);
  }
  //printf("sizes: ro %d sq %d sC %d Q %d\n",
  //        rowOrdersSize, squareSize, swapCountsSize, xQueueSize);
  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 getYorSize(int *n) {
//   ----------
  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;
} // getYorSize

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

void getInts(int *p, int *q) {
//   -------
 bool result=F; int c; *p=*q=0; do { c=getchar(); } while ((c==' ')|(c=='\t'));
  if ( ('0'<=c)&(c<='9') ) {
    int i=c-'0'; while ( ('0'<=(c=getchar()))&&(c<='9') ) i=i*10+c-'0'; *p=i;
    if ((c==' ')|(c=='\t')) {
      do { c=getchar(); } while ((c==' ')|(c=='\t'));
      if ( ('0'<=c)&(c<='9') ) {
        int i=c-'0'; while ( ('0'<=(c=getchar()))&&(c<='9') ) i=i*10+c-'0'; *q=i;
      }
    }
    result=T;
  }   
  clearLine(c);
} // getInts

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

bool isCorrect() {
//   ---------
  if (N>maxNMagicConstant) return T; // Overflow, can't check.
  const unsigned long chkSum=zeroBased ? MagicConstant-N : MagicConstant;
  const int size=N, min=zeroBased ? 0 : 1, max=NN+min-1; unsigned long sumX, sumY, sumXY=0, sumYX=0;
  for (int i=min; i<=max; i++) numberUsed[i]=F;
  for (int i=0; i<size; i++) {
    sumX=0; sumY=0;
    for (int j=0; j<size; j++) { int tmp=squareA[i][j]; numberUsed[tmp]=T; sumX+=tmp; sumY+=squareA[j][i]; }
    if ((sumX!=chkSum)|(sumY!=chkSum)) return F; sumXY+=squareA[i][size-i-1]; sumYX+=squareA[i][i];
  }
  if ((sumXY!=chkSum)|(sumYX!=chkSum)) return F; for (int i=min; i<=max; i++) if (!numberUsed[i]) return F; return T;
} // isCorrect

int getWidth(int i) {
//  --------
  const 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=LONG_MAX, biggest=LONG_MIN;
  for (int r=0; r<N; r++) for (int c=0; c<N; c++) {
	  int tmp, rv;
    if ( (rv=fscanf_s(rfp, "%d", &tmp))==1) {
      if (tmp<smallest) smallest=tmp; if (tmp>biggest) biggest=tmp; squareA[r][c]=tmp;
    } else {
      if ( (rv!=EOF)|(r!=0)|(c!=0) ) reportError("Reading square from file"); return F;
    }
  }
  zeroBased=(smallest==0); isMagic=(smallest>=0)&(biggest<=NN) ? isCorrect() : F;
  const int sWidth=getWidth(smallest), bWidth=getWidth(biggest); fieldWidth=sWidth>bWidth ? sWidth : bWidth;
  smallestRead=smallest; biggestRead=biggest; ++inputSqNum; return T;
} // readSquare

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], *rFname=NULL; FILE *rfp=NULL;
  do {
    printf("\nEnter the name of the squares file: ");
    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
//================================================ output ================================================

bool openDir(char *dir) {
//   -------
  char baseName[bufSize]; int sub=0;
  sprintf_s(baseName, bufSize, "Order%dXForm", N); strcpy_s(dir, bufSize, baseName);
  do {
    if (_mkdir(dir)!=-1) break;
    if (errno!=EEXIST) {
      sprintf_s(baseName, bufSize, "\a\nCan't make folder %s", dir); perror(baseName); return F;
    }
    sprintf_s(dir, bufSize, "%s_%d", baseName, ++sub);
  } while (T);
  printf("Output file(s) are in folder %s\n", dir); return T;
} // openDir

FILE *openOutput(char *dir) {
//    ----------
  FILE *wfp=NULL; int sub=0; const int baseSize=bufSize+25; char baseName[baseSize], buf[outSize];
  sprintf_s(baseName, baseSize, "%s\\%d", dir, inputSqNum); sprintf_s(buf, outSize, "%s.txt", baseName);
  do {
    if (_access_s(buf, 00)==ENOENT) break; else sprintf_s(buf, outSize, "%s%d.txt", baseName, ++sub);
  } while (T);
  if (fopen_s(&wfp, buf, "w")!=0) {
    char msg[outSize+50];
    sprintf_s(msg, outSize+50, "\a\nCan't open for write %s", buf); perror(msg);
  }
  return wfp;
} // openOutput

typedef bool (*t_Write)(FILE *);

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

bool writeSquare2(FILE *wfp) {
//   ------------
  for (int r=0; r<N; r++) {
    if (fprintf(wfp, "%2d", squareA[r][0])<0) return F;
    for (int c=1; c<N; c++) { if (fprintf(wfp, " %2d", squareA[r][c])<0) return F; }
    if (fputc('\n', wfp)==EOF) return F;
  }
  return T;
} // writeSquare2

bool writeSquare3(FILE *wfp) {
//   ------------
  for (int r=0; r<N; r++) {
    if (fprintf(wfp, "%3d", squareA[r][0])<0) return F;
    for (int c=1; c<N; c++) { if (fprintf(wfp, " %3d", squareA[r][c])<0) return F; }
    if (fputc('\n', wfp)==EOF) return F;
  }
  return T;
} // writeSquare3

bool writeSquare4(FILE *wfp) {
//   ------------
  for (int r=0; r<N; r++) {
    if (fprintf(wfp, "%4d", squareA[r][0])<0) return F;
    for (int c=1; c<N; c++) { if (fprintf(wfp, " %4d", squareA[r][c])<0) return F; }
    if (fputc('\n', wfp)==EOF) return F;
  }
  return T;
} // writeSquare4

bool writeSquare5(FILE *wfp) {
//   ------------
  for (int r=0; r<N; r++) {
    if (fprintf(wfp, "%5d", squareA[r][0])<0) return F;
    for (int c=1; c<N; c++) { if (fprintf(wfp, " %5d", squareA[r][c])<0) return F; }
    if (fputc('\n', wfp)==EOF) return F;
  }
  return T;
} // writeSquare5

bool writeSquare6(FILE *wfp) {
//   ------------
  for (int r=0; r<N; r++) {
    if (fprintf(wfp, "%6d", squareA[r][0])<0) return F;
    for (int c=1; c<N; c++) { if (fprintf(wfp, " %6d", squareA[r][c])<0) return F; }
    if (fputc('\n', wfp)==EOF) return F;
  }
  return T;
} // writeSquare6

bool writeSquare7(FILE *wfp) {
//   ------------
  for (int r=0; r<N; r++) {
    if (fprintf(wfp, "%7d", squareA[r][0])<0) return F;
    for (int c=1; c<N; c++) { if (fprintf(wfp, " %7d", squareA[r][c])<0) return F; }
    if (fputc('\n', wfp)==EOF) return F;
  }
  return T;
} // writeSquare7

bool writeSquare8(FILE *wfp) {
//   ------------
  for (int r=0; r<N; r++) {
    if (fprintf(wfp, "%8d", squareA[r][0])<0) return F;
    for (int c=1; c<N; c++) { if (fprintf(wfp, " %8d", squareA[r][c])<0) return F; }
    if (fputc('\n', wfp)==EOF) return F;
  }
  return T;
} // writeSquare8

bool writeSquare9(FILE *wfp) {
//   ------------
  for (int r=0; r<N; r++) {
    if (fprintf(wfp, "%9d", squareA[r][0])<0) return F;
    for (int c=1; c<N; c++) { if (fprintf(wfp, " %9d", squareA[r][c])<0) return F; }
    if (fputc('\n', wfp)==EOF) return F;
  }
  return T;
} // writeSquare9

bool writeSquare10(FILE *wfp) {
//   -------------
  for (int r=0; r<N; r++) {
    if (fprintf(wfp, "%10d", squareA[r][0])<0) return F;
    for (int c=1; c<N; c++) { if (fprintf(wfp, " %10d", squareA[r][c])<0) return F; }
    if (fputc('\n', wfp)==EOF) return F;
  }
  return T;
} // writeSquare10

bool writeSquare11(FILE *wfp) {
//   -------------
  for (int r=0; r<N; r++) {
    if (fprintf(wfp, "%11d", squareA[r][0])<0) return F;
    for (int c=1; c<N; c++) { if (fprintf(wfp, " %11d", squareA[r][c])<0) return F; }
    if (fputc('\n', wfp)==EOF) return F;
  }
  return T;
} // writeSquare11

static t_Write writeSquareFW[]=
  { NULL,        writeSquare1, writeSquare2,  writeSquare3, writeSquare4, writeSquare5,
   writeSquare6, writeSquare7, writeSquare8, writeSquare9, writeSquare10, writeSquare11 };

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

bool writeSquare(FILE *wfp) {
//   -----------
  if (++numSquares==printNum) {
    const int msq=printNum/1000000, tsq=printNum%1000000/1000, rsq=printNum%1000;
    if (msq==0)
      if (tsq==0) printf(".. %10d\n", rsq); else printf(".. %6d,%03d\n", tsq, rsq);
    else printf(".. %2d,%03d,%03d\n", msq, tsq, rsq); printNum+=printNum;
  }
  if ((N==5)&(fieldWidth==2)&(smallestRead>=0)&(biggestRead<=NN)) return writeSquareOrder5(wfp);
  else return writeSquareFW[fieldWidth](wfp) ? fputc('\n', wfp)!=EOF : F;
} // writeSquare

void printOutputMessage() {
//   ------------------
  if (numSquares==maxPossibleSquaresI)
    if (N<=3) printf("Made the %d square.\n", numSquares); else printf("Made all %d squares.\n\n", numSquares);
  else
    if (maxPossibleSquaresI==0)
      if (maxPossibleSquaresD<0) printf("Made %d of the very large number of possible squares.\n\n", numSquares);
      else printf("Made %d of %1e possible squares.\n\n", numSquares, maxPossibleSquaresD);
    else printf("Made %d of %d possible squares.\n\n", numSquares, maxPossibleSquaresI);
} // printOutputMessage
//================================================== transform ==================================================

void swapRowsCols1(const int i) {
//   -------------
  const int w=xQueue[i].w, n=N, y=M-w; int *tmp=squareA[w]; squareA[w]=squareA[y]; squareA[y]=tmp;
  for (int r=0; r<n; ++r) { int tmp=squareA[r][w]; squareA[r][w]=squareA[r][y]; squareA[r][y]=tmp; }
} // swapRowsCols1

void swapRowsCols2(const int i) {
//   -------------
  t_xform *p=&xQueue[i]; const int w=p->w, x=p->x, n=N, y=M-w, z=M-x;
  int *tmp=squareA[w]; squareA[w]=squareA[x]; squareA[x]=tmp;
       tmp=squareA[y]; squareA[y]=squareA[z]; squareA[z]=tmp;
  for (int r=0; r<n; ++r) {
    int tmp=squareA[r][w]; squareA[r][w]=squareA[r][x]; squareA[r][x]=tmp;
        tmp=squareA[r][y]; squareA[r][y]=squareA[r][z]; squareA[r][z]=tmp;
  }
} // swapRowsCols2

bool putRowOrder(int *ro) {
//   -----------
  if ((ROindex>=rowOrdersSize)&&!increaseRowOrders()) return F;
  int *p=rowOrders[ROindex++]; for (int i=0; i<Half; ++i) p[i]=ro[i];
  //printRowOrders();
  return T;
} // putRowOrder

//void printXQueue() {
////   -----------
//  printf("xQueue\n"); int k=0;
//  for (int i=0; i<Cindex; ++i) {
//    int c=swapCounts[i]; printf("swaps %d\n", c);
//    for (int j=0; j<c; ++j) { printf("%d %d\n", xQueue[k].w, xQueue[k].x); ++k; }
//  }
//} // printXQueue

bool getCombos() {
//   ---------
  printf(".. queueing row order combinations\n");
  const int Mid=Odd ? N/2 : -1;  // -1 indicates no Mid for even squares.
  int count=0, *p=rowOrderTmp, incr=maxCombos/numRFirstGroups, iMax;
  if (incr==0) { iMax=maxCombos-1; incr=1; } else iMax=Half;
  for (int i=0; i<iMax; ++i) {
    // Set up the first combo that starts with i.
    bool *Free=valueFree; for (int v=0; v<N; ++v) Free[v]=T;
    if (Mid>=0) Free[Mid]=F; p[0]=i; Free[i]=F; Free[M-i]=F;
    for (int y=1; y<Half; ++y) { int x=0; while (!Free[x]) ++x; p[y]=x; Free[x]=F; Free[M-x]=F; }
    const int stop=i==(iMax-1) ? maxCombos-1 : (i+1)*incr; int j=Half-1;
    while(T) {
      if (putRowOrder(p)) { if (count++>=stop) break; } else return F; Free[p[j]]=T; Free[M-p[j]]=T;
      bool end=j<1;
      while (j>=1) { bool next=F; Free[p[j]]=T; Free[M-p[j]]=T;
        while (T) { const int x=++p[j]; if (x>=N) { if (j==1) end=T; --j; next=F; break; }        
          if (Free[x]) {
            next=T; Free[x]=F; Free[M-x]=F;
            for (int l=j+1; l<Half; ++l) for (int m=0; m<N; ++m) {
              if (!Free[m]) continue; Free[m]=F; Free[M-m]=F;
              if (p[l]<N) { Free[p[l]]=T; Free[M-p[l]]=T; } p[l]=m; break;
            }
            j=Half-1; break;
          } // if (Free[x]
        } // while (T
        if (next) break;
      } // while (j>=1
      if (end) break;
    } // while(T)
  } // for (int i=0;
  //printRowOrders();
  //printf(".. row order combinations: %d\n", ROindex);
  return T;
//printf("ROindex %d\n", ROindex);
} // getCombos

bool pushXQueue(const int i, const int j) {
//   ----------
  if ((Qindex>=xQueueSize)&&!increaseXQueue()) return F;  t_xform *p=&xQueue[Qindex++]; p->w=i; p->x=j; return T;
} // pushXQueue

int getPos(const int v, bool *isOpp) {
//  ------
  for (int i=0; i<Half; ++i) { const int x=rowOrderTmp[i]; if ((x==v)|(x==(M-v))) { *isOpp=(x!=v); return i; }}
  printf("\aProgrsm error: getPos failed.\n"); return 0;
} // getPos

//void printRowOrder(int *p, char *s) {
////   -------------
//  printf("%s: ", s); for (int i=0; i<Half; ++i) printf(" %d", p[i]); printf("\n");
//} // printRowOrder

bool addToSwapCounts(const int swaps) {
//   ---------------
  if ((Cindex>=swapCountsSize)&&!increaseSwapCounts()) return F; swapCounts[Cindex++]=swaps; return T;
} // addToSwapCounts

bool pushTransforms() {
//   --------------
  printf(".. queueing swaps\n"); int *p=rowOrderTmp; Qindex=0, Cindex=0; for (int i=0; i<Half; ++i) p[i]=i;
  for (int i=1; i<ROindex; ++i) { // 0 is the original square already written
    int *q=rowOrders[i], swaps=0;
    for (int j=0; j<Half; ++j) {
      if (q[j]!=p[j]) {
        if (q[j]==(M-p[j])) { if (!pushXQueue(j, -1)) return F; ++swaps; p[j]=M-p[j]; }
        else {
          bool isOpp; const int k=getPos(q[j], &isOpp);
          if (isOpp) { if (!pushXQueue(k, -1)) return F; ++swaps; p[k]=M-p[k]; }
          if (!pushXQueue(j, k)) return F; ++swaps; const int x=p[j]; p[j]=p[k]; p[k]=x;
        }
      }
    }
    if (!addToSwapCounts(swaps)) return F;
  }
  // printXQueue(); //printf(".. swaps queued: %d\n", Qindex);
  return T;
} // pushTransforms

bool getTransformedSquares(FILE *wfp) {
//   ---------------------
  bool msg=T; int j=0; /* Q index */ for (int i=0; i<Half; ++i) rowOrderTmp[i]=i;
  for (int i=0; i<Cindex; ++i) {
    const int num=swapCounts[i];
    for (int i=0; i<num; ++i) if (xQueue[j].x<0) swapRowsCols1(j++); else swapRowsCols2(j++);
    if (!writeSquare(wfp)) return F;
    if (isMagic&&!isCorrect()&&msg) { msg=F; reportError("Incorrect result. Please report by email"); }
  }
  printOutputMessage(); return T;
} // getTransformedSquares

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

bool transformSquare(char *dir) {
//   ---------------
  printf(".. making transformed squares\n");
  time_t startTime=time(NULL); numSquares=0; ROindex=1;
  FILE *wfp=openOutput(dir);
  if (wfp!=NULL) {
    const bool ok=writeSquare(wfp)&&getTransformedSquares(wfp); fclose(wfp);
    if (!ok) { perror("\a\nError writing file"); return F; }
  }
  reportElapsedTime(startTime); return T;
} // transformSquare
//===================================================== main =====================================================

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

bool checkSize() { if (N<=0) { reportError("N must be a positive integer"); return F; } return T; }
//   ---------

void fcloseNULL(FILE **pfp) { if (*pfp!=NULL) { fclose(*pfp); *pfp=NULL; } }
//   ----------

bool doAnotherSize(bool *inputSize) {
//   -------------
  printf("\nTransform more squares?\ninput y (yes) or n (no) or the order of the squares: ");
  if (getYorSize(&N)) { *inputSize=(N==0); return T; } else { message=T; freeStore(); return F; }
} // doAnotherSize

int _tmain() {
//  ------
  outputLocalTime(); bool anotherSize=T, inputSize=T, ok=F;
  do {
    if (inputSize) { printf("\nInput the order of the squares: "); N=getInt(); }
    if (checkSize()) { initGlobals();
      if (allocateStore()) { char dir[bufSize];
        if (openDir(dir)) {
          FILE *rfp=openInput();
          if (rfp!=NULL) { if (getCombos()) { if (pushTransforms()) {
            inputSqNum=0; while (readSquare(rfp)) { if (!transformSquare(dir)) break; ok=T; }
          }}}
          fcloseNULL(&rfp);
        } // (rfp!=NULL)
      } // (allocateStore())
    } // (checkSize())
    anotherSize=doAnotherSize(&inputSize);
  } while (anotherSize);
  printf("\nPress a key to close the console");
  while (!_kbhit()) Sleep(250); return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // _tmain