/*
  * File:    Order5TopGroups.cpp
  * Author:  S Harry White
  * Created: 2019-04-28
  * Updated: 2020-12-16
  *   Fix getInts hanging when only one number is entered.
  * Updated 2022-01-19
  *   Tidy code.
  * Updated: 2023-01-07
  *   Change to compile with g++ for macOS.
  */

/*
 *  Makes the order 5 magic squares of the input group number.
 *  Makes an html file showing the group's complement pair pattern.
 */

#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>

typedef unsigned int uint;
typedef signed char schar;
#define N 5
#define NN N*N         // 25
#define NNd4 NN/4+1    //  7
const int M=N-1,       //  4
          O=N+1,       //  6
          NM=NN-1,     // 24
          NL=NM-1,     // 23
          NI=NN-5,     // 20
          S2=NM,       // 24
          S=S2/2*N,    // 60
// zero based
          L=NM/2,      // 12
          maxGroup=102, maxGroupAspects=8, bufSize=1024;
struct t_group { /* 28 bytes */ union { schar cpos[NN]; uint ovly[NNd4]; } U; };
struct t_group *groupAspects=NULL; int GAindex; const bool F=false, T=true; bool writeError;
//=============================================== store ==============================================

void freeStore() { free(groupAspects); groupAspects=NULL; }
//   ---------

bool allocateStore() {
//   -------------
  groupAspects=(struct t_group *)malloc(maxGroupAspects*sizeof(struct t_group));
  if (groupAspects==NULL) { printf("\aAllocate groupAspects failed\n"); return F; }
  return T;
} // allocateStore
//=============================================== input ===============================================

void clearLine(int c) { while (c!='\n') c=getchar(); }
//   ---------

void checkRange(int *p, int *q, const int max) {
//   ----------
  if (*q==0) *q=*p; if (*p<1) *p=1; if (*p>max) *p=max; if (*q<1) *q=1; if (*q>max) *q=max;
  if (*q<*p) { const int x=*p; *p=*q; *q=x; }
} // checkRange

bool getInts(int *p, int *q, const int max) {
//   -------
  bool result=F; int c; *p=*q=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; }
    }
    checkRange(p, q, max); result=T;
  }   
  clearLine(c); return result;
} // getInts

bool getY() {
//   ----
  int c; do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n'));
  clearLine(c); return (c=='Y')|(c=='y');
}
//================================================== output ==================================================

bool openDir() {
//   -------
  int sub=0; char buf[bufSize], msg[bufSize+50];
  const char *baseName="./GroupSquares"; strcpy(buf, baseName);
  do {
    if (mkdir(buf, 0775)==0) break;
    if (errno!=EEXIST) {
      snprintf(msg, bufSize+50, "Can't make folder %s", buf); perror(msg); return F;
    }
    snprintf(buf, bufSize, "%s_%d", baseName, ++sub);
  } while (T); 
  if (chdir(buf)!=0) {
    snprintf(msg, bufSize+50, "Can't open folder %s", buf); perror(msg); return F;
  }
  printf("Output files are in folder %s\n", buf); return T;
} // openDir

FILE *open_File(const int group, const char *ext) {
//    ---------
  char buf[bufSize], baseName[bufSize]; int sub=0; FILE *wfp=NULL;
  snprintf(baseName, bufSize, "./Group%d", group); snprintf(buf, bufSize, "%s.%s", baseName, ext);
  do {
    if (access(buf, F_OK)!=0) break;
    snprintf(buf, bufSize, "%s_%d.%s", baseName, ++sub, ext);
  } while (true);
  if ((wfp=fopen(buf, "w"))==NULL) {
    char msg[bufSize+50]; snprintf(msg, bufSize+50, "\a\nCan't open for write %s", buf); perror(msg);
  } else {
    printf("%s %s%s", ext[0]=='t'?"\nOutput files are":" and", buf, ext[0]=='t'?"":".\n");
  }
  return wfp;
} // open_File

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

//bool boolPrint(bool *x, FILE *wfp) {
////   ---------
//  int i=0;
//  for (int r=0; r<N; r++) {
//    if (fprintf(wfp, "%2c", x[i++]==0?'F':'T')<0) return F;
//    for (int c=1; c<N; c++) if (fprintf(wfp, "%3c", x[i++]==0?'F':'T')<0) return F;
//    if (fputc('\n', wfp)==EOF) return F;
//  }
//  return fputc('\n', wfp)!=EOF;
//} // boolPrint
//-------------------------------------------- start html ----------------------------------------------

