/*
 *  File:    CopySquaresByType.cpp
 *  Author:  S Harry White
 *  Created: 2012-02-01
 *  Updated: 2020-06-13
 *    Add prime number magic squares
 * Updated: 2020-07-13
 *   Add ultramagic, compact, complete, most-perfect for prime number magic squares
 * Updated: 2020-09-27
 *   Fix open_input when file name includes .txt.
 * Updated: 2020-11-06
 *   Add reversible squares. Add odd-associative and even-associative.
 * Updated: 2020-11-16
 *   Add associative and pandiagonal for notMagic, otherMagic squares.
 * Updated: 2020-11-24
 *   Reduce isCompactK time, (e.g., from 12 hours to 8 minutes for compact2048).
 * Updated: 2021-01-04
 *   Add type "transpose parity", i.e., X[r][c]&1 matches X[c][r]&1.
 * Updated: 2021-01-05
 *   Add type "symmetric parity": cells symmetrically opposite from the center have the same parity.
 * Updated: 2021-01-06
 *   Add type "axial parity": cells opposite left-right of the middle and/or up-down from the middle
 *   have the same parity.
 * Updated: 2021-04-29
 *   Add cyclic and semi-cyclic for pandiagonal Latin squares.
 * Updated: 2021-06-21
 *   Report cyclic if rows and columns are cyclic; otherwise,
 *   report semi-cyclic if any of rows, or columns, or \diagonals, or /diagonals are cyclic.
 * Updated: 2021-08-11
 *   Add type doubly self-orthogonal.
 * Updated: 2021-12-29
 *   Replace & with && where necessary.
 * Updated: 2022-07-20
 *   Add non-square rectangles.
 * Updated: 2022-08-07
 *  Add isSelfComplement for non-square rectangles.
 * Updated: 2022-08-17
 *  Add isPandiagonal for non-square rectangles.
 *  Updated: 2022-10-02
 *   Add isCompact, isBentDiagonal, and isFranklin for non-square rectangles.
 *  Updated: 2023-01-19
 *    Change to compile with g++ for macOS.
 *  Updated: 2023-01-25
 *    Fix isBentDiagonal for non-square rectangles.
 *  Updated: 2023-03-28
 *    Fix store allocation. Remove allocatedR=0, allocatedC=0 from initGlobals().
 */

/*
 *  Copies squares from files.
 *  Console inputs:
 *    . the order of the squares
 *    . the squares file name
 *    . an include type list
 *    . an exclude type list
 *  Output:
 *    to file: copy of the selected squares
 */

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

const int left=1, right=2, up=4, down=8, startSize=32;
int R, C,           // The order of the rectangle.
    M,              // R-1
    RC,             // R*C
    Nk,             // k range of compact, Uzigzagk, Vzigzagk
    Sum2, Sum4,
    **xSquare=NULL, **ySquare=NULL, **tSquare=NULL, allocatedR, allocatedC, squareNum, *opp,
    smallestRead, biggestRead, fieldWidth, numSquares, printNum; // print a message at this number of squares

struct t_ways { char ways, lrud; };
struct t_ways bent, parity, cyclic, semiCyclic, *Uzig, *Vzig, *VzigA;
const bool F=false, T=true; bool zeroBase, *numUsed=NULL, **QR, *compact, readError ;

typedef unsigned int Uint;
Uint magicConstant, rowConstant, colConstant,
     halfMagicConstant, // 1 base,
                        // (halfMagicConstant valid only for R and C doubly even).
     magicSum, rowSum, colSum,
     halfMagicSum,      // Use for 0 or 1 base,
                        // (halfMagicSum valid only for R and C doubly even).
     chkSum;            // Used for otherMagic squares

enum magicType {
  notMagic, normalSemiMagic, otherSemiMagic, normalMagic, otherMagic,
  biMagic, triMagic, Prime, Prime1,
  adjacentCorner, adjacentSide, Associative, oddAssociative, evenAssociative, nearAssociative,
  Concentric, Bordered, Bordering, Symlateral, nearSymlateral,
  Pandiagonal, nearPandiagonal, Complete, Compact, Pandiagonal1Way,
  bentDiagonal, Franklin, Uzigzag, Vzigzag, VzigzagA, selfComplement,
  LS, DLS, AssociativeDLS, nearAssociativeDLS, PLS, cyclicPLS, semiCyclicPLS, WPLS, Sym, DSym, CSym, nearCSym,
  nfr, nfc, nfr_nfc, selfTranspose, nbd, OLS, SOLS, DSOLS,
  Reversible, axialParity, symmetricParity, transposeParity
};

const magicType firstType=notMagic, lastType=transposeParity,
      lastBasicType=otherMagic, lastMagicType=selfComplement;
magicType squareType, prevType;

struct t_type { bool isType; const char *name; };
const int numTypes=lastType-firstType+1;
struct t_type squareTypes[numTypes]=
{
  {F, "not magic"},
  {F, "normal semi-magic"},
  {F, "other semi-magic"},
  {F, "normal magic"},
  {F, "other magic"},
  {F, "bimagic"},
  {F, "trimagic"},
  {F, "prime"},
  {F, "prime & 1"},
  {F, "adjacent corner"},
  {F, "adjacent side"},
  {F, "associative"},
  {F, "odd-associative"},
  {F, "even-associative"},
  {F, "near-associative"},
  {F, "concentric"},
  {F, "bordered"},
  {F, "bordering"},
  {F, "symlateral"},
  {F, "near-symlateral"},
  {F, "pandiagonal"},
  {F, "near-pandiagonal"},
  {F, "complete"},
  {F, "compact"},
  {F, "pandiagonal 1-way"},
  {F, "bent diagonal"},
  {F, "Franklin"},
  {F, "U zigzag"},
  {F, "V zigzag"},
  {F, "V zigzagA"},
  {F, "self-complement"},
  {F, "Latin"},
  {F, "diagonal Latin"},
  {F, "associative"},
  {F, "near-associative"},
  {F, "pandiagonal"},
  {F, "cyclic"},
  {F, "semi-cyclic"},
  {F, "weakly pandiagonal"},
  {F, "axial symmetric"},
  {F, "double axial symmetric"},
  {F, "center symmetric"},
  {F, "near center symmetric"},
  {F, "nfr"},
  {F, "nfc"},
  {F, "nfr nfc"},
  {F, "self-transpose"},
  {F, "natural \\diagonal"},
  {F, "orthogonal pair"},
  {F, "self-orthogonal"},
  {F, "doubly self-orthogonal"},
  {F, "reversible"},
  {F, "axial parity"},
  {F, "symmetric parity"},
  {F, "transpose parity"}
};

struct t_bool5 { bool b[5]; };
bool getType[numTypes], excludeType[numTypes], *getTypeCompact, *excludeTypeCompact,
     getTypeBent[5], excludeTypeBent[5], getTypeCyclic[5], excludeTypeCyclic[5],
     getTypeSemiCyclic[5], excludeTypeSemiCyclic[5], getTypeParity[5], excludeTypeParity[5];
struct t_bool5 *getTypeUzig, *excludeTypeUzig,
               *getTypeVzig, *excludeTypeVzig, *getTypeVzigA, *excludeTypeVzigA;

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

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

typedef magicType (*t_mtippcici)(int **, const int, const int); t_mtippcici isMagic, isNormal;
magicType isMagicSq(int **x, const int nr, const int nc) { // nr==nc
//        ---------
  int sumX, sumY, sumXY=0, sumYX=0; const int nm1=nr-1;
  for (int i=0; i<nr; i++) {
    sumX=0; sumY=0; for (int j=0; j<nr; j++) { sumX+=x[i][j]; sumY+=x[j][i]; } if (i==0) chkSum=sumX;
    if ((sumX!=chkSum)|(sumY!=chkSum)) return notMagic; sumXY+=x[i][nm1-i]; sumYX+=x[i][i];
  }
  return basicType[(sumXY==chkSum)&&(sumYX==chkSum)][F];
} // isMagicSq

magicType isMagicRect(int **x, const int nr, const int nc) {
//        -----------
  for (int r=0; r<nr; r++) {
    int sum=0; for (int c=0; c<nc; c++) sum+=x[r][c];
    if (r==0) chkSum=sum; else if (sum!=chkSum) return notMagic;
  }
  for (int c=0; c<nc; c++) {
    int sum=0; for (int r=0; r<nr; r++) sum+=x[r][c];
    if (c==0) chkSum=sum; else if (sum!=chkSum) return notMagic;
  }
  return otherMagic;
} // isMagicRect

magicType isNormalSq(int **x, const int nr, const int nc) { // nr==nc
//        ----------
  const int min=zeroBase?0:1, max=RC+min-1, nm1=nr-1; Uint sumX, sumY, sumXY=0, sumYX=0;
  for (int i=min; i<=max; i++) numUsed[i]=F;
  for (int i=0; i<nr; i++) {
    sumX=0; sumY=0;
    for (int j=0; j<nr; j++) {
      const int tmp=x[i][j]; numUsed[tmp]=T; sumX+=tmp; sumY+=x[j][i];
    }
    if (i==0) chkSum=sumX; if ((sumX!=chkSum)|(sumY!=chkSum)) return notMagic;
    sumXY+=x[i][nm1-i]; sumYX+=x[i][i];
  }

  bool allNumbersUsed=T;
  for (int i=min; i<=max; i++) if (!numUsed[i]) { allNumbersUsed=F; break; }
  return basicType[(sumXY==chkSum)&(sumYX==chkSum)][allNumbersUsed&(chkSum==magicSum)];
} // isNormalSq

magicType isNormalRect(int **x, const int nr, const int nc) {
//        ------------
  const int min=zeroBase?0:1, max=RC+min-1, nm1=nr-1; Uint chkSumR, chkSumC;
  for (int i=min; i<=max; i++) numUsed[i]=F;
  for (int r=0; r<nr; r++) {
    int sum=0; for (int c=0; c<nc; c++) { const int tmp=x[r][c]; numUsed[tmp]=T; sum+=tmp; }
    if (r==0) chkSumR=sum; else if (sum!=chkSumR) return notMagic;
  }
  for (int c=0; c<nc; c++) {
    int sum=0; for (int r=0; r<nr; r++) sum+=x[r][c];
    if (c==0) chkSumC=sum; else if (sum!=chkSumC) return notMagic;
  }
  bool allNumbersUsed=T;
  for (int i=min; i<=max; i++) if (!numUsed[i]) { allNumbersUsed=F; break; }
  return basicType[T][allNumbersUsed&(chkSumR==rowSum)&(chkSumC==colSum)];
} // isNormalRect

magicType isLatin(int **x, const int n) { // R==C
//        -------
  const int min=zeroBase?0:1, max=n+min-1, m=n-1; bool ls=T, dls=T;
  for (int r=0; r<n; ++r) {
    for (int i=min; i<=max; i++) numUsed[i]=F; for (int c=0; c<n; ++c) numUsed[x[r][c]]=T;
    for (int i=min; i<=max; ++i) if (!numUsed[i]) { ls=F; break; } if (!ls) break;
  }
  if (ls) for (int c=0; c<n; ++c) {
    for (int i=min; i<=max; i++) numUsed[i]=F; for (int r=0; r<n; ++r) numUsed[x[r][c]]=T;
    for (int i=min; i<=max; ++i) if (!numUsed[i]) { ls=F; break; } if (!ls) break;
  }
  if (ls) {
    for (int i=min; i<=max; i++) numUsed[i]=F; for (int r=0; r<n; ++r) numUsed[x[r][r]]=T;
    for (int i=min; i<=max; ++i) if (!numUsed[i]) { dls=F; break; }
    if (dls) {
      for (int i=min; i<=max; i++) numUsed[i]=F; for (int r=0; r<n; ++r) numUsed[x[r][m-r]]=T;
      for (int i=min; i<=max; ++i) if (!numUsed[i]) { dls=F; break; }
    }
  } else dls=F;
  return dls?DLS:ls?LS:isNormalSq(x, n, n);
} // isLatin

void initGlobals() {
//  ------------
  Nk=(R<=C) ? R/2+1 : C/2+1;
  if (R==C) { isMagic=isMagicSq; isNormal=isNormalSq; }
  else { isMagic=isMagicRect; isNormal=isNormalRect; }
  M=R-1; RC=R*C; readError=F; bell=F; squareNum=0; numSquares=0; printNum=100000;
  if (R==C) {
    if ((R&1)==1) /* odd */ magicConstant=(RC+1)/2*R;
    else {
      if ((R&2)==0) { // doubly even
        halfMagicConstant=(RC+1)*(R/4); // magicConstant/2 might give F results on overflow
        magicConstant=halfMagicConstant+halfMagicConstant; // overflow ok
      } else /* singly even */ magicConstant=(RC+1)*(R/2);
      rowConstant=magicConstant; colConstant=magicConstant;
    }
  } else { const int S2=RC+1; rowConstant=(C&1) ? S2/2*C : C/2*S2; colConstant=(R&1) ? S2/2*R : R/2*S2; }
} // initGlobals

void initListTypes() {
//   -------------
  for (int i=firstType; i<=lastType; ++i) { getType[i]=F; excludeType[i]=F; }
  for (int i=0; i<5; ++i) { getTypeBent[i]=F; excludeTypeBent[i]=F; 
                            getTypeCyclic[i]=F; excludeTypeCyclic[i]=F;
                            getTypeSemiCyclic[i]=F; excludeTypeSemiCyclic[i]=F; 
                            getTypeParity[i]=F; excludeTypeParity[i]=F; }
  for (int i=0; i<Nk; ++i) {
    for (int j=0; j<5; ++j) {
      getTypeVzig[i].b[j]=F; excludeTypeVzig[i].b[j]=F; getTypeUzig[i].b[j]=F; excludeTypeUzig[i].b[j]=F;
    }
    getTypeCompact[i]=F; excludeTypeCompact[i]=F;
  }
  for (int i=0; i<=R; ++i) for (int j=0; j<5; ++j) { getTypeVzigA[i].b[j]=F; excludeTypeVzigA[i].b[j]=F; }
} // initListTypes

void initSquareTypes() {
//   ---------------
  for (int i=firstType; i<=lastType; ++i) squareTypes[i].isType=F;
  bent.ways=0; bent.lrud=0; cyclic.ways=0; cyclic.lrud=0;
  semiCyclic.ways=0; semiCyclic.lrud=0; parity.ways=0; parity.lrud=0;
  for (int k=0; k<Nk; ++k)
    { Uzig[k].ways=0; Uzig[k].lrud=0; Vzig[k].ways=0; Vzig[k].lrud=0; compact[k]=0; }
  for (int k=0; k<=R; ++k) { VzigA[k].ways=0; VzigA[k].lrud=0; }
} // initSquareTypes
//================================================= store ===========================================

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

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

void freeStore() {
//   ----------
  freeRectangle(&xSquare,allocatedR); freeRectangle(&ySquare,allocatedR);
  freeRectangle(&tSquare,allocatedR); freeBoolSquare(&QR, allocatedR+1); allocatedR=0; allocatedC=0;
  free(opp); opp=NULL; free(numUsed); numUsed=NULL;
  free(Uzig); Uzig=NULL; free(Vzig); Vzig=NULL; free(VzigA); VzigA=NULL; free(compact); compact=NULL;
  free(getTypeCompact); getTypeCompact=NULL; free(excludeTypeCompact); excludeTypeCompact=NULL;
  free(getTypeUzig);    getTypeUzig=NULL;    free(excludeTypeUzig);    excludeTypeUzig=NULL; 
  free(getTypeVzig);    getTypeVzig=NULL;    free(excludeTypeVzig);    excludeTypeVzig=NULL;
  free(getTypeVzigA);   getTypeVzigA=NULL;   free(excludeTypeVzigA);   excludeTypeVzigA=NULL;
} // freeStore