bool writeHead(const int g, FILE *wfp) {
//   ---------
  if (fprintf(wfp,
    "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\""
    " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
    "<html lang=\"en\" xml:lang=\"en\" "
    "xmlns=\"http://www.w3.org/1999/xhtml\">\n\n"
    "<head>\n"
    "<title>Group %d</title>\n"
    "<style type=\"text/css\" id=\"internalStyle\">\n"
    "  body { font-family: Verdana, Arial, Helvetica, sans-serif; color: #000080 }\n"
    "  table { margin-left: auto; margin-right: auto; border: solid #aaaaaa 3px }\n"
    "  td { width: 25px; height: 25px; font-weight: bolder; text-align: center; "
    "border: inset white thin; }\n"
    "    .no, aq, fu, li, or, pu, ta, wh, ye { font-size: large; color: #000080;  }\n"
    "    .bk { font-size: small; color: #ffffff; background-color: #000000 }\n",g)<0) return F;
  if (fprintf(wfp,"    .aq { background-color: #00ffff }\n")<0) return F;
  if (fprintf(wfp,"    .fu { background-color: #ff00ff }\n")<0) return F;
  if (fprintf(wfp,"    .li { background-color: #00ff00 }\n")<0) return F;
  if (fprintf(wfp,"    .no { background-color: #e0e0e0 }\n")<0) return F;
  if (fprintf(wfp,"    .or { background-color: #ffcc66 }\n")<0) return F;
  if (fprintf(wfp,"    .pu { background-color: #cc99ff }\n")<0) return F;
  if (fprintf(wfp,"    .wh { background-color: #ffffff }\n")<0) return F;
  if (fprintf(wfp,"    .ta { background-color: #cc9966 }\n")<0) return F;
  if (fprintf(wfp,"    .ye { background-color: #ffff00 }\n")<0) return F;
  return fprintf(wfp,"</style>\n</head>\n\n<body>\n")>0;
} // writeHead

bool writeCellMid(FILE *wfp) { return fprintf(wfp, "<td class=\"bk\">13\n")>0; }

bool writeCell(FILE *wfp, const char *_class, const int value) {
  return fprintf(wfp, "<td class=\"%s\">%c\n", _class, value)>0; }

bool writeFoot(FILE *wfp) { return fprintf(wfp, "</table>\n</body>\n</html>")>0; }

bool htmlPrint(const int g, const int size, schar *p, FILE *wfp) {
//   ---------
  bool ok=writeHead(g,wfp);
  if (ok) {
    enum { aq, bk, fu, li, no, or_, pu, ta, wh, ye };
    const char *cl[]={ "aq", "bk", "fu", "li", "no", "or", "pu", "ta", "wh", "ye" };
    const int cn[]={no,aq,no,or_,no,fu,no,no,no,li,no,no,no,no,no,ye,no,no,no,pu,no,ta,no,wh,no};
    int col[NN], let[NN]; schar *pin=p; for (int i=0; i<NN; ++i) { col[i]=-1; let[i]=-1; }
    for (int i=0; i<NN; ++i) {
      if (*p==i) col[i]=bk;
      else switch(i) {
        case 1: case 3: case 5: case 9: case 15: case 19: case 21: case 23:
          if (col[i]<0) { col[i]=cn[i]; col[*p]=cn[i]; if (let[i]<0) { let[i]='a'+i; let[*p]='a'+i; } }
          break;
        default:
          if (col[i]<0) { col[i]=cn[*p]; if (let[i]<0) { let[i]='a'+i; let[*p]='a'+i; } }
          break;
      }
      ++p;
    }
    if (fprintf(wfp, "<table>\n")<0) return F;
    if (fprintf(wfp, "<caption align=\"bottom\"><strong>G%d<br />%d,%03d</strong></caption>\n",
      g, size/1000, size%1000)<0) return F; p=pin;
    for (int r=0; r<NN; r+=N) {
      if (fprintf(wfp, "<tr>\n")<0) return F;
      for (int c=0; c<N; ++c) {
        if (*p++==(r+c)) ok=writeCellMid(wfp); else ok=writeCell(wfp,cl[col[r+c]],let[r+c]);
        if (!ok) break;
      }
      if (!ok) break;
    }
    return ok?writeFoot(wfp):F;
  }
  return F;
} // htmlPrint
//---------------------------------------------------------------------------------------------

int groupSize[]={
  0,228960,174240,174240,145632,145632,90144,48544,48544,48544,45072,45072,43776,26688,
  26688,23232,23232,22536,22536,22536,22536,20336,20336,19728,19728,19728,19728,18784,
  18784,16896,16896,14464,14464,14104,14104,14104,14104,13808,13808,13808,13808,12544,
  12544,12416,12416,12416,12416,12192,12192,11568,11568,10848,10848,10272,10272,9776,
  9776,9776,9776,9004,9004,8352,8352,7200,7200,7200,7200,7200,7200,7200,7200,7008,7008,
  6656,6656,6622,6622,6622,6622,6456,6456,6456,6456,6432,6432,6400,6400,6272,6096,5936,
  5936,5936,5936,5936,5936,5536,5536,5264,5264,5264,5264,5088,5088
};