bool allocateRectangle(int*** rectangle, const int nR, const int nC) {
//   -----------------
  bool ok; *rectangle=(int**) malloc(nR*sizeof(int*)); ok=(*rectangle!=NULL);
  if (ok) {
    int numAllocated=0;
    for(int i=0; i<nR; i++) {
      int* p=(int*) malloc(nC*sizeof(int)); (*rectangle)[i]=p; if (p==NULL) { numAllocated=i; ok=F; break; }
    }
    if (!ok) freeRectangle(rectangle, numAllocated);
  }
  return ok;
} // allocateRectangle

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

bool allocateWays(struct t_ways **x, const int size) {
//   -----------
  return ((*x=(struct t_ways*) malloc(size*sizeof(struct t_ways)))!=NULL);
} // allocateWays

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

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

bool allocate5Bools(struct t_bool5 **x, const int size) {
//   --------------
  return ((*x=(struct t_bool5*) malloc(size*sizeof(struct t_bool5)))!=NULL);
} // allocate5Bools

bool allocateStore() {
//   -------------
  bool ok=T; int nR=R, nC=C; if (nR<startSize) nR=startSize; if (nC<startSize) nC=startSize;
  if ((nR>allocatedR)|(nC>allocatedC)) {
    const int nRnC=nR*nC, nways=nR/2+1; freeStore();
    if ((ok=allocateRectangle(&xSquare, nR, nC)))
      if ((ok=allocateRectangle(&ySquare, nR, nC)))
        if ((ok=allocateRectangle(&tSquare, nR, nC)))
          if ((ok=allocateBoolSquare(&QR, nR+1)))
            if ((ok=allocateInts(&opp, nR+1)))
              if ((ok=allocateBools(&numUsed, nRnC+1)))
                if ((ok=allocateWays(&Uzig, nways)))
                  if ((ok=allocateWays(&Vzig, nways)))
                    if ((ok=allocateWays(&VzigA, nR+1)))
                      if ((ok=allocateBools(&compact, nways)))
                        if ((ok=allocateBools(&getTypeCompact, nways)))
                          if ((ok=allocateBools(&excludeTypeCompact, nways)))
                            if ((ok=allocate5Bools(&getTypeUzig, nways)))
                              if ((ok=allocate5Bools(&excludeTypeUzig, nways)))
                                if ((ok=allocate5Bools(&getTypeVzig, nways)))
                                  if ((ok=allocate5Bools(&excludeTypeVzig, nways)))
                                    if ((ok=allocate5Bools(&getTypeVzigA, nR+1)))
                                      if ((ok=allocate5Bools(&excludeTypeVzigA, nR+1)))
                                        { allocatedR=nR; allocatedC=nC; }
    if (!ok) freeStore();
  }
  return ok;
} // allocateStore
//============================================== output ================================================

bool printConflicts(const int t1, const int t2) {
//   --------------
  if ( ((t1==notMagic)|(t1==otherSemiMagic))&(t2>lastBasicType) )
    printf("\a\nUnsupported combination: %s, %s\n", squareTypes[t1].name, squareTypes[t2].name);
  else printf("\a\nType conflict: %s, %s\n", squareTypes[t1].name, squareTypes[t2].name);
  return F;
} // printConflicts

bool orderReject(const int t, bool list[numTypes], const int n) {
//   -----------
  if (list[t]) { printf("\a\nThere are no order %d %s squares.\n", n, squareTypes[t].name); return T; }
  return F;
} // orderReject

bool orderRejectRect(const int t, bool list[numTypes]) {
//   ---------------
  if (list[t]) {
    printf("\a\nThere are no order %d %d %s rectangles.\n", R, C, squareTypes[t].name); return T;
  }
  return F;
} // orderRejectRect

bool orderRejectMagic(const int t, bool list[numTypes], const int n) {
//   ----------------
  if (list[t]) { printf("\a\nThere are no order %d%s %s squares.\n",
                        n, t==normalMagic?"":" normal magic", squareTypes[t].name); return T;
  }
  return F;
} // orderRejectMagic

bool orderRejectLS(const int t, bool list[numTypes], const int n) {
//   -------------
  if (list[t]) { printf("\a\nThere are no order %d%s %s squares.\n",
                        n, t==LS?"":" LS", squareTypes[t].name); return T;
  }
  return F;
} // orderRejectLS

bool orderRejectDLS(const int t, bool list[numTypes], const int n) {
//   --------------
  if (list[t]) { printf("\a\nThere are no order %d%s %s squares.\n",
                        n, t==DLS?"":" DLS", squareTypes[t].name); return T;
  }
  return F;
} // orderRejectDLS

void putWaysBent(bool *b) {
//   -----------
  char waysBentStr[10], *s=waysBentStr; *s++=' '; *s++=b[1]?'1':b[2]?'2':'3';
  *s++='-'; *s++='w'; *s++='a'; *s++='y'; *s='\0'; printf("%s", waysBentStr);
} // putWaysBent

void putWayParity() {
//   ------------
  char wayStr[10], *s=wayStr;
  *s++=' '; *s++='1'; *s++='-'; *s++='w'; *s++='a'; *s++='y'; *s='\0'; printf("%s", wayStr);
} // putWayParity

void putWayCyclic(bool *b) {
//   ------------
  char wayStr[10], *s=wayStr; *s++=' '; *s++=b[2]?'2':b[3]?'3':'4';
  *s++='-'; *s++='w'; *s++='a'; *s++='y'; *s='\0'; printf("%s", wayStr);
} // putWayCyclic

void putWaySemiCyclic(bool *b) {
//   ----------------
  char wayStr[10], *s=wayStr; *s++=' '; *s++=b[1]?'1':b[2]?'2':'3';
  *s++='-'; *s++='w'; *s++='a'; *s++='y'; *s='\0'; printf("%s", wayStr);
} // putWaySemiCyclic

void addTypeInfo(const int i, const bool in) {
//   -----------
  bool first=F, *b; struct t_bool5 *b5;
  switch (i) {
    case Compact:
      b=in?getTypeCompact:excludeTypeCompact;
      if (b[0]) { // any > 2 ?
        first=!b[2];
        for (int j=3; j<Nk; ++j) if (b[j]) {
          if (first) printf("%d", j); else printf(", %s%d", squareTypes[i].name, j); first=F;
        }
      }
      break;
    case bentDiagonal:
      b=in?getTypeBent:excludeTypeBent; if (!b[4]) putWaysBent(b); break;
    case Uzigzag:
      b5=in?getTypeUzig:excludeTypeUzig; first=!b5[2].b[0];
      if (!first&!b5[2].b[4]) putWaysBent(b5[2].b);
      if (b5[0].b[0]) { // any k > 2?
        for (int j=3; j<Nk; ++j) {
          if (b5[j].b[0]) {
            if (first) printf("%d", j); else printf(", %s%d", squareTypes[i].name, j);
            if (!b5[j].b[4]) putWaysBent(b5[j].b); first=F;
          }
        }
      }
      break;
    case Vzigzag:
      b5=in?getTypeVzig:excludeTypeVzig; first=!b5[2].b[0];
      if (!first&!b5[2].b[4]) putWaysBent(b5[2].b);
      if (b5[0].b[0]) { // any k > 2?
        for (int j=3; j<Nk; ++j) {
          if (b5[j].b[0]) {
            if (first) printf("%d", j); else printf(", %s%d", squareTypes[i].name, j);
            if (!b5[j].b[4]) putWaysBent(b5[j].b); first=F;
          }
        }
      }
      break;
    case VzigzagA:
      b5=in?getTypeVzigA:excludeTypeVzigA; first=T;
      if (b5[0].b[0]) { // any
        for (int j=3; j<=R; ++j) {
          if (b5[j].b[0]) {
            if (first) printf("%d", j); else printf(", %s%d", squareTypes[i].name, j);
            if (!b5[j].b[4]) putWaysBent(b5[j].b); first=F;
          }
        }
      }
      break;
   case axialParity:
      b=in?getTypeParity:excludeTypeParity; if (b[1]) putWayParity(); break;
   case cyclicPLS:
      b=in?getTypeCyclic:excludeTypeCyclic; if (!b[0]) putWayCyclic(b); break;
   case semiCyclicPLS:
     b=in?getTypeSemiCyclic:excludeTypeSemiCyclic; if (!b[0]) putWaySemiCyclic(b); break;
    default: break;
  }
} // addTypeInfo

const int bufSize=1024, outSize=bufSize+50;
FILE *openOutput(char *buf) {
//    ----------
  FILE *wfp=NULL; int sub=0; const int baseSize=bufSize+25; char baseName[baseSize];
  if (R==C) snprintf(baseName, baseSize, "./Order%dCopy", R);
  else snprintf(baseName, baseSize, "./Order%dx%dCopy", R, C);
  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) {
    const int msgSize=outSize+50; char msg[msgSize];
    snprintf(msg, msgSize, "\a\nCan't open for write %s", buf); perror(msg);
  } else printf("\n.. writing %ss to file %s\n", R==C ? "square" : "rectangle",   buf);
  return wfp;
} // openOutput

typedef bool (*t_Write)(FILE *);

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

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

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

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

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

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

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

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

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

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

bool writeSquare11(FILE *wfp) {
//   -------------
  for (int r=0; r<R; r++) {
    if (fprintf(wfp, "%11d", xSquare[r][0])<0) return F;
    for (int c=1; c<C; c++) if (fprintf(wfp, " %11d", xSquare[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<R; r++) for (int c=0; c<C; c++) {
    const int v=xSquare[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==C) { *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 ((R==5)&(C==5)&(fieldWidth==2)&(smallestRead>=0)&(biggestRead<=RC)) return writeSquareOrder5(wfp);
  else return writeSquareFW[fieldWidth](wfp) ? fputc('\n', wfp)!=EOF : F;
} // writeSquare
//============================================= 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

char *getLine(char *s, char nl) {
//    -------
  int c, i=0; char *sin=s; do { c=getchar(); } while ((c==' ')|(c=='\t')|(c==nl) );
  *s=c; if (c=='\n') return s; // no exclude list
  while (i++<bufSize) if ( (*++s=getchar())=='\n') break;
  if (*s!='\n') { printf("\nType list too long.\n"); clearLine(*s); return NULL; }
  while ( (*--s==' ')||(*s=='\t') )
    ; /* strip any trailing whitespace */ *++s='\0'; s=sin; return sin;
} // getLine

void getZigZagType(bool *type, char token[bufSize],
//   -------------
                   const int lo, const int hi, struct t_bool5 *b5, const bool chk) {
  char *t=token; int j=lo, k=lo; *type=T;
  while (!isdigit(*t)) { if (*t=='\0') break; ++t; }
  if (isdigit(*t)) { // get k
    j=0; while ((*t>='0')&(*t<='9')) { j=j*10+*t-'0'; ++t; }; k=((j<lo)|(j>hi))?lo:j;
  }
        
  if (*t=='\0') b5[k].b[4]=T;
  else { // get ways
    if (strstr(t, "1")!=NULL) b5[k].b[1]=T; else if (strstr(t, "2")!=NULL) b5[k].b[2]=T;
    else if (strstr(t, "3")!=NULL) b5[k].b[3]=T; else if (strstr(t, "4")!=NULL) b5[k].b[4]=T;
    else if ((j>0)&&(j<5)&&(strstr(t, "way")!=NULL)) { k=lo; b5[k].b[j]=T; } else b5[k].b[4]=T;
  } 
  b5[k].b[0]=T; /* any k ? */ if (!chk|(k>2)) b5[0].b[0]=T; // any ?
} // getZigZagType

bool matchToken(bool list[numTypes], char token[bufSize], const bool in) {
//   ----------
  for (int i=firstType; i<=lastType; ++i) {
    if (strcmp(token, squareTypes[i].name)==0) {
      switch (i) {
      case bentDiagonal: if (in) getTypeBent[4]=T; else excludeTypeBent[4]=T; break;
      case Compact: if (in) getTypeCompact[2]=T; else excludeTypeCompact[2]=T; break;
      case Uzigzag: 
        if (in) { getTypeUzig[2].b[0]=T; getTypeUzig[2].b[4]=T; }
        else { excludeTypeUzig[2].b[0]=T; excludeTypeUzig[2].b[4]=T; } break;
      case Vzigzag:
        if (in) { getTypeVzig[2].b[0]=T; getTypeVzig[2].b[4]=T; }
        else { excludeTypeVzig[2].b[0]=T; excludeTypeVzig[2].b[4]=T; } break;
      case VzigzagA:
        if (in) { getTypeVzigA[0].b[0]=T; getTypeVzigA[3].b[0]=T; getTypeVzigA[3].b[4]=T; }
        else { excludeTypeVzigA[0].b[0]=T; excludeTypeVzigA[3].b[0]=T; excludeTypeVzigA[3].b[4]=T; } break;
      case axialParity: if (in) getTypeParity[2]=T; else excludeTypeParity[2]=T; break;
      case cyclicPLS: if (in) getTypeCyclic[0]=T; else excludeTypeCyclic[0]=T; break;
      case semiCyclicPLS: if (in) getTypeSemiCyclic[0]=T; else excludeTypeSemiCyclic[0]=T; break;
      default: break;
      }
      return list[i]=T;
    }
  }
  return F;
} // matchToken

char *getNextType(char *s, bool *unknownType, bool list[numTypes], const bool in) {
//    -----------
  *unknownType=F; if (*s=='\0') return NULL; char token[bufSize], *t=token;
  while ((*s!=',')&(*s!='\0')) *t++=*s++; *t='\0';
  if (matchToken(list, token, in)) { while ((*s==',')|(*s==' ')|(*s=='\t')) ++s; return s; }

  const int length=strlen(token);
  while ((*s==',')|(*s==' ')|(*s=='\t')) ++s;
  if (length>=2) {
    for (int i=0; i<length; ++i) token[i]=tolower(token[i]);
    switch (token[0]) {
    case 'a':
      if (strstr(token, "ass")!=NULL) {
        if (strstr(token, "dls")!=NULL) list[AssociativeDLS]=T; else list[Associative]=T; return s;
      } else if (strstr(token, "axi")!=NULL) {
        if (strstr(token, "par")!=NULL) {
          list[axialParity]=T;
          if (strstr(token, "1")!=NULL) if (in) getTypeParity[1]=T; else excludeTypeParity[1]=T;
          else if (in) getTypeParity[2]=T; else excludeTypeParity[2]=T; return s;
        } else { list[Sym]=T;
          if (strstr(token, "dls")!=NULL) list[DLS]=T; else if (strstr(token, "ls")!=NULL) list[LS]=T;
        } return s;
      } else if (strstr(token, "adj")!=NULL) {
        if (strstr(token, "cor")!=NULL) { list[adjacentCorner]=T; return s;
        } else if (strstr(token, "side")!=NULL) { list[adjacentSide]=T; return s; }
      }
      break;
    case 'b':
      if (strstr(token, "bent")!=NULL) {
        list[bentDiagonal]=T;
        if (strstr(token, "1")!=NULL) if (in) getTypeBent[1]=T; else excludeTypeBent[1]=T;
        else if (strstr(token, "2")!=NULL) if (in) getTypeBent[2]=T; else excludeTypeBent[2]=T;
        else if (strstr(token, "3")!=NULL) if (in) getTypeBent[3]=T; else excludeTypeBent[3]=T;
        else if (in) getTypeBent[4]=T; else excludeTypeBent[4]=T; return s;
      } else if (strstr(token, "bord")!=NULL) {
        if (strstr(token, "ring")!=NULL) list[Bordering]=T; else list[Bordered]=T; return s;
      } else if (strstr(token, "bi")!=NULL) { list[biMagic]=T; return s; }
      break;
    case 'c':
      if (strstr(token, "con")!=NULL) { list[Concentric]=T; return s;
      } else if (strstr(token, "compl")!=NULL) { list[Complete]=T; return s;
      } else if (strstr(token, "compa")!=NULL) {
        int k=2; list[Compact]=T; t=token;
        while (!isdigit(*t)) { if (*t=='\0') break; ++t; }
        if (isdigit(*t)) { //get k
          k=0; while ((*t>='0')&(*t<='9')) { k=k*10+*t-'0'; ++t; }; if ((k<2)|(k>=Nk)) k=2;
        }
        if (in) getTypeCompact[k]=T; else excludeTypeCompact[k]=T;
        if (k>2) { if (in) getTypeCompact[0]=T; else excludeTypeCompact[0]=T; } return s;
      } else if ((strstr(token, "cent")!=NULL)||(strstr(token, "csym")!=NULL)) {
        if (strstr(token, "dls")!=NULL) list[DLS]=T;
        else if (strstr(token, "ls")!=NULL) list[LS]=T; list[CSym]=T; return s;
      } else if (strstr(token, "cyc")!=NULL) { list[cyclicPLS]=T; list[DLS]=T; list[PLS]=T;
        if (strstr(token, "2")!=NULL) if (in) getTypeCyclic[2]=T; else excludeTypeCyclic[2]=T;
        else if (strstr(token, "3")!=NULL) if (in) getTypeCyclic[3]=T; else excludeTypeCyclic[3]=T;
        else if (strstr(token, "4")!=NULL) if (in) getTypeCyclic[4]=T; else excludeTypeCyclic[4]=T;
        else if (in) getTypeCyclic[0]=T; else excludeTypeCyclic[0]=T; return s;
      }
      break;
    case 'd':
      if (strstr(token, "dsols")!=NULL) { list[LS]=T; list[DSOLS]=T; return s;
      } else if (strstr(token, "dsodls")!=NULL) { list[DLS]=T; list[DSOLS]=T; return s;
      } else if ((strstr(token, "dou")!=NULL)&&(strstr(token, "self")!=NULL)) {
        list[DSOLS]=T; return s;
      } else if ((strstr(token, "dia")!=NULL)||(strstr(token, "dls")!=NULL)) {
        if ((strstr(token, "dsym")!=NULL)||(strstr(token, "dou")!=NULL)) list[DSym]=T;
        list[DLS]=T; return s;
      } else if ((strstr(token, "dsym")!=NULL)||(strstr(token, "dou")!=NULL)) {
        if (strstr(token, "ls")!=NULL) list[LS]=T; list[DSym]=T; return s;
      }
      break;
    case 'e':
      if (strstr(token, "even")!=NULL)
        if (strstr(token, "ass")!=NULL) { list[evenAssociative]=T; return s; } break;
    case 'f':
      if (strstr(token, "frank")!=NULL) { list[Franklin]=T; return s; } break;
    case 'k':
      if (strstr(token, "knut")!=NULL) { list[DLS]=T; list[PLS]=T; return s; } break;
    case 'l':
      if ((strstr(token, "lat")!=NULL)||(strstr(token, "ls")!=NULL)) { list[LS]=T; return s; } break;
    case 'm':
      if (strstr(token, "magic")!=NULL) { list[normalMagic]=T; return s;
      } else if (strstr(token, "most")!=NULL) {
        list[Compact]=T; if (in) getTypeCompact[2]=T; else excludeTypeCompact[2]=T;
        list[Complete]=T; return s;
      }
      break;
    case 'n':
      if (strstr(token, "not")!=NULL) { list[notMagic]=T; return s;
      } else if (strstr(token, "nfr")!=NULL) {
        if (strstr(token, "nfc")!=NULL) { list[LS]=T; list[nfr_nfc]=T; } else list[nfr]=T; return s;
      } else if (strstr(token, "nfc")!=NULL) { list[nfc]=T; return s;
      } else if (strstr(token, "norm")!=NULL) {
        if (strstr(token, "semi")!=NULL) list[normalSemiMagic]=T; else list[normalMagic]=T; return s;
      } else if (strstr(token, "near")!=NULL) {
        if (strstr(token, "ass")!=NULL) {
          if (strstr(token, "dls")!=NULL) list[nearAssociativeDLS]=T; else list[nearAssociative]=T; return s;
        } else if ((strstr(token, "cent")!=NULL)||(strstr(token, "csym")!=NULL)) {
          list[nearCSym]=T; return s;
        } else if (strstr(token, "pan")!=NULL) { list[nearPandiagonal]=T; return s;
        } else if (strstr(token, "sym")!=NULL) { list[nearSymlateral]=T; return s; }
      } else if ((strstr(token, "nat")!=NULL)&&(strstr(token, "dia")!=NULL)) {
         list[nbd]=T; return s;
      } else if ((strstr(token, "nbd")!=NULL)||(strstr(token, "nfd")!=NULL)) {
        list[nbd]=T; return s; // \diagonal sometimes called back, sometimes forward
      }
      break;
    case 'o':
      if (strstr(token, "oth")!=NULL) {
        if (strstr(token, "semi")!=NULL) list[otherSemiMagic]=T; else list[otherMagic]=T; return s;
      } else if (strstr(token, "ort")!=NULL) { list[OLS]=T; return s;
      } else if (strstr(token, "ols")!=NULL) { list[LS]=T; list[OLS]=T; return s;
      } else if (strstr(token, "odls")!=NULL) { list[DLS]=T; list[OLS]=T; return s;
      } else if (strstr(token, "odd")!=NULL)
        if (strstr(token, "ass")!=NULL) { list[oddAssociative]=T; return s; }
      break;
    case 'p':
      if (strstr(token, "pan")!=NULL) {
        if (strstr(token, "1")!=NULL) list[Pandiagonal1Way]=T;
        else if (strstr(token, "dls")!=NULL) { list[DLS]=T; list[PLS]=T; } else list[Pandiagonal]=T; return s;
      } else if (strstr(token, "pls")!=NULL) { list[DLS]=T; list[PLS]=T; return s;
      } else if (strstr(token, "prime")!=NULL) {
        if (strstr(token, "1")!=NULL) list[Prime1]=T; else list[Prime]=T; return s; }
      break;
    case 'r':
      if (strstr(token, "rev")!=NULL) { list[Reversible]=T; return s; } break;
    case 's':
      if (strstr(token, "semi")!=NULL) {
        if (strstr(token, "cyc")!=NULL) { list[semiCyclicPLS]=T; list[DLS]=T; list[PLS]=T;
          if (strstr(token, "1")!=NULL) if (in) getTypeSemiCyclic[1]=T; else excludeTypeSemiCyclic[1]=T;
          else if (strstr(token, "2")!=NULL) if (in) getTypeSemiCyclic[2]=T; else excludeTypeSemiCyclic[2]=T;
          else if (strstr(token, "3")!=NULL) if (in) getTypeSemiCyclic[3]=T; else excludeTypeSemiCyclic[3]=T;
          else if (in) getTypeSemiCyclic[0]=T; else excludeTypeSemiCyclic[0]=T; return s;
        } else { list[normalSemiMagic]=T; return s; }
      } else if (strstr(token, "self")!=NULL) {
        if (strstr(token, "comp")!=NULL) { list[selfComplement]=T; return s;
        } else if (strstr(token, "ort")!=NULL) { list[SOLS]=T; return s;
        } else if (strstr(token, "tran")!=NULL) { list[LS]=T; list[selfTranspose]=T; return s; }
      } else if (strstr(token, "syml")!=NULL) { list[Symlateral]=T; return s;
      } else if (strstr(token, "sym")!=NULL) {
        if (strstr(token, "par")!=NULL) { list[symmetricParity]=T; return s;
        } else {
          list[Sym]=T;
          if ((strstr(token, "dls")!=NULL)||(strstr(token, "dia")!=NULL)) list[DLS]=T;
          else if ((strstr(token, "ls")!=NULL)||(strstr(token, "lat")!=NULL)) list[LS]=T;
          return s; 
        }
      } else if (strstr(token, "sols")!=NULL) { list[LS]=T; list[SOLS]=T; return s;
      } else if (strstr(token, "sodls")!=NULL) { list[DLS]=T; list[SOLS]=T; return s; }
      break;
    case 't':
      if (strstr(token, "tri")!=NULL) { list[triMagic]=T; return s;
      } else if (strstr(token, "tran")!=NULL) {
        if (strstr(token, "par")!=NULL) { list[transposeParity]=T; return s; }
      }
      break;
    case 'u':
      if (strstr(token, "ult")!=NULL) {
        if (strstr(token, "dls")!=NULL) {
          const int mod6=R%6; list[DLS]=T; list[AssociativeDLS]=T;
          if ((mod6==1)|(mod6==5)) list[PLS]=T; else list[WPLS]=T;
        } else { list[Associative]=T; list[Pandiagonal]=T; }
        return s;
      } else if (strstr(token, "zig")!=NULL) {
        getZigZagType(&list[Uzigzag], token, 2, Nk, in?getTypeUzig:excludeTypeUzig, T);
        return s;
      }
      break;
    case 'v':     
      if (strstr(token, "zig")!=NULL) {
        if ((strstr(token, "ga")!=NULL)||(strstr(token, " a")!=NULL)) {
          getZigZagType(&list[VzigzagA], token, 3, R, in?getTypeVzigA:excludeTypeVzigA, F); return s;
        } else {
          getZigZagType(&list[Vzigzag], token, 2, Nk, in?getTypeVzig:excludeTypeVzig, T); return s;
        }
      }
      break;
    case 'w':
      if ((strstr(token, "weak")!=NULL)||(strstr(token, "wpls")!=NULL)) { list[WPLS]=T; return s; } break;
    default: break;
    }
  }
  printf("\a\nUnknown type: \"%s\".\n", token); *unknownType=T; return NULL;
} // getNextType

// notMagic, normalSemiMagic, otherSemiMagic, normalMagic, otherMagic,
// biMagic, triMagic, Prime, Prime1,
// adjacentCorner, adjacentSide, Associative, oddAssociative, evenAssociative, nearAssociative,
// Concentric, Bordered, Bordering, Symlateral, nearSymlateral,
// Pandiagonal, nearPandiagonal, Complete, Compact, Pandiagonal1Way,
// bentDiagonal, Franklin, Uzigzag, Vzigzag, VzigzagA, selfComplement,
// LS, DLS, AssociativeDLS, nearAssociativeDLS, PLS, cyclicPLS, semiCyclicPLS, WPLS, Sym, DSym, CSym, nearCSym,
// nfr, nfc, nfr_nfc, selfTranspose, nbd, OLS, SOLS, DSOLS,
// Reversible, axialParity, symmetricParity, transposeParity,

//{n,n,o,n,o,b,t,p,p,a,a,a,o,e,n,c,b,b,s,n,p,n,c,c,p,b,f,u,v,v,s,l,d,a,n,p,c,s,w,s,d,c,n,n,n,n,s,n,o,s,d,r,a,s,t}
bool noType1[]=
  {T,T,T,F,F,F,F,T,F,T,T,F,T,T,T,T,T,T,T,T,F,T,T,T,T,T,T,T,T,T,F,F,F,F,T,F,F,F,F,F,F,F,T,F,F,F,F,F,F,F,F,F,F,F,F},
     noType2[]=
  {F,T,F,T,F,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,T,F,T,T,T,T,T,T,T,T,F,F,T,F,F,F,F,T,T,T,T,F,F,F,F},
     noType3[]=
  {F,F,F,F,F,T,T,F,F,T,T,F,T,T,T,F,F,T,T,T,T,T,T,T,T,T,T,T,T,T,F,F,T,T,T,T,T,T,T,T,T,F,T,F,F,F,F,F,F,T,T,F,F,F,F},
     noType4[]=
  {F,F,F,F,F,T,T,F,F,F,F,F,T,T,T,T,T,T,T,T,F,T,F,F,T,T,T,T,T,T,F,F,F,F,T,T,T,T,F,F,F,F,T,F,F,F,F,F,F,F,F,F,F,F,F},
     noType5[]=
  {F,F,F,F,F,T,T,F,F,T,F,F,F,F,T,F,F,T,F,T,F,T,T,T,F,T,T,T,T,T,F,F,F,F,T,F,F,F,T,T,T,F,T,F,F,F,F,F,F,F,F,F,F,F,F},
     *noType[]={ NULL, noType1, noType2, noType3, noType4, noType5 };

bool rejectTypes(bool list[numTypes], const int n, const bool in) {
//   -----------
   if (list[normalMagic]&(n<=5)) {
    for (int t=firstType; t<=lastMagicType; ++t) {
      if (noType[n][t]) {
        if (t==bentDiagonal) { bool *b=in?getTypeBent:excludeTypeBent;
          if ((n==4)&b[2]) continue; if ((n==5)&b[1]) continue;
        }
        if (t==Vzigzag) { struct t_bool5 *b5=in?getTypeVzig:excludeTypeVzig;
          if (((n==4)|(n==5))&b5[2].b[2]) continue;
        }
        if (t==VzigzagA) { struct t_bool5 *b5=in?getTypeVzigA:excludeTypeVzigA;
          if ((n==4)&b5[3].b[2]) continue;
        }
        if (orderRejectMagic(t, list, n)) return T;
      }
    }
  }
  if (list[LS]&(n<=5)) {
    for (int t=DLS; t<=lastType; ++t) { if (noType[n][t]&&orderRejectLS(t, list, n)) return T; }
  }
  if (list[DLS]&(n<=5)) {
    for (int t=DLS; t<=lastType; ++t) { if (noType[n][t]&&orderRejectDLS(t, list, n)) return T; }
  }
  if ((n<12)&(n!=1)) {
    if (orderReject(triMagic, list, n)) return T; if (n<8) { if (orderReject(biMagic, list, n)) return T; }
  }
  if (n&1) { // odd
    if (orderReject(adjacentCorner, list, n)) return T; if (orderReject(Bordering, list, n)) return T;
    if (orderReject(Complete, list, n)) return T; if (orderReject(Sym, list, n)) return T;
    if (orderReject(DSym, list, n)) return T; if (orderReject(Uzigzag, list, n)) return T;
  } else { // even
    if (orderReject(oddAssociative, list, n)||orderReject(evenAssociative, list, n)) return T;
    if ((n&2)==0) { // doubly even   
      if (((n&4)!=0)&list[normalMagic]&list[Franklin]) {
        printf("\a\nThere are no order %d %s %s squares.\n", n,
               squareTypes[normalMagic].name, squareTypes[Franklin].name); return T;
      }
      if ((n==12)&&orderReject(Franklin, list, n)) return T;
    } else { // singly even
      if (orderReject(AssociativeDLS, list, n)) return T; if (orderReject(adjacentCorner, list, n)) return T;
      if (list[normalMagic]&&orderRejectMagic(Compact, list, n)) return T;
      if (orderReject(Pandiagonal1Way, list, n)) return T;
      if ((list[normalMagic])&&(orderReject(Pandiagonal, list, n))) return T;
      // below for semiMagic ?
      if (orderReject(Uzigzag, list, n)) return T; if (orderReject(Vzigzag, list, n)) return T;
    }
  }
  return F;
} // rejectTypes

bool rejectTypesRect(bool list[numTypes]) {
//   ---------------
   if (!(R&1)&&(orderRejectRect(oddAssociative, list)||orderRejectRect(evenAssociative, list))) return T;
   for (int i=biMagic; i<=lastType; ++i) if (list[i]) {
     switch (i) {
     case Associative: case nearAssociative: case evenAssociative: case oddAssociative: case Pandiagonal:
     case Compact: case Pandiagonal1Way: case bentDiagonal: case Franklin: case Concentric: case Bordered:
     case selfComplement: case axialParity: case symmetricParity: break;
     default:
       printf("Type %s is only supported for squares.\n", squareTypes[i].name); return T;
    }
  }
  return F;
} // rejectTypesRect

void fixupWays(bool *b) {
//   ---------
  if (b[4]) { b[3]=F; b[2]=F; b[1]=F; } else if (b[3]) { b[2]=F; b[1]=F; } else if (b[2]) b[1]=F;
} // fixupWays

void fixupWay(bool *b) { if (b[2]) b[1]=F; }
//   --------

bool fixupTypes(bool unknownType, bool list[numTypes], const bool in) {
//   ----------
  if (unknownType) return F; int basicTypes=0;
  for (int i=notMagic; i<=otherMagic; ++i) if (list[i]) ++basicTypes;
  if (list[LS]) ++basicTypes; if (list[DLS]) ++basicTypes;

  if (basicTypes>1) {
    if ((basicTypes==2)&list[Franklin]&list[normalMagic]&list[normalSemiMagic]) {
      list[normalMagic]=F; list[normalSemiMagic]=F; basicTypes=0;
    } else {
      int t1=-1, t2=-1; for (int i=notMagic; i<=otherMagic; ++i) if (list[i]) { t1=i; break; }
      if (t1<0) { if (list[LS]) t1=LS; else t1=DLS; }
      for (int i=notMagic; i<=otherMagic; ++i) if ((i!=t1)&(list[i])) { t2=i; break; }
      if (t2<0) { if ((LS!=t1)&list[LS]) t2=LS; else t2=DLS; } return printConflicts(t1, t2);
    }
  }

  if (basicTypes==0) {
    bool done=F;
    for (int i=otherMagic+1; i<=lastType; ++i) {
      switch (i) {
      case Pandiagonal1Way:
      case Complete: case Compact: 
      case Concentric: case Bordered: case biMagic: case triMagic:
        if (list[i]) {
          if (list[Prime]|list[Prime1]) list[otherMagic]=T; /* else list[normalMagic]=T; */ done=T; } break;
      case oddAssociative: case evenAssociative: case nearAssociative: case nearPandiagonal:
      case Bordering: case Symlateral: case nearSymlateral:
        if (list[i]) { list[normalMagic]=T; done=T; } break;
      case AssociativeDLS: case nearAssociativeDLS:
        if (list[i]) { list[DLS]=T; done=T; } break;
      case nfr_nfc: case selfTranspose:
        if (list[i]) { list[LS]=T; done=T; } break;
      case Reversible:
        if (list[i]) { list[notMagic]=T; done=T; } break;
      case cyclicPLS: case semiCyclicPLS:
        if (list[i]) { list[DLS]=T; done=T; } break;
      default: break;
      } // switch (i)
      if (done) break;
    } // for (int i=otherMagic+1
  } else { // basicTypes==1
    bool done=F; int t1=-1, t2=-1;
    if ((R!=C)&list[normalSemiMagic]) { list[normalSemiMagic]=F; list[normalMagic]=T; }
    for (int i=otherMagic+1; i<=lastType; ++i) {
     switch (i) {
     case Associative: case Pandiagonal:
        if (list[i]&!(list[normalMagic]|list[DLS]|list[otherMagic]|list[notMagic])) { t2=i; done=T; } break;
     case Pandiagonal1Way: case Concentric: case Bordered: case Complete: 
     case biMagic: case triMagic:
       if (list[i]&!(list[normalMagic]|list[otherMagic])) { t2=i; done=T; } break;
     case nearAssociative: case nearPandiagonal:
        if (list[i]&!(list[normalMagic]|list[DLS])) { t2=i; done=T; } break;
      case Bordering: case Symlateral: case nearSymlateral:
        if (list[i]&!list[normalMagic]) { t2=i; done=T; } break;
      case Compact:
        if (list[i]&!(list[normalMagic]|list[normalSemiMagic]|list[otherMagic])) { t2=i; done=T; } break;
      case adjacentCorner: case adjacentSide: case bentDiagonal:
      case oddAssociative: case evenAssociative: case Franklin:
      case selfComplement: case Uzigzag: case Vzigzag: case VzigzagA:
        if (list[i]&!(list[normalMagic]|list[normalSemiMagic])) { t2=i; done=T; } break;
      case AssociativeDLS: case nearAssociativeDLS:
        if (list[i]&!list[DLS]) { t2=i; done=T; } break;
      case Sym: case DSym: case CSym: case nearCSym: case OLS: case SOLS: case DSOLS:
        if (list[i]&!(list[DLS]|list[LS])) { t2=i; done=T; } break;
      case nfr_nfc: case selfTranspose:
        if (list[i]&!list[LS]) { t2=i; done=T; } break;
      case Reversible:
        if (list[i]&!list[notMagic]) { t2=i; done=T; } break;
      case axialParity: case symmetricParity: case transposeParity:
        if (list[i]) { t2=0; done=T; } break;
      case cyclicPLS: case semiCyclicPLS:
        if (list[i]&!list[DLS]) { t2=i; done=T; } break;
      default:
        if (list[i])
          if (!(list[normalMagic]|list[normalSemiMagic]|list[otherMagic]|list[LS]|list[DLS])) {
            t2=i; done=T;
          } break;
      } // switch (i)
      if (done) break;
    } // for (int i=otherMagic+1
    if (t2>0) {
      for (int i=notMagic; i<=otherMagic; ++i) if (list[i]) { t1=i; break; }
      if (t1<0) { if (list[LS]) t1=LS; else t1=DLS; } return printConflicts(t1, t2);
    }
  } // if (basicTypes==0)

  if (list[Associative]) {
    if (list[nearAssociative]) return printConflicts(Associative, nearAssociative);
    if (list[evenAssociative]) return printConflicts(Associative, evenAssociative);
    if (list[oddAssociative]) return printConflicts(Associative, oddAssociative);
    if (list[DLS]) { list[Associative]=F; list[AssociativeDLS]=T; }
  }
  if (list[nearAssociative]) {
    if (list[DLS]) { list[nearAssociative]=F; list[nearAssociativeDLS]=T; }
    else if (!(((R&3)==2) & ((C&3)==2))) { list[nearAssociative]=F; list[Associative]=T; }
  }
  if (list[evenAssociative]) {
    if (list[oddAssociative]) return printConflicts(evenAssociative, oddAssociative);
  }
  if (list[AssociativeDLS]&(!(list[Pandiagonal]|list[PLS]|list[WPLS]))) { // don't change ultramagic
    if ((R==C)&((R&3)==2)) { list[AssociativeDLS]=F; list[nearAssociativeDLS]=T; }
  }
  if (list[nearAssociativeDLS]) { if ((R&3)!=2) { list[nearAssociativeDLS]=F; list[AssociativeDLS]=T; }}
  // For singly even LS, there are both CSym and nearCSym.
  //if (list[CSym]) { if ((R&3)==2) { list[CSym]=F; list[nearCSym]=T; }}
  //if (list[nearCSym]) { if ((R&3)!=2) { list[nearCSym]=F; list[CSym]=T; }}
  if (list[Pandiagonal]) { if (list[DLS]) { list[Pandiagonal]=F; list[PLS]=T; }}
  if (list[nearPandiagonal]) { if ((R&3)!=2) { list[nearPandiagonal]=F; list[Pandiagonal]=T; }}
  if (list[PLS]) { const int mod6=R%6; if (!((mod6==1)|(mod6==5))) { list[PLS]=F; list[WPLS]=T; }}
  if (list[Symlateral]) { if(!(R&1)) { list[Symlateral]=F; list[nearSymlateral]=T; }}
  if (list[nearSymlateral]) { if (R&1) { list[nearSymlateral]=F; list[Symlateral]=T; }}
  if ((list[Bordered]|list[Bordering])&list[normalMagic]) list[Concentric]=T;
  if (list[Complete]) list[Pandiagonal]=T;
  if (list[Franklin]) { bool *b=in?getTypeBent:excludeTypeBent;
    list[bentDiagonal]=T; b[4]=T; list[Compact]=T; b=in?getTypeCompact:excludeTypeCompact; b[2]=T;
  }
  if (list[triMagic]) { list[biMagic]=T; }
  if (list[Prime]|list[Prime1]) { list[normalMagic]=F; list[otherMagic]=T; }
  if (list[otherMagic]&list[Bordered]) { list[Bordered]=F; list[Concentric]=T; }
  if (list[bentDiagonal]) fixupWays(in?getTypeBent:excludeTypeBent);
  if (list[Compact]) { bool *b=in?getTypeCompact:excludeTypeCompact;
    if (b[0]) { // any > 2 ?
      for (int k=Nk-1; k>3; --k) if (b[k]) { const int kd2=k/2;
        for (int f=2; f<=kd2; ++f) if (((k%f)==0)&b[f]) b[k]=F;
      }
    }
  }
  if (list[Uzigzag]) { struct t_bool5 *b5=in?getTypeUzig:excludeTypeUzig;
    if (b5[2].b[0]) fixupWays(b5[2].b);
    if (b5[0].b[0]) { /* any > 2 ? */ for (int i=3; i<Nk; ++i) if (b5[i].b[0]) fixupWays(b5[i].b); }
  }
  if (list[Vzigzag]) { struct t_bool5 *b5=in?getTypeVzig:excludeTypeVzig;
    if (b5[2].b[0]) fixupWays(b5[2].b);
    if (b5[0].b[0]) { /* any > 2 ? */ for (int i=3; i<Nk; ++i) if (b5[i].b[0]) fixupWays(b5[i].b); }
  }
  if (list[VzigzagA]) { struct t_bool5 *b5=in?getTypeVzigA:excludeTypeVzigA;
    if (b5[0].b[0]) { /* any ? */ for (int i=3; i<=R; ++i) if (b5[i].b[0]) fixupWays(b5[i].b); }
  }
  if (list[axialParity]) fixupWay(in?getTypeParity:excludeTypeParity);
  if (list[cyclicPLS]) fixupWay(in?getTypeCyclic:excludeTypeCyclic);
  if (list[semiCyclicPLS]) fixupWays(in?getTypeSemiCyclic:excludeTypeSemiCyclic);
  if (R==C) { if (rejectTypes(list, R, in)) return F; } else if (rejectTypesRect(list)) return F;
  return T;
 } // fixupTypes

bool getIncludeTypes() {
//   ---------------
  char buf[bufSize], *s=getLine(buf, '\n');
  if (s!=NULL) {
    bool unknownType=F;
    while ((s=getNextType(s, &unknownType, getType, T))!=NULL) if (unknownType) break;
    return fixupTypes(unknownType, getType, T);
  }
  return F;
} // getIncludeTypes

bool noExcludeTypes;
bool getExcludeTypes() {
//   ---------------
  char buf[bufSize], *s=getLine(buf, ' ');
  if (s!=NULL) {
    noExcludeTypes=(*s=='\n'); if (noExcludeTypes) return T;
    bool unknownType=F;
    while ((s=getNextType(s, &unknownType, excludeType, F))!=NULL) if (unknownType) break;
    return fixupTypes(unknownType, excludeType, F);
  }
  return F;
} // getExcludeTypes

bool getTypes() {
//   --------
  initListTypes();
  printf("\nEnter include type list: ");
  if (getIncludeTypes()) {
    printf("\nEnter exclude type list: ");
    if (getExcludeTypes()) {
      if (!noExcludeTypes) {
        bool empty=T;
        for (int i=firstType; i<=lastType; ++i) {
          if (excludeType[i]) {
            if (getType[i]) {
              switch (i) {
              case Compact:
                if (excludeTypeCompact[2]&!getTypeCompact[2]) empty=F;
                else if (excludeTypeCompact[0]) { // any > 2 ?
                  if (!getTypeCompact[0]) /* none > 2 ? */ empty=F;
                  else 
                    for (int k=3; k<Nk; ++k) if (excludeTypeCompact[k]&!getTypeCompact[k]) { empty=F; break; }
                } break;
              case Uzigzag:
                if (excludeTypeUzig[2].b[0]&!getTypeUzig[2].b[0]) empty=F;
                else if (excludeTypeUzig[0].b[0]) { // any > 2 ?
                  if (!getTypeUzig[0].b[0]) /* none > 2 ? */ empty=F;
                  else 
                    for (int k=3; k<Nk; ++k) if (excludeTypeUzig[k].b[0]&!getTypeUzig[k].b[0]) { empty=F; break; }
                } break;
              case Vzigzag:
                if (excludeTypeVzig[2].b[0]&!getTypeVzig[2].b[0]) empty=F;
                else if (excludeTypeVzig[0].b[0]) { // any > 2 ?
                  if (!getTypeVzig[0].b[0]) /* none > 2 ? */ empty=F;
                  else 
                    for (int k=3; k<Nk; ++k) if (excludeTypeVzig[k].b[0]&!getTypeVzig[k].b[0]) { empty=F; break; }
                } break;
              case VzigzagA:
                if (!getTypeVzigA[0].b[0]) /* none ? */ empty=F;
                else 
                  for (int k=3; k<=R; ++k) if (excludeTypeVzigA[k].b[0]&!getTypeVzigA[k].b[0]) { empty=F; break; }
                break;
              default: break;
              }
              if (!empty) break;
            } else if (!((i==selfComplement)&getType[Associative])) { empty=F; break; } // (getType[i])
          } // excludeType[i]
        } // for (int i=firstType
        if (empty) { printf("\aExclude list empties include list.\n"); return F; }
      } // if (!noExcludeTypes)
      return T;
    }
  }
  return F;
} // getTypes

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], *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; }
    else { 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) {
      char msg[bufSize+50]; snprintf(msg, bufSize+50, "\a\nCan't open for read %s", buf); perror(msg);
    }
  }
  return rfp;
} // openInput

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

bool readSquare(FILE *rfp) {
//   ----------
  int smallest=INT_MAX, biggest=INT_MIN;
  for (int r=0; r<R; r++) {
    for (int c=0; c<C; 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) ) printf("\aError reading from file.\n"); return F;
      }
    }
  }
  zeroBase=(smallest==0);
  if (zeroBase) {
    magicSum=magicConstant-R; rowSum=rowConstant-C; colSum=colConstant-R;
    halfMagicSum=halfMagicConstant-R/2;
  } else {
   magicSum=magicConstant; rowSum=rowConstant; colSum=colConstant;
   halfMagicSum=halfMagicConstant;
  }
  Sum2=smallest+biggest; Sum4=Sum2+Sum2;
  const int sWidth=getWidth(smallest), bWidth=getWidth(biggest);
  fieldWidth=sWidth>bWidth?sWidth:bWidth; zeroBase=(smallest==0);
  if ( (smallest<0)|(biggest>RC) ) squareType=isMagic(xSquare, R, C);
  else if ((R==C)&(R!=1)&(smallest<=1)&(biggest==(smallest+M))) squareType=isLatin(xSquare, R);
  else squareType=isNormal(xSquare, R, C);
  smallestRead=smallest; biggestRead=biggest; ++squareNum; return T;
} // readSquare
//=========================================== copy squares ==========================================