schar groupForm[][NN]={
  {0,},
  {24,3,22,1,20,9,18,17,16,5,14,13,12,11,10,19,8,7,6,15,4,23,2,21,0},
  {24,21,22,23,20,9,18,17,16,5,14,13,12,11,10,19,8,7,6,15,4,1,2,3,0},
  {24,3,22,1,20,15,18,17,16,19,14,13,12,11,10,5,8,7,6,9,4,23,2,21,0},
  {24,21,22,23,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,1,2,3,0},
  {24,3,22,1,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,23,2,21,0},
  {6,3,22,1,16,9,0,17,20,5,14,13,12,11,10,19,4,7,24,15,8,23,2,21,18},
  {24,23,22,21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0},
  {18,19,17,15,16,23,24,22,20,21,13,14,12,10,11,3,4,2,0,1,8,9,7,5,6},
  {6,5,7,9,8,1,0,2,4,3,11,10,12,14,13,21,20,22,24,23,16,15,17,19,18},
  {6,21,22,23,16,9,0,17,20,5,14,13,12,11,10,19,4,7,24,15,8,1,2,3,18},
  {6,3,22,1,16,15,0,17,20,19,14,13,12,11,10,5,4,7,24,9,8,23,2,21,18},
  {4,21,22,23,0,15,8,17,6,19,14,13,12,11,10,5,18,7,16,9,24,1,2,3,20},
  {24,2,1,23,20,9,7,6,8,5,14,17,16,18,10,19,12,11,13,15,4,22,21,3,0},
  {0,3,4,1,2,15,18,9,16,7,20,13,24,11,22,5,8,19,6,17,10,23,14,21,12},
  {24,21,22,23,20,9,6,8,7,5,19,16,18,17,15,14,11,13,12,10,4,1,2,3,0},
  {0,3,4,1,2,15,18,17,16,19,20,23,24,21,22,5,8,7,6,9,10,13,14,11,12},
  {18,21,22,23,16,9,24,17,20,5,14,13,12,11,10,19,4,7,0,15,8,1,2,3,6},
  {18,3,22,1,16,15,24,17,20,19,14,13,12,11,10,5,4,7,0,9,8,23,2,21,6},
  {6,21,22,23,8,9,0,17,4,5,14,13,12,11,10,19,20,7,24,15,16,1,2,3,18},
  {6,3,22,1,8,15,0,17,4,19,14,13,12,11,10,5,20,7,24,9,16,23,2,21,18},
  {24,2,1,23,20,9,7,6,8,5,19,17,16,18,15,14,12,11,13,10,4,22,21,3,0},
  {0,3,4,1,2,15,18,9,16,7,20,23,24,21,22,5,8,19,6,17,10,13,14,11,12},
  {2,19,0,18,16,7,9,5,8,6,22,14,20,23,11,17,4,15,3,1,12,24,10,13,21},
  {1,0,14,13,12,6,5,9,8,7,11,10,4,3,2,21,20,19,23,17,16,15,24,18,22},
  {0,4,3,2,1,20,24,8,7,21,15,14,18,17,11,10,19,13,12,16,5,9,23,22,6},
  {0,2,1,4,3,10,12,11,9,8,5,7,6,14,13,20,17,16,24,23,15,22,21,19,18},
  {18,3,17,1,16,23,24,22,20,21,14,13,12,11,10,19,4,2,0,15,8,9,7,5,6},
  {6,3,7,1,8,9,0,2,4,5,14,13,12,11,10,21,20,22,24,23,16,15,17,19,18},
  {18,3,17,1,16,23,24,22,20,21,11,10,12,14,13,19,4,2,0,15,8,9,7,5,6},
  {6,3,7,1,8,9,0,2,4,5,13,14,12,10,11,21,20,22,24,23,16,15,17,19,18},
  {24,2,1,23,20,9,7,6,8,5,15,17,16,18,19,10,12,11,13,14,4,22,21,3,0},
  {0,3,4,1,2,15,18,9,16,7,20,21,24,23,22,5,8,19,6,17,10,11,14,13,12},
  {2,19,0,18,16,7,9,5,8,6,22,24,20,23,21,17,4,15,3,1,12,14,10,13,11},
  {2,6,0,9,8,7,1,5,4,3,22,21,20,24,23,17,16,15,19,18,12,11,10,14,13},
  {0,4,3,2,1,20,24,8,7,21,15,19,18,17,16,10,14,13,12,11,5,9,23,22,6},
  {0,2,1,4,3,10,12,11,13,14,5,7,6,8,9,20,22,21,24,23,15,17,16,19,18},
  {3,11,14,0,12,23,21,24,20,22,13,1,4,10,2,18,16,19,15,17,8,6,9,5,7},
  {1,0,14,13,12,6,5,9,8,7,11,10,4,3,2,21,20,24,23,22,16,15,19,18,17},
  {0,4,3,2,1,20,24,22,23,21,15,19,18,17,16,10,14,13,12,11,5,9,7,8,6},
  {0,2,1,4,3,10,12,11,9,8,5,7,6,14,13,20,22,21,24,23,15,17,16,19,18},
  {3,19,17,0,16,23,21,22,20,24,13,14,12,10,11,18,4,2,15,1,8,6,7,5,9},
  {1,0,7,9,8,6,5,2,4,3,11,10,12,14,13,21,20,22,23,24,16,15,17,18,19},
  {3,11,14,0,12,23,21,22,20,24,13,1,4,10,2,18,16,19,15,17,8,6,7,5,9},
  {1,0,14,13,12,6,5,9,8,7,11,10,4,3,2,21,20,22,23,24,16,15,17,18,19},
  {0,4,3,2,1,20,24,22,23,21,15,14,18,17,11,10,19,13,12,16,5,9,7,8,6},
  {0,2,1,4,3,10,12,11,9,8,5,7,6,14,13,20,21,22,24,23,15,16,17,19,18},
  {3,19,17,0,16,23,9,22,20,6,13,14,12,10,11,18,4,2,15,1,8,24,7,5,21},
  {1,0,7,9,8,6,5,2,4,3,11,10,12,14,13,21,20,22,19,18,16,15,17,24,23},
  {18,2,1,15,16,23,24,21,20,22,13,14,12,10,11,3,4,19,0,17,8,7,9,5,6},
  {6,2,1,9,8,7,0,5,4,3,11,10,12,14,13,22,20,21,24,23,16,17,15,19,18},
  {6,21,22,23,16,19,0,17,20,15,14,13,12,11,10,9,4,7,24,5,8,1,2,3,18},
  {6,3,22,1,16,19,0,17,20,15,14,13,12,11,10,9,4,7,24,5,8,23,2,21,18},
  {4,23,22,21,0,15,16,17,18,19,14,13,12,11,10,5,6,7,8,9,24,3,2,1,20},
  {4,23,22,21,0,9,16,17,18,5,14,13,12,11,10,19,6,7,8,15,24,3,2,1,20},
  {2,19,0,18,16,7,9,5,8,6,22,21,20,23,24,17,4,15,3,1,12,11,10,13,14},
  {2,6,0,9,8,7,1,5,4,3,22,21,20,23,24,17,16,15,19,18,12,11,10,13,14},
  {0,4,3,2,1,20,24,8,7,21,15,16,18,17,19,10,11,13,12,14,5,9,23,22,6},
  {0,2,1,4,3,10,12,11,13,14,5,7,6,8,9,20,17,16,24,23,15,22,21,19,18},
  {18,3,17,1,16,21,24,22,20,23,13,14,12,10,11,19,4,2,0,15,8,5,7,9,6},
  {6,3,7,1,8,9,0,2,4,5,11,10,12,14,13,23,20,22,24,21,16,19,17,15,18},
  {4,23,22,21,0,15,8,17,6,19,14,13,12,11,10,5,18,7,16,9,24,3,2,1,20},
  {4,21,22,23,0,19,8,17,6,15,14,13,12,11,10,9,18,7,16,5,24,1,2,3,20},
  {24,11,21,13,20,9,12,22,23,5,14,1,6,3,10,19,17,16,18,15,4,2,7,8,0},
  {24,6,7,22,20,9,1,2,12,5,14,21,8,23,10,19,16,18,17,15,4,11,3,13,0},
  {24,2,1,18,20,9,7,6,8,5,14,21,16,23,10,19,12,22,3,15,4,11,17,13,0},
  {24,2,1,8,20,9,12,22,3,5,14,21,6,23,10,19,17,16,18,15,4,11,7,13,0},
  {0,3,4,1,2,20,18,22,16,17,15,13,24,11,19,10,8,9,6,14,5,23,7,21,12},
  {0,3,4,1,2,20,18,9,16,7,15,13,24,11,19,10,8,22,6,14,5,23,17,21,12},
  {0,2,1,4,3,15,18,17,16,19,20,23,24,14,13,5,8,7,6,9,10,22,21,11,12},
  {0,2,1,4,3,15,18,17,16,19,20,14,24,23,11,5,8,7,6,9,10,22,21,13,12},
  {18,2,1,15,16,23,24,9,20,7,13,14,12,10,11,3,4,19,0,17,8,22,21,5,6},
  {6,2,1,9,8,7,0,5,4,3,11,10,12,14,13,17,20,15,24,23,16,22,21,19,18},
  {18,3,17,1,16,9,24,22,20,5,11,10,12,14,13,19,4,2,0,15,8,23,7,21,6},
  {6,3,7,1,8,9,0,2,4,5,13,14,12,10,11,19,20,22,24,15,16,23,17,21,18},
  {12,14,10,13,11,22,24,20,23,21,2,4,0,3,1,17,19,15,18,16,7,9,5,8,6},
  {6,5,9,8,7,1,0,4,3,2,21,20,24,23,22,16,15,19,18,17,11,10,14,13,12},
  {0,4,3,2,1,20,24,23,22,21,15,19,18,17,16,10,14,13,12,11,5,9,8,7,6},
  {0,2,1,4,3,10,12,11,14,13,5,7,6,9,8,20,22,21,24,23,15,17,16,19,18},
  {3,6,22,0,16,9,1,17,20,5,14,13,12,11,10,19,4,7,23,15,8,24,2,18,21},
  {3,6,22,0,8,15,1,17,4,19,14,13,12,11,10,5,20,7,23,9,16,24,2,18,21},
  {1,0,22,18,16,15,21,17,20,19,14,13,12,11,10,5,4,7,3,9,8,6,2,24,23},
  {1,0,22,18,8,9,21,17,4,5,14,13,12,11,10,19,20,7,3,15,16,6,2,24,23},
  {6,21,7,23,16,9,0,2,20,5,13,14,12,10,11,19,4,22,24,15,8,1,17,3,18},
  {6,3,7,1,16,15,0,2,20,19,13,14,12,10,11,5,4,22,24,9,8,23,17,21,18},
  {24,5,22,19,20,1,18,17,16,21,14,13,12,11,10,23,8,7,6,3,4,9,2,15,0},
  {24,5,22,15,20,1,18,17,16,23,14,13,12,11,10,3,8,7,6,21,4,19,2,9,0},
  {4,23,22,21,0,19,16,17,18,15,14,13,12,11,10,9,6,7,8,5,24,3,2,1,20},
  {4,23,22,21,0,19,8,17,6,15,14,13,12,11,10,9,18,7,16,5,24,3,2,1,20},
  {3,6,7,0,16,23,1,2,20,19,13,14,12,10,11,18,4,22,15,9,8,24,17,5,21},
  {1,0,17,23,8,6,5,22,4,24,11,10,12,14,13,21,20,2,19,18,16,15,7,3,9},
  {1,0,17,18,16,23,21,22,20,24,11,10,12,14,13,19,4,2,3,15,8,6,7,5,9},
  {1,0,17,18,8,6,5,22,4,24,11,10,12,14,13,21,20,2,3,23,16,15,7,19,9},
  {1,0,17,15,16,23,21,22,20,24,11,10,12,14,13,3,4,2,19,18,8,6,7,5,9},
  {1,0,17,9,8,15,21,22,4,3,11,10,12,14,13,5,20,2,19,18,16,6,7,24,23},
  {18,9,7,15,16,23,24,2,20,1,13,14,12,10,11,3,4,22,0,21,8,19,17,5,6},
  {6,5,7,9,8,1,0,2,4,3,13,14,12,10,11,23,20,22,24,21,16,19,17,15,18},
  {3,6,22,0,16,23,1,17,20,19,13,14,12,10,11,18,4,7,15,9,8,24,2,5,21},
  {1,0,22,23,8,6,5,17,4,24,11,10,12,14,13,21,20,7,19,18,16,15,2,3,9},
  {1,0,22,9,8,15,21,17,4,3,11,10,12,14,13,5,20,7,19,18,16,6,2,24,23},
  {1,0,17,18,16,23,21,22,20,24,14,13,12,11,10,19,4,2,3,15,8,6,7,5,9},
  {18,3,17,1,16,9,24,22,20,5,13,14,12,10,11,19,4,2,0,15,8,23,7,21,6},
  {6,3,7,1,8,9,0,2,4,5,11,10,12,14,13,19,20,22,24,15,16,23,17,21,18}
};