bool isAdjacentCorner(int **x, const int n, const int nc) { // R==C
//   ----------------
  if ((n&3)!=0) return F; // only doubly even
  const int m=n-1, l=n-2, S2=Sum2; int v;
  v=x[0][0]; if (v+x[1][1]!=S2) return F; // corners
  v=x[0][m]; if (v+x[1][m-1]!=S2) return F;
  v=x[m][0]; if (v+x[m-1][1]!=S2) return F;
  v=x[m][m]; if (v+x[m-1][m-1]!=S2) return F;

  for (int i=1; i<m; ++i) { // rest of top, bottom, left, right
    v=x[0][i]; if ((v+x[1][i-1]!=S2)&(v+x[1][i+1]!=S2)) return F;
    v=x[m][i]; if ((v+x[m-1][i-1]!=S2)&(v+x[m-1][i+1]!=S2)) return F;
    v=x[i][0]; if ((v+x[i-1][1]!=S2)&(v+x[i+1][1]!=S2)) return F;
    v=x[i][m]; if ((v+x[i-1][m-1]!=S2)&(v+x[i+1][m-1]!=S2)) return F;
  }

  for (int r=1; r<m; ++r) for (int c=1; c<m; ++c) { 
    v=x[r][c];
    if ((v+x[r-1][c-1]!=S2)&(v+x[r-1][c+1]!=S2)&(v+x[r+1][c-1]!=S2)&(v+x[r+1][c+1]!=S2)) return F;
  }
  return squareTypes[adjacentCorner].isType=T;
} // isAdjacentCorner

bool isAdjacentSideEven(int **x, const int n) {
//   ------------------
  const int m=n-1, l=n-2, S2=Sum2; int v;

  // corners
  v=x[0][0]; if ((v+x[0][1]!=S2)&(v+x[1][0]!=S2)) return F;
  v=x[0][m]; if ((v+x[0][l]!=S2)&(v+x[1][m]!=S2)) return F;
  v=x[m][0]; if ((v+x[m][1]!=S2)&(v+x[l][0]!=S2)) return F;
  v=x[m][m]; if ((v+x[m][l]!=S2)&(v+x[l][m]!=S2)) return F;

  for (int i=1; i<m; ++i) { // rest of top, bottom, left, right
    v=x[0][i]; if ((v+x[0][i-1]!=S2)&(v+x[0][i+1]!=S2)&(v+x[1][i]!=S2)) return F;
    v=x[m][i]; if ((v+x[m][i-1]!=S2)&(v+x[m][i+1]!=S2)&(v+x[l][i]!=S2)) return F;
    v=x[i][0]; if ((v+x[i-1][0]!=S2)&(v+x[i+1][0]!=S2)&(v+x[i][1]!=S2)) return F;
    v=x[i][m]; if ((v+x[i-1][m]!=S2)&(v+x[i+1][m]!=S2)&(v+x[i][l]!=S2)) return F;
  }

  for (int r=1; r<m; ++r) for (int c=1; c<m; ++c) { 
    v=x[r][c];
    if ((v+x[r-1][c]!=S2)&(v+x[r][c+1]!=S2)&(v+x[r+1][c]!=S2)&(v+x[r][c-1]!=S2)) return F;
  }
  return squareTypes[adjacentSide].isType=T;
} // isAdjacentSideEven

bool isAdjacentSideOdd(int **x, const int n) {
//   -----------------
  if (n==1) return F; const int m=n-1, l=n-2, S2=Sum2, S1=S2/2; int v;

  // corners
  v=x[0][0]; if ((v+x[0][1]!=S2)&(v+x[1][0]!=S2)) if (v!=S1) return F;
  v=x[0][m]; if ((v+x[0][l]!=S2)&(v+x[1][m]!=S2)) if (v!=S1) return F;
  v=x[m][0]; if ((v+x[m][1]!=S2)&(v+x[l][0]!=S2)) if (v!=S1) return F;
  v=x[m][m]; if ((v+x[m][l]!=S2)&(v+x[l][m]!=S2)) if (v!=S1) return F;

  for (int i=1; i<m; ++i) { // rest of top, bottom, left, right
    v=x[0][i];
    if ((v+x[0][i-1]!=S2)&(v+x[0][i+1]!=S2)&(v+x[1][i]!=S2)) if (v!=S1) return F;
    v=x[m][i];
    if ((v+x[m][i-1]!=S2)&(v+x[m][i+1]!=S2)&(v+x[l][i]!=S2)) if (v!=S1) return F;
    v=x[i][0];
    if ((v+x[i-1][0]!=S2)&(v+x[i+1][0]!=S2)&(v+x[i][1]!=S2)) if (v!=S1) return F;
    v=x[i][m];
    if ((v+x[i-1][m]!=S2)&(v+x[i+1][m]!=S2)&(v+x[i][l]!=S2)) if (v!=S1) return F;
  }

  for (int r=1; r<m; ++r) for (int c=1; c<m; ++c) { 
    v=x[r][c];
    if ((v+x[r-1][c]!=S2)&(v+x[r][c+1]!=S2)&(v+x[r+1][c]!=S2)&(v+x[r][c-1]!=S2)) if (v!=S1) return F;
  }
  return squareTypes[adjacentSide].isType=T;
} // isAdjacentSideOdd

bool isNearAssociativeRect(int **x, const int nr, const int nc, const int S2) {
//   ---------------------
  const int nrd2=nr/2, nrm1=nr-1, ncm1=nc-1, maxCount=2; int count=0;
  for (int r=0; r<nrd2; ++r) for (int c=0; c<nc; ++c) {
    if (x[r][c]+x[nrm1-r][ncm1-c]!=S2) if (++count>maxCount) return F;
  }
  return squareTypes[nearAssociative].isType=T;
} // isNearAssociativeRect

bool isNearAssociative(int **x, const int nr, const int nc) { return isNearAssociativeRect(x, nr, nc, Sum2); }
//   -----------------

bool isAssociativeRect(int **x, const int nr, const int nc, int S2) {
//   -----------------
  bool odd=((nr&1)==1); const int nrd2=nr/2, nrm1=nr-1, ncd2=nc/2, ncm1=nc-1;
  if ((squareType==notMagic)|(squareType==otherMagic)) S2=x[0][0]+x[nrm1][ncm1];
  for (int r=0; r<nrd2; ++r) for (int c=0; c<nc; ++c) { if (x[r][c]+x[nrm1-r][ncm1-c]!=S2) return F; }
  if (odd) for (int c=0; c<ncd2; ++c) if (x[nrd2][c]+x[nrd2][ncm1-c]!=S2) return F;
  return squareTypes[Associative].isType=T;
} // isAssociativeRect

bool isAssociative(int **x, const int nr, const int nc) { return isAssociativeRect(x, nr, nc, Sum2); }
//   -------------

bool isOddAssociativeRect (int **x, const int nr, const int nc, const int S2) {
//   --------------------
  if (isAssociativeRect(x, nr, nc, S2)) return F; if ((S2&1)==1) return F;
  const int nrm1=nr-1, ncm1=nc-1;
  for (int r=0; r<nr; ++r) for (int c=0; c<nc; ++c) {
    if ((x[r][c]&1)==1) if (x[r][c]+x[nrm1-r][ncm1-c]!=S2) return F;
  }
  return squareTypes[oddAssociative].isType=T;
} // isOddAssociativeRect

bool isOddAssociative(int **x, const int nr, const int nc) { return isOddAssociativeRect(x, nr, nc, Sum2); }
//   ----------------

bool isEvenAssociativeRect(int **x, const int nr, const int nc, const int S2) {
//   ---------------------
  if (isAssociativeRect(x, nr, nc, S2)) return F; if ((S2&1)==1) return F;
  const int nrm1=nr-1, ncm1=nc-1;
  for (int r=0; r<nr; ++r) for (int c=0; c<nc; ++c) {
    if ((x[r][c]&1)==0) if (x[r][c]+x[nrm1-r][ncm1-c]!=S2) return F;
  }
  return squareTypes[evenAssociative].isType=T;
} // isEvenAssociativeRect

bool isEvenAssociative(int **x, const int nr, const int nc) { return isEvenAssociativeRect(x, nr, nc, Sum2); }
//   -----------------

bool isContiguous(int **x, const int mr, const int mc, int *first) {
//   ------------
  const int low=*first, high=low+mr+mc-3;
  int lo=INT_MAX, hi=INT_MIN, o=(R-mr)/2, lr=o+mr-1, lc=o+mc-1, tmp=0;
  // Corners
  tmp=min(x[o][o], x[lr][lc]); if (tmp<lo) lo=tmp; if (tmp>hi) hi=tmp;
  tmp=min(x[o][lc], x[lr][o]); if (tmp<lo) lo=tmp; if (tmp>hi) hi=tmp;
  // Top, bottom
  for (int c=o+1; c<lc; ++c) { tmp=min(x[o][c], x[lr][c]); if (tmp<lo) lo=tmp; if (tmp>hi) hi=tmp; }
  // Left, right
  for (int r=o+1; r<lr; ++r) { tmp=min(x[r][o], x[r][lc]); if (tmp<lo) lo=tmp; if (tmp>hi) hi=tmp; }
  if ((lo!=low)|(hi!=high) ) return F; *first=hi+1; return T;
} // isContiguous

bool getBordered(int **x, const int nr, const int nc) {
//   -----------
  int first=zeroBase?0:1;
  const int mid=(nr&1)?5:4, diff=(nr<nc)?nr-mid:nc-mid, end=(nr==nc)?(nr&1)?3:6:nr-diff;
  for (int mr=nr, mc=nc; mr>=end; mr-=2, mc-=2) if (!isContiguous(x, mr, mc, &first)) return F; return T;
} // getBordered

bool getBordering(int **x, const int n, const int nc) { // R==C
//   ------------
  if (!(n&1)) { // only even
    int first=zeroBase?8:9;
    for (int m=6; m<=n; m+=2) if (!isContiguous(x, m, m, &first)) return F; return T;
  }
  return F;
} // getBordering

bool isBorder(int **x, const int mr, const int mc, const int S2) {
//   --------
  const int o=(R-mr)/2, lr=o+mr-1, lc=o+mc-1;
  // Corners
  if ((x[o][o]+x[lr][lc])!=S2) return F; if ((x[o][lc]+x[lr][o])!=S2) return F;
  // Top, bottom
  for (int c=o+1; c<lc; ++c) if ((x[o][c]+x[lr][c])!=S2) return F;
  // Left, right
  for (int r=o+1; r<lr; ++r) if ((x[r][o]+x[r][lc])!=S2) return F; return T;
} // isBorder

bool isConcentric(int **x, const int nr, const int nc) {
//   ------------
  if (nr==nc) {
    if (nr==3) return T; if (nr==4) return F;
    const int start=(nr&1)?5:6, S2=(squareType==normalMagic)?Sum2:x[0][0]+x[nr-1][nr-1];
    for (int m=start; m<=nr; m+=2) if (!isBorder(x, m, m, S2)) return F;
    if (squareType==normalMagic) {
      if (getBordered(x, nr, nc)) squareTypes[Bordered].isType=T;
      else if (getBordering(x, nr, nc)) squareTypes[Bordering].isType=T;
    }
  } else {
    if ((nr<5)|(nc<5)) if ((nr<=3)|(nc<=3)) return F;
    const int mid=(nr&1)?5:4, diff=(nr<nc)?nr-mid:nc-mid, startr=nr-diff, startc=nc-diff;
    for (int mr=startr, mc=startc; mr<=nr; mr+=2, mc+=2) if (!isBorder(x, mr, mc, Sum2)) return F;
    if (getBordered(x, nr, nc)) squareTypes[Bordered].isType=T;
  }
  return squareTypes[Concentric].isType=T;
} // isConcentric

bool isBordered(int **x, const int nr, const int nc) {
//   ----------
  return isConcentric(x, nr, nc)&&squareTypes[Bordered].isType;
}

bool isBordering(int **x, const int nr, const int nc) {
//   -----------
  return isConcentric(x, nr, nc)&&squareTypes[Bordering].isType;
}

bool isFranklin(int **x, const int nr, const int nc) {
//   ----------
  if ((nr&3)==0) { // else half sums not valid
    if (squareTypes[bentDiagonal].isType&(bent.ways==4)&compact[2]) {
      const int Rd2=nr/2, Cd2=nc/2, halfRowSum=rowSum/2, halfColSum=colSum/2; 
      for (int i=0; i<nr; ++i) {
        int rowH=0; for (int j=0; j<Cd2; ++j) rowH+=x[i][j];
        if (rowH!=halfRowSum) return F;
      }
      for (int i=0; i<nc; ++i) {
        int colH=0; for (int j=0; j<Rd2; ++j) colH+=x[j][i];
        if (colH!=halfColSum) return F;
      }
      return squareTypes[Franklin].isType=T;
    }
  }
  return F;
} // isFranklin

bool isBentDiagonal(int **x, const int nr, const int nc) {
//   --------------
  const int Rm1=R-1, Rd2=R/2, Rd2p1=Rd2+1, Cm1=C-1, Cd2=C/2, Cd2p1=Cd2+1; int waysBent=0;
  const bool odd=(nr&1); bool bentL=T, bentR=T, bentU=T, bentD=T;
  if (R>2) for (int i=Cm1; i>=0; --i) { // bent left ?
    int sum=0;
    for (int r=0, c=i; r<Rd2; ++r, --c) { if (c<0) c=Cm1; sum+=x[r][c]+x[Rm1-r][c]; }
    if (odd) { int j=i-Rd2; while (j<0) j+=C; sum+=x[Rd2][j]; } if (sum!=colSum) { bentL=F; break; }
  } else bentL=F;
  if (bentL) { bent.lrud|=left; ++waysBent; }
  if (R>2) for (int i=0; i<C; ++i) {  // bent right ?
    int sum=0;
    for (int r=0, c=i; r<Rd2; ++r, ++c) { if (c==C) c=0; sum+=x[r][c]+x[Rm1-r][c]; }
    if (odd) { int j=i+Rd2; while (j>=C) j-=C; sum+=x[Rd2][j]; } if (sum!=colSum) { bentR=F; break; }
  } else bentR=F;
  if (bentR) { bent.lrud|=right; ++waysBent; }
  if (C>2) for (int i=Rm1; i>=0; --i) { // bent up ?
    int sum=0;
    for (int c=0, r=i; c<Cd2; ++c, --r) { if (r<0) r=Rm1; sum+=x[r][c]+x[r][Cm1-c]; }
    if (odd) { int j=i-Cd2; while (j<0) j+=R; sum+=x[j][Cd2]; } if (sum!=rowSum) { bentU=F; break; }
  } else bentU=F;
  if (bentU) { bent.lrud|=up; ++waysBent; }
  if (C>2) for (int i=0; i<R; ++i) { // bent down ?
    int sum=0;
    for (int c=0, r=i; c<Cd2; ++c, ++r) { if (r==R) r=0; sum+=x[r][c]+x[r][Cm1-c]; }
    if (odd) { int j=i+Cd2; while (j>=R) j-=R; sum+=x[j][Cd2]; } if (sum!=rowSum) { bentD=F; break; }
  } else bentD=F;
  if (bentD) { bent.lrud|=down; ++waysBent; }
  bent.ways=waysBent; return squareTypes[bentDiagonal].isType=(waysBent>0);
} // isBentDiagonal

bool isVzigzagK(int **x, const int n, const int k) { // R==C
//   ----------
  int ways=0, lrud=0; bool zigL=T, zigR=T, zigU=T, zigD=T;

  for (int c=0; c<n; ++c) { // zig left ?
    int sum=0, i=c, j=k, incr=-1;
    for (int r=0; r<n; ++r) {
      sum+=x[r][i]; if (--j==0) { incr=-incr; j=k-1; } i+=incr; if (i<0) i+=n; else if (i >= n) i-=n;
    }
    if (sum!=magicSum) { zigL=F; break; }
  }
  if (zigL) { lrud|=left; ++ways; }

  for (int c=0; c<n; ++c) { // zig right ?
    int sum=0, i=c, j=k, incr=1;
    for (int r=0; r<n; ++r) {
      sum+=x[r][i]; if (--j==0) { incr=-incr; j=k-1; } i+=incr; if (i<0) i+=n; else if (i >= n) i-=n;
    }
    if (sum!=magicSum) { zigR=F; break; }
  }
  if (zigR) { lrud|=right; ++ways; }

  for (int r=0; r<n; ++r) { // zig up ?
    int sum=0, i=r, j=k, incr=-1;
    for (int c=0; c<n; ++c) {
      sum+=x[i][c]; if (--j==0) { incr=-incr; j=k-1; } i+=incr; if (i<0) i+=n; else if (i >= n) i-=n;
    }
    if (sum!=magicSum) { zigU=F; break; }
  }
  if (zigU) { lrud|=up; ++ways; }

  for (int r=0; r<n; ++r) { // zig down ?
    int sum=0, i=r, j=k, incr=1;
    for (int c=0; c<n; ++c) {
      sum+=x[i][c]; if (--j==0) { incr=-incr; j=k-1; } i+=incr; if (i<0) i+=n; else if (i >= n) i-=n;
    }
    if (sum!=magicSum) { zigD=F; break; }
  }
  if (zigD) { lrud|=down; ++ways; }
  if (ways>0) { Vzig[k].ways=ways; Vzig[k].lrud=lrud; if (k>2) Vzig[0].ways=1; // some > 2
                return squareTypes[Vzigzag].isType=T; } return F;
} // isVzigzagK