int row[]={0,0,0,0,0,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4},
    col[]={0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4,0,1,2,3,4};
bool bdia[]={T,F,F,F,F,F,T,F,F,F,F,F,T,F,F,F,F,F,T,F,F,F,F,F,T},
     fdia[]={F,F,F,F,T,F,F,F,T,F,F,F,T,F,F,F,T,F,F,F,T,F,F,F,F};
bool fill[NN]; int rNum[N], cNum[N], bNum, fNum, rSum[N], cSum[N], bSum, fSum;

int cmpSquare(schar *x, schar *a) {
//  ---------
  int i=0; while (i++<NN) if (*x<*a) return -1; else if (*x++>*a++) return 1; return 0;
} // cmpSquare

int findAspect(schar *x, struct t_group *g) {
//  ----------
  for (int i=0; i<GAindex; ++i) if (cmpSquare(x, g[i].U.cpos)==0) return T; return F;
} // findAspect

void insertAspect(struct t_group *g) {
//   ------------
  schar *p=groupAspects[GAindex++].U.cpos; for (int i=0; i<NN; ++i) { p[i]=g->U.cpos[i]; }
} // insertAspect

void pushGroup(schar *x) {
//   ---------
  struct t_group g; for (int i=0; i<NNd4; ++i) g.U.ovly[i]=0xFFFFFFFF;
  for (int i=0; i<NN; ++i) {
    if (x[i]==L) g.U.cpos[i]=i; else if (g.U.cpos[i]<0) { schar t=NM-x[i];
      for (int j=i+1; j<NN; ++j) if (x[j]==t) { g.U.cpos[i]=j; g.U.cpos[j]=i; break; }
    }
  }
  if (GAindex>0) if (findAspect(g.U.cpos, groupAspects)) return; insertAspect(&g);
} // pushGroup

// zero based
int rot90[NN], rot180[NN], rot270[NN], rotY[NN], rotXY[NN], rotX[NN], rotYX[NN];

void makeTurnTables() {
//   -------------
  for (int r=0; r<N; r++)
    for (int c=0; c<N; c++) {
      rot90[r*N+c] =c*N+M-r;     // rotate 90
      rot180[r*N+c]=(M-r)*N+M-c; // rotate 180
      rot270[r*N+c]=(M-c)*N+r;   // rotate-90
      rotY[r*N+c]  =r*N+M-c;     // rotate Y
      rotXY[r*N+c] =(M-c)*N+M-r; // rotate XY
      rotX[r*N+c]  =(M-r)*N+c;   // rotate X
      rotYX[r*N+c] =c*N+r;       // rotate YX
    }
} // makeTurnTables

void getGroupAspects(schar *gin) {
//   ---------------
  schar a[NN], a1[NN], a2[NN], a3[NN], a4[NN], a5[NN], a6[NN], a7[NN];
  for (int i=0; i<NN; ++i) if (gin[i]==i) a[i]=L; else a[i]=-1; int j=0;
  for (int i=0; i<NN; ++i) if (a[i]<0) { if (j==L) ++j; a[i]=j; a[gin[i]]=NM-j++; }
  for (int i=0; i<NN; ++i) {
    const int x=a[i]; a1[rot90[i]]=x; a2[rot180[i]]=x; a3[rot270[i]]=x;
    a4[rotY[i]]=x;    a5[rotXY[i]]=x; a6[rotX[i]]=x;   a7[rotYX[i]]=x;
  }
  pushGroup(a);  pushGroup(a1); pushGroup(a2); pushGroup(a3);
  pushGroup(a4); pushGroup(a5); pushGroup(a6); pushGroup(a7);
} // getGroupAspects