bool isVzigzag(int **x, const int n, const int nc) { // R==C
//   --------
  if ((n&3)==2) return F; // no singly even
  if (n>3) {
    if (n&1) {
      const int lim=n/2+2;
      for (int j=2; j<lim; j+=2) if ((((n-1)%j)==0)|(((n+1)%j)==0)) isVzigzagK(x,n,(j+2)/2); // ?
    } else {
      const int lim=n/2+1; for (int j=2; j<lim; j+=2) if ((n%j)==0) isVzigzagK(x,n,(j+2)/2);
    }
  }
  return squareTypes[Vzigzag].isType;
} // isVzigzag

bool matchSubVzigzagA(const int k, const int ways, const int lrud) {
//   ----------------
  if ((Vzig[2].ways==ways)&(Vzig[2].lrud==lrud)) return T;
  const int kd2p1=k/2+1;
  for (int f=3; f<=kd2p1; ++f) {
    const int c=f-1; int s=f;
    while (s<=k) { if ((s==k)&(VzigA[f].ways==ways)&(VzigA[f].lrud==lrud)) return T; s+=c; }
  }
  return F;
} // matchSubVzigzagA

bool isVzigzagAk(int **x, const int n, const int k) { // R==C
//   -----------
  const int km1=k-1, nmkm1=n-km1; int ways=0, lrud=0; bool zigL=T, zigR=T, zigU=T, zigD=T;

  for (int c=0; c<n; ++c) { // zig left ?
    int sum1=0, sum2=0, j=c >= km1?c-km1:c-km1+n; for (int r=0; r<n; r+=2) sum1+=x[r][c];
    for (int r=1; r<n; r+=2) sum2+=x[r][j]; if ((sum1+sum2)!=magicSum) { zigL=F; break; }
  }
  if (zigL) { lrud|=left; ++ways; }

  for (int c=0; c<n; ++c) { // zig right ?
    int sum1=0, sum2=0, j=c<nmkm1?c+km1:c+km1-n; for (int r=0; r<n; r+=2) sum1+=x[r][c];
    for (int r=1; r<n; r+=2) sum2+=x[r][j]; if ((sum1+sum2)!=magicSum) { zigR=F; break; }
  }
  if (zigR) { lrud|=right; ++ways; }

  for (int r=0; r<n; ++r) { // zig up ?
    int sum1=0, sum2=0, i=r >= km1?r-km1:r-km1+n; for (int c=0; c<n; c+=2) sum1+=x[r][c];
    for (int c=1; c<n; c+=2) sum2+=x[i][c]; if ((sum1+sum2)!=magicSum) { zigU=F; break; }
  }
  if (zigU) { lrud|=up; ++ways; }

  for (int r=0; r<n; ++r) { // zig down ?
    int sum1=0, sum2=0, i=r<nmkm1?r+km1:r+km1-n; for (int c=0; c<n; c+=2) sum1+=x[r][c];
    for (int c=1; c<n; c+=2) sum2+=x[i][c]; if ((sum1+sum2)!=magicSum) { zigD=F; break; }
  }
  if (zigD) { lrud|=down; ++ways; }

  if (ways>0) {
    if (matchSubVzigzagA(k, ways, lrud)) return F;
    VzigA[k].ways=ways; VzigA[k].lrud=lrud; VzigA[0].ways=1; // some
    return squareTypes[VzigzagA].isType=T;
  }
  return F;
} // isVzigzagAk

bool subVzigzagA(const int k) {
//   -----------
  const int kd2p1=k/2+1;
  for (int f=3; f<=kd2p1; ++f) {
    const int c=f-1; int s=f; while (s<=k) { if ((s==k)&(VzigA[f].ways==4)) return T; s+=c; }
  }
  return F;
} // subVzigzagA

bool isVzigzagA(int **x, const int n, const int nc) { // R==C
//   ----------
  if (n>3) {
    if (!(isVzigzagK(x,n,2)&&(Vzig[2].ways==4))) {
      isVzigzagAk(x,n,3); isVzigzagAk(x,n,4);
      for (int k=5; k<=n; ++k) { if (subVzigzagA(k)) continue; isVzigzagAk(x,n,k); }
    }
  }
  return squareTypes[VzigzagA].isType;
} // isVzigzagA

bool isUzigzagK(int **x, const int n, const int k) {
//   ----------
  int ways=0, lrud=0; bool zigL=T, zigR=T, zigU=T, zigD=T;

  for (int c=0; c<n; ++c) { // zig left ?
    int sum=0, i=c, j=k, incr=-1; bool t=T;
    for (int r=0; r<n; ++r) {
      sum+=x[r][i]; if (--j==0) { incr=-incr; t=F; j=k; }
      if (t) i+=incr; t=T; if (i<0) i+=n; else if (i>=n) i-=n;
    }
    if (sum!=magicSum) { zigL=F; break; }
  }
  if (zigL) { lrud|=left; ++ways; }

  for (int c=0; c<n; ++c) { // zig right ?
    int sum=0, i=c, j=k, incr=1; bool t=T;
    for (int r=0; r<n; ++r) {
      sum+=x[r][i]; if (--j==0) { incr=-incr; t=F; j=k; }
      if (t) i+=incr; t=T; if (i<0) i+=n; else if (i >= n) i-=n;
    }
    if (sum!=magicSum) { zigR=F; break; }
  }
  if (zigR) { lrud|=right; ++ways; }

  for (int r=0; r<n; ++r) { // zig up ?
    int sum=0, i=r, j=k, incr=-1; bool t=T;
    for (int c=0; c<n; ++c) {
      sum+=x[i][c]; if (--j==0) { incr=-incr; t=F; j=k; }
      if (t) i+=incr; t=T; if (i<0) i+=n; else if (i >= n) i-=n;
    }
    if (sum!=magicSum) { zigU=F; break; }
  }
  if (zigU) { lrud|=up; ++ways; }

  for (int r=0; r<n; ++r) { // zig down ?
    int sum=0, i=r, j=k, incr=1; bool t=T;
    for (int c=0; c<n; ++c) {
      sum+=x[i][c]; if (--j==0) { incr=-incr; t=F; j=k; }
      if (t) i+=incr; t=T; if (i<0) i+=n; else if (i >= n) i-=n;
    }
    if (sum!=magicSum) { zigD=F; break; }
  }
  if (zigD) { lrud|=down; ++ways; }

  if (ways>0) { Uzig[k].ways=ways; Uzig[k].lrud=lrud; if (k>2) Uzig[0].ways=1; // some > 2
                return squareTypes[Uzigzag].isType=T; }
  return F;
} // isUzigzagK

bool isUzigzag(int **x, const int n, const int nc) { // R==C
//   ---------
 if ((n&3)==0) { // only doubly even
    const int nd4=n/4;  // n/2 is bent diagonal
    for (int k=2; k<=nd4; ++k) { const int len=k+k; if ((n%len)==0) isUzigzagK(x,n,k); }
  }
  return squareTypes[Uzigzag].isType;
} // isUzigzag

bool isCompactK(int **x, const int nr, const int nc, const int k) {
//   ----------
  const int mr=nr-1; int Sk=0, S; for (int r=0; r<k; ++r) for (int c=0; c<k; ++c) Sk+=x[r][c]; S=Sk;
  for (int r=0; r<nr; ++r) {
    if (r>0) {
      int i=r-1, l=r+k-1; if (l>=nr) l-=nr;
      for (int j=0; j<k; ++j) { S-=x[i][j]; S+=x[l][j]; } if (S!=Sk) return F;
    }
    for (int c=1; c<nc; ++c) {
      for (int h=r; h<(r+k); ++h) {
        int i=(h>=nr)?h-nr:h, j=c-1, l=c+k-1; if (l>=nc) l-=nc; S-=x[i][j]; S+=x[i][l];
      }
      //if (k==2) printf("r %d c %d S %d\n", r, c, S);
      if (S!=Sk) return F;
    }
  }
  if (k>2) compact[0]=T; /* there is a k > 2 */ return compact[k]=T;
} // isCompactK

bool subCompact(const int k) {
//   ----------
  const int kd2=k/2; for (int f=2; f<=kd2; ++f) if (((k%f)==0)&compact[f]) return T; return F;
} // subCompact

bool isCompact(int **x, const int nr, const int nc) {
//   ---------
  bool any=F;
  for (int k=2; k<Nk; ++k) if (((nr%k)==0)&((nc%k)==0)) {
    if (subCompact(k)) continue; if (isCompactK(x,nr,nc,k)) any=T;
  }
  return squareTypes[Compact].isType=any;
} // isCompact

bool isComplete(int **x, const int n, const int nc) { // R==C
//   ----------
  if ((n&1)!=0) return F; // only even
  if ((squareType==normalMagic)&((n&3)!=0)) return F; // only doubly even
  const int nd2=n/2, nd2m1=nd2-1, S2=(chkSum+chkSum)/n;

  for (int r=0; r<nd2m1; ++r) { // no need to check last pair in each diag
    const int i=r+nd2;
    for (int c=0; c<n; ++c) { int j=c+nd2; j=j<n?j:j-n; if ((x[r][c]+x[i][j])!=S2) return F; }
  }
  return squareTypes[Complete].isType=T;
} // isComplete

bool isNearPandiagonal(int **x, const int n, const int nc) { // R==C
//   -----------------
  const int nm1=n-1, maxCount=4; int count=0;

  for (int i=0; i<n; ++i) {
    int sumYX=0, sumXY=0;
    for (int r=0, c=i; r<n; ++r, ++c) { int cModn=c<n?c:c-n; sumYX+=x[r][cModn]; sumXY+=x[r][nm1-cModn]; }
    if (sumYX!=chkSum) ++count; if (sumXY!=chkSum) ++count; if (count>maxCount) return F;
  }
  return squareTypes[nearPandiagonal].isType=T;
} // isNearPandiagonal

bool getPanDiagonal(int **x, const int nr, const int nc) {
//   -------------
  const int Cm1=C-1; bool panBack=T, panForward=T;
  if (R<=C) {
    int sum=0; for (int r=0; r<nr; ++r) sum+=x[r][r]; 
    for (int i=0; i<nc; ++i) {
      int sumYX=0, sumXY=0;
      for (int r=0, c=i; r<nr; ++r, ++c) {
        const int cModC=c<C ? c : c-C; sumYX+=x[r][cModC]; sumXY+=x[r][Cm1-cModC];
      }
      if (sumYX!=sum) panBack=F; if (sumXY!=sum) panForward=F; if (!(panBack|panForward)) return F;
    }
  } else {
    int sum=0, sumYX=0, sumXY=0; for (int c=0; c<nc; ++c) sum+=x[c][c];
    for (int i=0; i<nr; ++i) {
      int sum=0, sumYX=0, sumXY=0; for (int c=0; c<nc; ++c) sum+=x[0][c]; 
      for (int r=i, c=0; c<C; ++r, ++c) {
        const int rModR=r<R ? r : r-R; sumYX+=x[rModR][c]; sumXY+=x[rModR][Cm1-c];
      }
      if (sumYX!=sum) panBack=F; if (sumXY!=sum) panForward=F; if (!(panBack|panForward)) return F;
    }
  }
  if (panBack&panForward) {
    if (nr==nc) isComplete(x, nr, Sum2);
    return squareTypes[Pandiagonal].isType=T;
  }
  return squareTypes[Pandiagonal1Way].isType=T;
} // getPanDiagonal

bool isSymlateralOdd(int **x, const int n) {
//   ---------------
  const int nd2=n/2, nm1=n-1, S2=Sum2;

  // Diagonals
  for (int r=0; r<nd2; ++r) {
    if (x[r][r]+x[nm1-r][nm1-r]!=S2) return F; if (x[r][nm1-r]+x[nm1-r][r]!=S2) return F;
  }
  // Center row(s), column(s)
  for (int i=0; i<nd2; ++i) {
    if (x[i][nd2]+x[nm1-i][nd2]!=S2) return F; if (x[nd2][i]+x[nd2][nm1-i]!=S2) return F;
  }
  // Rest of rows, columns
  for (int i=0; i<nd2; ++i) {
    for (int j=i+1; j<nd2; ++j) {
      if (x[i][j]+x[i][nm1-j]!=S2) return F; if (x[j][i]+x[nm1-j][i]!=S2) return F;
    }
  }
  return squareTypes[Symlateral].isType=T;
} // isSymlateralOdd

bool isSymlateralEven(int **x, const int n) {
//   ----------------
  const int nd2=n/2, nm1=n-1, nd2m1=nd2-1, S2=Sum2, maxCount=8; int count=0;

  // Diagonals
  for (int r=0; r<nd2; ++r) {
    if (x[r][r]+x[nm1-r][nm1-r]!=S2) if (++count>maxCount) return F;
    if (x[r][nm1-r]+x[nm1-r][r]!=S2) if (++count>maxCount) return F;
  }
  // Center row(s), column(s)
  for (int j=nd2m1; j<=nd2; ++j) {
    for (int i=0; i<nd2m1; ++i) {
      if (x[i][j]+x[nm1-i][j]!=S2) if (++count>maxCount) return F;
      if (x[j][i]+x[j][nm1-i]!=S2) if (++count>maxCount) return F;
    }
  }

  // Rest of rows, columns
  for (int i=0; i<nd2m1; ++i) {
    for (int j=i+1; j<nd2m1; ++j) {
      if (x[i][j]+x[i][nm1-j]!=S2) return F; if (x[j][i]+x[nm1-j][i]!=S2) return F;
    }
  }
  return squareTypes[nearSymlateral].isType=T;
} // isSymlateralEven

bool isSymlateral(int **x, const int n, const int nc) { // R==C
//   ------------
  if (n<=4) return F; return (n&1)?isSymlateralOdd(x, n):isSymlateralEven(x, n);
} // isSymlateral

bool isSelfComplement(int **x, const int nr, const int nc) {
//   ----------------
  if (isAssociative(x, nr, nc)) return squareTypes[selfComplement].isType=T;
  if (nr&1) return F; /* only even */ const int mr=nr-1, mc=nc-1, midr=nr/2, midc=nc/2, S2=Sum2;

  if ((x[0][0]+x[0][mc])==S2) {
    for (int r=0; r<nr; ++r) for (int c=0; c<midc; ++c) if ((x[r][c]+x[r][mc-c])!=S2) return F;
  } else if ((x[0][0]+x[mr][0])==S2) {
    for (int r=0; r<midr; ++r) for (int c=0; c<nc; ++c) if ((x[r][c]+x[mr-r][c])!=S2) return F;
  } else return F;
  return squareTypes[selfComplement].isType=T;
} // isSelfComplement

bool hasAxialParity(int **x, const int nr, const int nc) {
//   --------------
  const int mr=nr-1, mc=nc-1, gr=nr/2, gc=nc/2; int waysPar=0; bool parLR=T, parUD=T;
  for (int r=0; r<nr; ++r) {
    for (int c=0; c<gc; ++c) if ((x[r][c]&1)!=(x[r][mc-c]&1)) { parLR=F; break; } if (!parLR) break;
  }
  if (parLR) { ++waysPar; parity.lrud|=left; }

  for (int r=0; r<gr; ++r) {
    for (int c=0; c<nc; ++c) if ((x[r][c]&1)!=(x[mr-r][c]&1)) { parUD=F; break; } if (!parUD) break;
  }
  if (parUD) { ++waysPar; parity.lrud|=up; }
  parity.ways=waysPar; return squareTypes[axialParity].isType=(waysPar>0);
} // hasAxialParity

bool hasSymmetricParity(int **x, const int nr, const int nc) {
//   ------------------
  const int mr=nr-1, mc=nc-1, gr=nr/2, gc=nc/2;
  for (int r=0; r<gr; ++r) for (int c=0; c<nc; ++c) if ((x[r][c]&1)!=(x[mr-r][mc-c]&1)) return F;
  if (nr&1) for (int c=0; c<gc; ++c) if ((x[gr][c]&1)!=(x[gr][mc-c]&1)) return F;
  return squareTypes[symmetricParity].isType=T;
} // hasSymmetricParity

bool hasTransposeParity(int **x, const int n, const int nc) { // R==C
//   ------------------
  const int m=n-1;
  for (int r=0; r<m; ++r) for (int c=r; c<n; ++c) if ((x[r][c]&1)!=(x[c][r]&1)) return F;
  return squareTypes[transposeParity].isType=T;
} // hasTransposeParity

bool isNotMagic(int **x, const int nr, const int nc) { return squareType==notMagic; }
//   -----------

bool isNormalSemiMagic(int **x, const int nr, const int nc) { return squareType==normalSemiMagic; }
//   -----------------

bool isOtherSemiMagic(int **x, const int nr, const int nc) { return squareType==otherSemiMagic; }
//   ----------------

bool isNormalMagic(int **x, const int nr, const int nc) { return squareType==normalMagic; }
//   -------------

bool isOtherMagic(int **x, const int nr, const int nc) { return squareType==otherMagic; }
//   ------------

//bool biPandiagonal;
//bool isBiPanDiagonal(int **x, const int n, const int nc, const int bSum) {
////   ---------------
//  const int nm1=n-1;
//
//  // Main diagonals already checked in isBiMagic.
//  for (int i=1; i<n; ++i) {
//    int sumYX=0, sumXY=0, z;
//    for (int r=0, c=i; r<n; ++r, ++c) {
//      const int cModn=c<n?c:c-n; z=x[r][cModn]; sumYX+=z*z; z=x[r][nm1-cModn]; sumXY+=z*z;
//    }
//    if ( (sumYX!=bSum)|(sumXY!=bSum) ) return F;
//  }
//  return T;
//} // isBiPanDiagonal

bool isBiMagic(int **x, const int n, const int nc) { // R==C
//   ---------
  if (n<8) return n==1;
  const int nn=RC; int sumXY=0, sumYX=0, bSum; // bSum=n*(nn+1)*(nn+nn+1)/6

  for (int r=0; r<n; ++r) {
    int sumX=0, sumY=0, z; for (int c=0; c<n; ++c) { z=x[r][c]; sumX+=z*z; z=x[c][r]; sumY+=z*z; }

    if (r==0) bSum=sumX; // to handle overflow
    if ((sumX!=bSum)|(sumY!=bSum)) { return F; } z=x[r][n-r-1]; sumXY+=z*z; z=x[r][r]; sumYX+=z*z;
  }
  if ( (sumXY!=bSum)|(sumYX!=bSum) ) return F;
  //biPandiagonal=squareTypes[Pandiagonal].isType&&isBiPanDiagonal(x, n, bSum);
  return squareTypes[biMagic].isType=T;
} // isBiMagic

bool isTriMagic(int **x, const int n, const int nc) { // R==C
//   ----------
  if (n<12) return n==1; if (!isBiMagic(x, n, nc)) return F;
  const int nn=RC; int sumXY=0, sumYX=0, tSum; // tSum=n*nn*(nn+1)*(nn+1)/4

  for (int r=0; r<n; ++r) {
    int sumX=0, sumY=0, z; for (int c=0; c<n; ++c) { z=x[r][c]; sumX+=z*z*z; z=x[c][r]; sumY+=z*z*z; }
    if (r==0) tSum=sumX; // to handle overflow
    if ((sumX!=tSum)|(sumY!=tSum)) { return F; } z=x[r][n-r-1]; sumXY+=z*z*z; z=x[r][r]; sumYX+=z*z*z;
  }
  if ( (sumXY!=tSum)|(sumYX!=tSum) ) return F; return squareTypes[triMagic].isType=T;
} // isTriMagic

bool isPrime(const int k) { int i=2; if (k<=1) return F; while ((i*i)<=k) if ((k%i++)==0) return F; return T; }
//   -------

bool checkUnique(int **x, const int n) {
//   -----------
  for (int r=0; r<n; ++r) {
    for (int c=0; c<n; ++c) {
      const int t=x[r][c]; for (int c1=c+1; c1<n; ++c1) if (x[r][c1]==t) return F;
      for (int r1=r+1; r1<n; ++r1) for (int c1=0; c1<n; ++c1) if (x[r1][c1]==t) return F;
    }
  }
  return T;
} // checkUnique

bool isPrimeNumSquare(int **x, const int n, const int nc) { // R==C
//   ----------------
  bool has1=F;
  for (int r=0; r<n; ++r) for (int c=0; c<n; ++c)
    if (x[r][c]==1) has1=T; else if (!isPrime(x[r][c])) return F;
  if (checkUnique(x, n)) { return has1 ? squareTypes[Prime1].isType=T : squareTypes[Prime].isType=T; }
  return F;
} // isPrimeNumSquare

bool isPrimeMagic(int **x, const int n, const int nc) { return isPrimeNumSquare(x, n, nc)&&squareTypes[Prime].isType; }
//   -----------

bool isPrime1Magic(int **x, const int n, const int nc) { return isPrimeNumSquare(x, n, nc)&&squareTypes[Prime1].isType; }
//   -------------

bool isAdjacentSide(int **x, const int n, const int nc) { return (n&1)?isAdjacentSideOdd(x, n):isAdjacentSideEven(x, n); }
//   --------------

bool isPandiagonal1Way(int **x, const int nr, const int nc) {
//   -----------------
  return getPanDiagonal(x, nr, nc)&&squareTypes[Pandiagonal1Way].isType;
}

bool isPandiagonal(int **x, const int nr, const int nc) {
//   -------------
  return getPanDiagonal(x, nr, nc)&&squareTypes[Pandiagonal].isType;
}

bool isNearSymlateral(int **x, const int n, const int nc) { return isSymlateral(x, n, nc)&&squareTypes[nearSymlateral].isType; }
//   ----------------

bool isLS(int **x, const int n, const int nc) { return (squareType==LS)|((n==1)&(squareType==normalMagic)); }
//   -----

bool isDLS(int **x, const int n, const int nc) { return (squareType==DLS)|((n==1)&(squareType==normalMagic)); }
//   -----

bool isNearAssociativeDLS(int **x, const int n, const int nc) {
//   --------------------
  if (squareType!=DLS) return F; if ((n&3)!=2) return F; 
  const int nd2=n/2, m=n-1, S2=zeroBase?n-1:n+1, maxCount=2; int count=0;

  for (int r=0; r<nd2; ++r) for (int c=0; c<n; ++c) {
    if (x[r][c]+x[m-r][m-c]!=S2) if (++count>maxCount) return F;
  }
  return squareTypes[nearAssociativeDLS].isType=T;
} // isNearAssociativeDLS

bool isAssociativeDLS(int **x, const int n, const int nc) {
//   ----------------
  if (n==1) return (squareType==normalMagic); if (squareType!=DLS) return F;
  if ((n&3)==2) return F; bool odd=(n&1); const int nd2=n/2, m=n-1, S2=zeroBase?n-1:n+1;
  for (int r=0; r<nd2; ++r) if ((x[r][r]+x[m-r][m-r])!=S2) return F;
  for (int r=0; r<m; ++r) for (int c=r+1; c<n; ++c) if (x[r][c]+x[m-r][m-c]!=S2) return F;
  return squareTypes[AssociativeDLS].isType=T;
} // isAssociativeDLS

// Pandiagonal, Knut Vik design. Not even and not a multiple of 3. R%6 equals 1 or 5
bool isPLS(int **x, const int n, const int nc) {
//   -----
  if (n==1) return (squareType==normalMagic); if (squareType!=DLS) return F; const int mod6=n%6, nm1=n-1;
  if ((mod6==1)|(mod6==5)) {
    const int min=zeroBase?0:1, max=n+min-1, m=n-1; bool pls=T;
    for (int i=0; i<n; ++i) {
      for (int j=min; j<=max; j++) numUsed[j]=F; for (int r=0, c=i; r<n; ++r, ++c) numUsed[x[r][c<n?c:c-n]]=T;
      for (int j=min; j<=max; ++j) if (!numUsed[j]) { pls=F; break; }
    }
    if (pls) for (int i=0; i<n; ++i) {
      for (int j=min; j<=max; j++) numUsed[j]=F; for (int r=0, c=i; r<n; ++r, ++c) numUsed[x[r][nm1-(c<n?c:c-n)]]=T;
      for (int j=min; j<=max; ++j) if (!numUsed[j]) { pls=F; break; }
    }
    return squareTypes[PLS].isType=pls;
  }
  return F;
} // isPLS

bool checkCyclic(int **x, const int n) {
//   -----------
   if (!isPLS(x,n,n)) return F;
   const int m=n-1; int ways=0; int *seq=opp; bool rows=T, cols=T, bDiags=T, fDiags=T; int lrud=0;
   for (int c=0; c<n; ++c) seq[c]=x[0][c];
   for (int r=1; r<n; ++r) {
     int loop=n-1, i=1, j=0; for (int c=0; c<n; ++c) if (x[r][c]==seq[0]) { j=(c+1)%n; break; }
     while (loop--) { if (x[r][j++]!=seq[i++]) { rows=F; break; } if (j==n) j=0; } if (!rows) break;
   } if (rows) { ++ways; lrud|=left; } // use left for rows

   for (int r=0; r<n; ++r) seq[r]=x[r][0];
   for (int c=1; c<n; ++c) {
     int loop=n-1, i=1, j=0; for (int r=0; r<n; ++r) if (x[r][c]==seq[0]) { j=(r+1)%n; break; }
     while (loop--) { if (x[j++][c]!=seq[i++]) { cols=F; break; } if (j==n) j=0; } if (!cols) break;
   } if (cols) { ++ways; lrud|=right; } // use right for cols

   //if (rows|cols) {
     for (int r=0; r<n; ++r) seq[r]=x[r][r];
     for (int b=1; b<n; ++b) {
       int loop=m, i=1, jr=0, jc=0;
       for (int r=0, c=b; r<n; ++r) { if (x[r][c]==seq[0]) { jr=(r+1)%n; jc=(c+1)%n; break; } if (++c==n) c=0; }
       while (loop--) { if (x[jr][jc]!=seq[i++]) { bDiags=F; break; } if (++jr==n) jr=0; if (++jc==n) jc=0; }
       if (!bDiags) break;
     } if (bDiags) { ++ways; lrud|=up; } // use up for bDiags

     for (int r=0; r<n; ++r) seq[r]=x[r][m-r];
     for (int f=m-1; f>=0; --f) {
       int loop=m, i=1, jr=0, jc=0;
       for (int r=0, c=f; r<n; ++r) {
         if (x[r][c]==seq[0]) { jr=(r+1)%n; jc=(c-1); if (jc<0) jc=m; break; } if (--c<0) c=m;  }
       while (loop--) { if (x[jr][jc]!=seq[i++]) { fDiags=F; break; } if (++jr==n) jr=0; if (--jc<0) jc=m; }
       if (!fDiags) break;
     } if (fDiags) { ++ways; lrud|=down; } // use down for fDiags

     if (rows&cols) { cyclic.ways=ways; cyclic.lrud=lrud; return squareTypes[cyclicPLS].isType=T; }
     else if (ways>0) { semiCyclic.ways=ways; semiCyclic.lrud=lrud; return squareTypes[semiCyclicPLS].isType=T; }
   //}
   return F;
} // checkCyclic

bool isCyclic(int **x, const int n, const int nc) { checkCyclic(x, n); return squareTypes[cyclicPLS].isType; }
//   ---------

bool isSemiCyclic(int **x, const int n, const int nc) { checkCyclic(x, n); return squareTypes[semiCyclicPLS].isType; }
//   ------------

bool isWPLS(int **x, const int n, const int nc) { // Weakly pandiagonal. All diagonals have same sum.
//   ------
  if ((squareType!=LS)&(squareType!=DLS)) return F; if ((squareType==DLS)&&isPLS(x,n,n)) return F;
  const int nm1=n-1; int Sn=0; for (int r=0; r<n; ++r) Sn+=x[r][r];

  for (int i=1; i<n; ++i) {
    int S=0; for (int r=0, c=i; r<n; ++r, ++c) S+=x[r][c<n?c:c-n]; if (S!=Sn) return F;
  }
  for (int i=0; i<n; ++i) {
    int S=0; for (int r=0, c=i; r<n; ++r, ++c) S+=x[r][nm1-(c<n?c:c-n)]; if (S!=Sn) return F;
  }
  return squareTypes[WPLS].isType=T;
} // isWPLS

bool isDSym(int **x, const int n, const int nc) { 
//   ------
  if ((squareType!=LS)&(squareType!=DLS)) return F;
  const int nd2=n/2, m=n-1; for (int v=0; v<=n; ++v) opp[v]=-1;
  for (int r=0; r<n; ++r) {
    if (opp[x[r][0]]>=0) if (opp[x[r][0]]!=x[r][m]) return F; opp[x[r][0]]=x[r][m]; opp[x[r][m]]=x[r][0];
  }
  for (int r=0; r<n; ++r) for (int c=1; c<nd2; ++c) if (x[r][c]!=opp[x[r][m-c]]) return F;
  for (int v=0; v<=n; ++v) opp[v]=-1;
  for (int c=0; c<n; ++c) {
    if (opp[x[0][c]]>=0) if (opp[x[0][c]]!=x[m][c]) return F; opp[x[0][c]]=x[m][c]; opp[x[m][c]]=x[0][c];
  }
  for (int r=1; r<nd2; ++r) for (int c=0; c<n; ++c) if (x[r][c]!=opp[x[m-r][c]]) return F;
  return squareTypes[DSym].isType=T;
} // isDSym

bool isSym(int **x, const int n, const int nc) { 
//   -----
  if ((squareType!=LS)&(squareType!=DLS)) return F;
  const int nd2=n/2, m=n-1; int symRows=T; for (int v=0; v<=n; ++v) opp[v]=-1;
  for (int r=0; r<n; ++r)
    if (opp[x[r][0]]>=0) { if (opp[x[r][0]]!=x[r][m]) { symRows=F; break; }
    } else { opp[x[r][0]]=x[r][m]; opp[x[r][m]]=x[r][0]; }

  if (symRows) for (int r=0; r<n; ++r) {
    for (int c=1; c<nd2; ++c) if (x[r][c]!=opp[x[r][m-c]]) { symRows=F; break; } if (!symRows) break;
  }
  if (symRows) { if (isDSym(x, n,n)) return F; return squareTypes[Sym].isType=T; }
  for (int v=0; v<=n; ++v) opp[v]=-1;
  for (int c=0; c<n; ++c)
    if (opp[x[0][c]]>=0) { if (opp[x[0][c]]!=x[m][c]) return F; }
    else { opp[x[0][c]]=x[m][c]; opp[x[m][c]]=x[0][c]; }
  for (int r=1; r<nd2; ++r) for (int c=0; c<n; ++c) if (x[r][c]!=opp[x[m-r][c]]) return F;
  return squareTypes[Sym].isType=T;
} // isSym