void getAspects(schar *g) { GAindex=0; getGroupAspects(g); }
//   ----------

void init(int x[NN], bool U[NN], bool wasM[NN]) {
//   ----
  for (int i=0; i<NN; ++i) { x[i]=-1; U[i]=F; wasM[i]=F; } // Must init X[i] for Lim0 calculation.
  for (int i=0; i<N; ++i) { rNum[i]=0; cNum[i]=0; rSum[i]=0; cSum[i]=0; }
  bNum=0; fNum=0; bSum=0; fSum=0; U[12]=T;
} // init

void initialize(schar *f, int x[NN], bool U[NN], bool wasM[NN], bool *fNumN) {
//   ----------
  init(x, U, wasM);
  for (int i=0; i<NN; ++i) if (f[i]==i) {
    x[i]=12; rNum[row[i]]=1; cNum[col[i]]=1; rSum[row[i]]=12; cSum[col[i]]=12;
    if (bdia[i]) { bNum=1; bSum=12; } if (fdia[i]) { fNum=1; fSum=12; } break;
  }
  for (int i=0; i<NN; ++i) fill[i]=T;
  for (int i=0; i<NN; i+=O) if (fill[i]) fill[f[i]]=F; *fNumN=T;
  for (int i=M; i<NM; i+=M) if (fill[i]) { fill[f[i]]=F; *fNumN=F; }
  for (int i=0; i<NN; ++i) if (fill[i]&!bdia[i]&!fdia[i]) fill[i]=i<f[i];
  //printf("fill\n"); boolPrint(fill,stdout);
} // initialize

bool putbd(schar *f, int x[NN], const int i, const uint u, bool U[NN]) {
//   -----
  const int j=f[i]; const uint v=NM-u; x[i]=u; x[f[i]]=v; U[u]=T; U[v]=T; 
  ++rNum[row[i]]; ++cNum[col[i]]; rSum[row[i]]+=u; cSum[col[i]]+=u;
  ++bNum; bSum+=u; if (fdia[i]) { ++fNum; fSum+=u; }
  ++rNum[row[j]]; ++cNum[col[j]]; rSum[row[j]]+=v; cSum[col[j]]+=v;
  if (bdia[j]) { ++bNum; bSum+=v; } if (fdia[j]) { ++fNum; fSum+=v; }
  if ((x[0]>(int)v)&((j==NM)|(j==M)|(j==NI))) return F;
  if ((fNum==N)&(x[NI]<x[M])) return F;
  if ((i==NM)&(x[0]>(int)u)) return F; return (bSum<=S)&(fSum<=S);
} // putbd

void cutbd(schar *f, int x[NN], const int i, const uint u, bool U[NN]) {
//   -----
  const int j=f[i]; const uint v=NM-u; U[u]=F; U[v]=F; 
  --rNum[row[i]]; --cNum[col[i]]; rSum[row[i]]-=u; cSum[col[i]]-=u;
  --bNum; bSum-=u; if (fdia[i]) { --fNum; fSum-=u; }
  --rNum[row[j]]; --cNum[col[j]]; rSum[row[j]]-=v; cSum[col[j]]-=v;
  if (bdia[j]) { --bNum; bSum-=v; } if (fdia[j]) { --fNum; fSum-=v; }
} // cutbd

bool putfd(schar *f, int x[NN], const int i, const uint u, bool U[NN]) {
//   -----
  const int j=f[i]; const uint v=NM-u; x[i]=u; x[f[i]]=v; U[u]=T; U[v]=T; 
  ++rNum[row[i]]; ++cNum[col[i]]; rSum[row[i]]+=u; cSum[col[i]]+=u; ++fNum; fSum+=u;
  ++rNum[row[j]]; ++cNum[col[j]]; rSum[row[j]]+=v; cSum[col[j]]+=v;
  if (fdia[j]) { ++fNum; fSum+=v; } if ((x[M]>(int)v)&(j==NI)) return F; 
  return fSum<=S;
} // putfd

void cutfd(schar *f, int x[NN], const int i, const uint u, bool U[NN]) {
//   -----
  const int j=f[i]; const uint v=NM-u; U[u]=F; U[v]=F; 
  --rNum[row[i]]; --cNum[col[i]]; rSum[row[i]]-=u; cSum[col[i]]-=u; --fNum; fSum-=u;
  --rNum[row[j]]; --cNum[col[j]]; rSum[row[j]]-=v; cSum[col[j]]-=v; if (fdia[j]) { --fNum; fSum-=v; }
} // cutfd

bool putr(schar *f, int x[NN], const int i, const uint u, bool U[NN]) {
//   ----
  const int j=f[i]; const uint v=NM-u; x[i]=u; x[f[i]]=v; U[u]=T; U[v]=T; 
  ++rNum[row[i]]; ++cNum[col[i]]; rSum[row[i]]+=u; cSum[col[i]]+=u;
  ++rNum[row[j]]; ++cNum[col[j]]; rSum[row[j]]+=v; cSum[col[j]]+=v;
  return (rSum[row[i]]<=S)&(cSum[col[i]]<=S)&(rSum[row[f[i]]]<=S)&(cSum[col[f[i]]]<=S);
} // putr

void cutr(schar *f, int x[NN], const int i, const uint u, bool U[NN]) {
//   ----
  const int j=f[i]; const uint v=NM-u; U[u]=F; U[v]=F; 
  --rNum[row[i]]; --cNum[col[i]]; rSum[row[i]]-=u; cSum[col[i]]-=u;
  --rNum[row[j]]; --cNum[col[j]]; rSum[row[j]]-=v; cSum[col[j]]-=v;
} // cutr