bool isCSym(int **x, const int n, const int nc) {
//   ------
  //if (squareType==DLS) {
  //  if ((n&3)==2) return isNearCSym(x,n); if (squareTypes[AssociativeDLS].isType) return F;
  //}
  if (n==1) return (squareType==normalMagic); if ((squareType!=LS)&(squareType!=DLS)) return F;
  const int nd2=n/2, m=n-1; for (int v=0; v<=n; ++v) opp[v]=-1;
  for (int c=0; c<n; ++c) {
    if (opp[x[0][c]]>=0) { if (opp[x[0][c]]!=x[m][m-c]) return F; }
    else { opp[x[0][c]]=x[m][m-c]; opp[x[m][m-c]]=x[0][c]; }
  }
  for (int r=1; r<nd2; ++r) if (x[r][r]!=opp[x[m-r][m-r]]) return F;
  for (int r=1; r<m; ++r) for (int c=r+1; c<n; ++c) if (x[r][c]!=opp[x[m-r][m-c]]) return F;
  return squareTypes[CSym].isType=T;
} // isCSym

bool isNearCSym(int **x, const int n, const int nc) {
//   ----------
  if ((squareType!=LS)&(squareType!=DLS)) return F; if ((n&3)!=2) return F;
  if ((squareType==DLS)&&isNearAssociativeDLS(x,n,n)) return F;
  if ((squareType==LS)&&isCSym(x,n,n)) return F;

  const int nd2=n/2, nm1=n-1; int **count=tSquare, viols=0;
  for (int r=0; r<n; ++r) for (int c=0; c<n; ++c) count[r][c]=0;
  for (int r=0; r<nd2; ++r) for (int c=0; c<n; ++c) {
    int v=x[r][c], o=x[nm1-r][nm1-c]; if (!zeroBase) { --v; --o; } ++count[v][o];
  }
  for (int v=0; v<n; ++v) {
    bool gt_one=F;
    for (int o=0; o<n; ++o) if (count[v][o]>0) {
      if (count[v][o]==1) ++viols; else { if (gt_one) return F; gt_one=T; }
    }
  }
  return (viols>2)?F:squareTypes[nearCSym].isType=T;
} // isNearCSym

bool isOrthogonal(int **x, const int n, const int nc) {
//   ------------
  if (squareNum==1) return F; if (n==1) return (squareType==normalMagic)&(prevType==normalMagic);
  int **y=ySquare; bool **b=QR;
  if (((squareType==LS)|(squareType==DLS))&((prevType==LS)|(prevType==DLS))) {
    const int min=zeroBase?0:1, max=zeroBase?n-1:n;
    for (int r=0; r<=n; ++r) for (int c=0; c<=n; ++c) b[r][c]=F;
      // x's zeroBase may be different from y's
    for (int r=0; r<n; ++r) for (int c=0; c<n; ++c) {
      const int u=x[r][c], v=y[r][c]; if (b[u][v]) return F; b[u][v]=T;
    }
    return squareTypes[OLS].isType=T;
  }
  return F;
} // isOrthogonal

bool isSelfOrthogonal(int **x, const int n, const int nc) {
//   ----------------
  if (n==1) return (squareType==normalMagic); if ((squareType!=LS)&(squareType!=DLS)) return F;
  bool **b=QR; const int min=zeroBase?0:1, max=zeroBase?n-1:n;
  for (int r=min; r<=max; ++r) for (int c=min; c<=max; ++c) b[r][c]=F;
  for (int r=0; r<n; ++r) for (int c=r; c<n; ++c) {
    const int u=x[r][c], v=x[c][r]; if (b[u][v]) return F; b[u][v]=T; b[v][u]=T;
  }
  return squareTypes[SOLS].isType=T;
} // isSelfOrthogonal

bool isDoublySelfOrthogonal(int **x, const int n, const int nc) {
//   ----------------------
  if (isSelfOrthogonal(x,n,n)) {
    bool **b=QR; const int min=zeroBase?0:1, max=zeroBase?n-1:n, m=n-1;
    for (int r=min; r<=max; ++r) for (int c=min; c<=max; ++c) b[r][c]=F;
    for (int r=0; r<n; ++r) for (int c=m-r; c>=0; --c) {
      const int u=x[r][c], v=x[m-c][m-r]; if (b[u][v]) return F; b[u][v]=T; b[v][u]=T;
    }
    return squareTypes[DSOLS].isType=T;
  }
  return F;
} // isDoublySelfOrthogonal

bool isSelfTranspose(int **x, const int n, const int nc) {
//   ---------------
  for (int r=0; r<n; ++r) for (int c=r+1; c<n; ++c) if (x[r][c]!=x[c][r]) return F;
  return squareTypes[selfTranspose].isType=T;
} // isSelfTranspose

bool isNFR(int **x, const int n, const int nc) {
//   -----
  const int min=zeroBase?0:1; for (int i=0; i<n; ++i) if (x[0][i]!=(i+min)) return F;
  return squareTypes[nfr].isType=T;
} // isNFR

bool isNFC(int **x, const int n, const int nc) {
//   -----
  const int min=zeroBase?0:1; for (int i=0; i<n; ++i) if (x[i][0]!=(i+min)) return F;
  return squareTypes[nfc].isType=T;
} // isNFC

bool isNFR_NFC(int **x, const int n, const int nc) {
//   ---------
  const int min=zeroBase?0:1;
  for (int i=0; i<n; ++i) { const int v=i+min; if ((x[0][i]!=v)|(x[i][0]!=v)) return F; }
  return squareTypes[nfr_nfc].isType=T;
} // isNFR_NFC

bool isNBD(int **x, const int n, const int nc) {
//   -----
  const int min=zeroBase?0:1; for (int i=0; i<n; ++i) if (x[i][i]!=(i+min)) return F;
  return squareTypes[nbd].isType=T;
} // isNBD

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

bool isReversible(int **x, const int n, const int nc) {
//   ------------
  if (!((R==C)&&allNumbersUsed(x))) return F; const int m=n-1, g=n/2;
  // 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 squareTypes[Reversible].isType=T;
} // isReversible

typedef bool (*t_isType)(int **, const int, const int);
static t_isType isType[]=
{
  isNotMagic,
  isNormalSemiMagic,
  isOtherSemiMagic,
  isNormalMagic,
  isOtherMagic,
  isBiMagic,
  isTriMagic,
  isPrimeMagic,
  isPrime1Magic,
  isAdjacentCorner,
  isAdjacentSide,
  isAssociative,
  isOddAssociative,
  isEvenAssociative,
  isNearAssociative,
  isConcentric,
  isBordered,
  isBordering,
  isSymlateral,
  isNearSymlateral,
  isPandiagonal,
  isNearPandiagonal,
  isComplete,
  isCompact,
  isPandiagonal1Way,
  isBentDiagonal,
  isFranklin,
  isUzigzag,
  isVzigzag,
  isVzigzagA,
  isSelfComplement,
  isLS,
  isDLS,
  isAssociativeDLS,
  isNearAssociativeDLS,
  isPLS,
  isCyclic,
  isSemiCyclic,
  isWPLS,
  isSym,
  isDSym,
  isCSym,
  isNearCSym,
  isNFR,
  isNFC,
  isNFR_NFC,
  isSelfTranspose,
  isNBD,
  isOrthogonal,
  isSelfOrthogonal,
  isDoublySelfOrthogonal,
  isReversible,
  hasAxialParity,
  hasSymmetricParity,
  hasTransposeParity
};

bool matchCompact(const bool in) {
//   ------------
  bool *c=in?getTypeCompact:excludeTypeCompact; if (c[2]&(!compact[2])) return F;
  if (c[0]) { // any c > 2
    if (!compact[0]) return F; // no compact > 2
    for (int i=3; i<Nk; ++i) if (c[i]&(!compact[i])) return F;
  }
  return T;
} // matchCompact

bool matchBent(const bool in) {
//   ---------
  bool *b=in?getTypeBent:excludeTypeBent; for (int j=1; j<5; ++j) if (b[j]&(bent.ways!=j)) return F; return T;
} // matchBent

bool matchUzigzag(const bool in) {
//   ------------
  struct t_bool5 *w=in?getTypeUzig:excludeTypeUzig;
  if (w[2].b[0]) { // any w[2]
    if (Uzig[2].ways==0) return F; // no Uzig[2]
    for (int j=1; j<5; ++j) if ((w[2].b[j])&(Uzig[2].ways!=j)) return F;
  }
  if (w[0].b[0]) { // any w > 2
    if (Uzig[0].ways==0) return F; // no Uzig > 2
    for (int i=3; i<Nk; ++i) if (w[i].b[0]) { // any w[i]
      if (Uzig[i].ways==0) return F; // no Uzig[i]
      for (int j=1; j<5; ++j) if ((w[i].b[j])&(Uzig[i].ways!=j)) return F;
    }
  }
  return T;
} // matchUzigzag

bool matchVzigzag(const bool in) {
//   -----------
  struct t_bool5 *z=in?getTypeVzig:excludeTypeVzig;

  if (z[2].b[0]) { // any z[2]
    if (Vzig[2].ways==0) return F; // no Vzig[2]
    for (int j=1; j<5; ++j) if ((z[2].b[j])&(Vzig[2].ways!=j)) return F;
  }
  if (z[0].b[0]) { // any z > 2
    if (Vzig[0].ways==0) return F; // no Vzig > 2
    for (int i=3; i<Nk; ++i) if (z[i].b[0]) { // any z[i]
      if (Vzig[i].ways==0) return F; // no Vzig[i]
      for (int j=1; j<5; ++j) if ((z[i].b[j])&(Vzig[i].ways!=j)) return F;
    }
  }
  return T;
} // matchVzigzag

bool matchVzigzagA(const bool in) {
//   -------------
  struct t_bool5 *z=in?getTypeVzigA:excludeTypeVzigA;

  if (z[0].b[0]) { // any z
    if (VzigA[0].ways==0) return F; // no VzigA
    for (int i=3; i<=R; ++i) if (z[i].b[0]) { // any z[i]
      if (VzigA[i].ways==0) return F; // no VzigA[i]
      for (int j=1; j<5; ++j) if ((z[i].b[j])&(VzigA[i].ways!=j)) return F;
    }
  }
  return T;
} // matchVzigzagA

bool matchCyclic(const bool in) {
//   -----------
  bool *b=in?getTypeCyclic:excludeTypeCyclic; if (b[0]) return T; // no way specified, natch any
  for (int j=2; j<5; ++j) if (b[j]&(cyclic.ways!=j)) return F; return T;
} // matchCyclic

bool matchSemiCyclic(const bool in) {
//   ---------------
  bool *b=in?getTypeSemiCyclic:excludeTypeSemiCyclic; if (b[0]) return T; // no way specified, natch any
  for (int j=1; j<4; ++j) if (b[j]&(semiCyclic.ways!=j)) return F; return T;
} // matchSemiCyclic

bool matchParity(const bool in) {
//   -----------
  bool *b=in?getTypeParity:excludeTypeParity;
  for (int j=1; j<3; ++j) if (b[j]&(parity.ways!=j)) return F; return T;
} // matchParity

bool matchTypes(int **x, const int nr, const int nc, bool list[numTypes], const bool in) {
//   ----------
  for (int i=firstType; i<=lastType; ++i) {
    if (list[i]) {
      if (isType[i](x, nr, nc)) {
        switch (i) {
          case Compact: if (!matchCompact(in)) return F; break;
          case bentDiagonal: if (!matchBent(in)) return F; break;
          case Uzigzag: if (!matchUzigzag(in)) return F; break;
          case Vzigzag: if (!matchVzigzag(in)) return F; break;
          case VzigzagA: if (!matchVzigzagA(in)) return F; break;
          case axialParity: if (!matchParity(in)) return F; break;
          case cyclicPLS: if (!matchCyclic(in)) return F; break;
          case semiCyclicPLS: if (!matchSemiCyclic(in)) return F; break;
          default: break;
        }
      } else {
        // Accept DLS, LS as LS orthogonal pair.
        if (!((i==LS)&list[OLS]&(prevType==LS)&(squareType==DLS))) return F;
      }
      // Don't accept LS, DLS as DLS orthogonal pair.
      if ((i==OLS)&list[DLS]&(prevType==LS)) return F;
    }
  }
  return T;
} // matchTypes

bool copySquares(FILE *rfp, FILE *wfp) {
//   -----------
  printf("\nCopy %ss matching types:\n  ", R==C ? "square" : "rectangle"); bool start=T, first=F;
  for (int i=firstType; i<=lastType; ++i) {
    if (getType[i]) {
      if (start) start=F; else printf(", "); printf("%s", squareTypes[i].name); addTypeInfo(i, T);
    }
  }
  putchar('\n');
  if (!noExcludeTypes) {
    printf("    but not matching types:\n  "); start=T;
    for (int i=firstType; i<=lastType; ++i) {
      if (excludeType[i]) {
        if (start) start=F; else printf(", "); printf("%s", squareTypes[i].name); addTypeInfo(i, F);
      }
    }
    putchar('\n');
  }
  while (readSquare(rfp)) {
    initSquareTypes();
    if (matchTypes(xSquare, R, C, getType, T)&&(noExcludeTypes||!matchTypes(xSquare, R, C, excludeType, F))) {
      if (getType[OLS]) {
        int **t=xSquare; xSquare=ySquare; if (!writeSquare(wfp)) return F;
        xSquare=t; --numSquares; // count pair
      }
      if (!writeSquare(wfp)) return F;
    }
    int **t=xSquare; xSquare=ySquare; ySquare=t; prevType=squareType;
  }
  return T;
} // copySquares
//================================================== main =================================================

bool validOrder(const int r, const int c) {
//   ----------
 if ((r<=0)|(r<=0)) { printf("\aOrder must be positive integer(s).\n"); return F; }
 if ((r&1)!=(c&1)) {
   printf("\a%s by %s is not supported.\n", (r&1) ? "Odd" : "Even", (r&1) ? "even" : "odd"); return F;
 }
 return T;
} // validOrder

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 outputElapsedTime(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);
} // outputtElapsedTime

int main() {
//  ----
  bool another=T, inputOrder=T, noWriteError=T, ok=F; allocatedR=0; allocatedC=0; outputLocalTime();
  do {
    if (inputOrder) { printf("\nOrder? "); if (!getInts(&R, &C, -1)) break; }
    if (validOrder(R, C)) {
      initGlobals();
      if (allocateStore()) {
        FILE *rfp=openInput();
        if (rfp!=NULL) {
          if (getTypes()) {
            time_t startTime=time(NULL);
            char outFile[outSize]; FILE *wfp=openOutput(outFile);
            if (wfp!=NULL) {
              noWriteError=copySquares(rfp, wfp);
              printf("Number of %ss: %d\n", R==C ? "square" : "rectangle", numSquares);
              if (noWriteError) ok=T; fclose(wfp); if (numSquares==0) remove(outFile);
            }
            outputElapsedTime(startTime);
          } // if (getTypes
          fclose(rfp);
        } // if (rfp!=NULL
      } // if (allocateStore
    } // if (validOrder
    if (noWriteError) {
      printf("\nAnother order? input y (yes), n (no) or the order: ");
      if (getYorInts(&R, &C)) inputOrder=(R<0); else another=F;
    } else { perror("\a\nError writing file"); another=F; }
  } while (another);
  freeStore(); printf("\nHit return to close the console.");
  while (T) if (getchar()=='\n') break; return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main