int makeSquares(const int g, FILE *wfp) {
//  -----------
  enum { bdf, fdf, rowf, bdb, fdb, rowb, out } next; int count=0; getAspects(groupForm[g]);
  //printf("aspects %d\n", GAindex); //return T;
  //for (int a=0; a<GAindex; ++a) {
  //  schar *f=groupAspects[a].U.cpos;
  //  printf("a %d\n", a); for (int k=0; k<NN; ++k) printf("%3d", f[k]); putchar('\n');
  //}
  for (int a=0; a<GAindex; ++a) {
    schar *f=groupAspects[a].U.cpos; bool U[NN], wasM[NN], fNumN=F;
    //for (int k=0; k<NN; ++k) printf("%3d", f[k]); putchar('\n');
    const int I=(f[0]==0)?O:0; int x[NN], i=I, t; uint u=0;
    bool bbreak=F; next=bdf; initialize(f,x,U,wasM,&fNumN);
    const uint Lim0=(x[NI]==L)?10:((x[M]==L)|(x[NM]==L))?11:15; // max x[0], empirical
    do {
      switch (next) {
      case bdf:
        if (fill[i]) {
          if (bNum==M) {
            wasM[i]=T; u=S-bSum;
            if ((u>NM)||(U[u])) { i-=O; next=bdb; }
            else { if (putbd(f,x,i,u,U)) { if (i<NM) { i+=O; u=0; }
            else if (fNumN) { i=1; u=0; next=rowf; } else { i=M; u=x[0]+1; next=fdf; }} else next=bdb; }
          } else {
            wasM[i]=F; while ((u<NN)&&U[u]) ++u;
            if (u>NM) { i-=O; next=bdb; } else if (putbd(f,x,i,u,U)) { if ((i<NM)&(bNum<N)) { i+=O; u=0; }
            else if (fNumN) { i=1; u=0; next=rowf; } else { i=M; u=x[0]+1; next=fdf; }} else next=bdb;
          }
        } else {
          if (i<NM) { i+=O; u=0; } else if (fNumN) { i=1; u=0; next=rowf; } else { i=M; u=x[0]+1; next=fdf; }
        }
        break;
      case fdf:
        if (fill[i]) {
          if (fNum==M) {
            wasM[i]=T; u=S-fSum;
            if ((u>NM)||(U[u])) { i-=M; if (i<M) { i=NM; next=bdb; } else { if (i==12) i-=M; next=fdb; }}
            else {
              if (putfd(f,x,i,u,U)) { if (i<NI) { i+=M; if (i==12) i+=M; u=0; }
              else { if (x[NI]<x[M]) next=fdb; else { i=1; next=rowf; u=0; }}} else next=fdb;
            }
          } else {
            wasM[i]=F; while ((u<NN)&&U[u]) ++u;
            if (u>NM) { i-=M; if (i<M) { i=NM; next=bdb; } else { if (i==12) i-=M; next=fdb; }
            } else if (putfd(f,x,i,u,U)) {
              if (i<NI) { i+=M; if (i==12) i+=M; u=0; }
              else { if (x[NI]<x[M]) next=fdb; else { i=1; next=rowf; u=0; } }
            } else next=fdb;
          }
        } else {
          if (i<NI) { i+=M; if (i==12) i+=M; u=0; }
          else { if (x[NI]<x[M]) next=fdb; else { i=1; next=rowf; u=0; } }
        }
        break;
      case rowf:
        if (bdia[i]|fdia[i]) ++i;   
        if (fill[i]) {
          if ((rNum[row[i]]==M)|(cNum[col[i]]==M)) {
            wasM[i]=T; u=(rNum[row[i]]==M)?S-rSum[row[i]]:S-cSum[col[i]];
            if ((u>NM)||(U[u])) { --i; next=rowb; } else {
              if (putr(f,x,i,u,U)) { if (rNum[row[i]]==N) {
                if (row[i]==M) next=out; else { t=row[i]; while (row[++i]==t); } } else ++i; u=0; 
              } else next=rowb;
            }
          } else {
            wasM[i]=F; while ((u<NN)&&U[u]) ++u;
            if (u>NM) { --i; next=rowb; }
            else if (putr(f,x,i,u,U)) { if (i<NL) { ++i; u=0; } else next=out; } else next=rowb;
          }
        } else if (i<NL) { ++i; u=0; } else next=out;
        break;

      case bdb:
        if (i<I) bbreak=T; else {
          if (fill[i]) { u=x[i]; cutbd(f,x,i,u,U); if (wasM[i]) i-=O; else next=bdf; } else i-=O;
          u=x[i]+1; if ((i==0)&(u>Lim0)) bbreak=T;
        }
        break;

      case fdb:
        if (i<M) { i=NM; next=bdb; } else {
          if (fill[i]) { u=x[i]; cutfd(f,x,i,u,U); if (wasM[i]) { i-=M; if (i==12) i-=M; } else next=fdf;
          } else { i-=M; if (i==12) i-=M; } if (i<M) { i=NM; next=bdb; } u=x[i]+1;
        }
        break;
      case rowb:
        if (bdia[i]|fdia[i]) --i;
        if (i<=0) { if (fNumN) { i=NM; next=bdb; } else { i=NI; next=fdb; }} else {
          if (fill[i]) { u=x[i]; cutr(f,x,i,u,U); if (wasM[i]) --i; else next=rowf; } else --i;
          if (bdia[i]|fdia[i]) --i; if (i<=0) { if (fNumN) { i=NM; next=bdb; } else { i=NI; next=fdb; } }
        } u=x[i]+1;
        break;
      default:
        txtPrint(x,wfp); ++count; next=rowb; i=NL; break;
      }
      if (bbreak) break;
    } while (T);
  }
  return count;
} // makeSquares
//=================================================== 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\n\0", local); printf("\n%s", dateTime);
  }
} // outputLocalTime

void printElapsedTime(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);
} // printElapsedTime

int main() {
//  ----
  bool ok=F; outputLocalTime();
  if (allocateStore()) {
    makeTurnTables();
    if (openDir()) {
      do {
        int num1, num2; printf("\nGroup number or number range, (1..%d)? ", maxGroup);
        if (getInts(&num1, &num2, maxGroup)) {
          for (int group=num1; group<=num2; ++group) {
	    FILE *wtfp=NULL;
	    if ((wtfp=open_File(group,"txt"))!=NULL) {
              FILE *whfp=NULL;
	      if ((whfp=open_File(group,"html"))!=NULL) {
                time_t startTime=time(NULL); writeError=F;
                if (htmlPrint(group,groupSize[group],groupForm[group],whfp)) {
                  const int count=makeSquares(group, wtfp); ok=count==groupSize[group];
                  printf("group size %d squares %d\n", groupSize[group], count);
                  if (count!=groupSize[group]) printf("\aProgram error: bad count.\n");
                } else writeError=T;
                if (writeError) { perror("\a\nError writing file"); break; } else ok=T;
                printElapsedTime(startTime); fclose(whfp);
              }
              fclose(wtfp);
            }
	  } // for (int group
        } // if (getInts
        if (ok) { printf("\nContinue? y (yes) or n (no): "); if (!getY()) break; }
      } while (T);
    } // if (openDir())
    freeStore();
  }
  printf("\nHit return to close the console.");
  while (T) if (getchar()=='\n') break; return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main