/*
 *  File:    BorderingSquares.cpp
 *  Author:  S Harry White
 *  Created: 2011-10-29
 *  Updated: 2020-05-03
 *    Added howMany.
 *  Updated: 2021-12-16
 *    Tidy code.
 *  Updated: 2023-01-05
 *    Change to compile with g++ for macOS.
 */

/*
 *  Makes bordering magic squares of even order n.
 */

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

const bool F=false, T=true; const int startSize=25, bufSize=1024;
int **xSquare=NULL, **bSquare=NULL,
    *top=NULL, *right=NULL, // Temporary for top row and right column, (without corners).
    allocatedSize, smallestValue;

void seed_rand() { srand((unsigned int)time(NULL)); }
//   ---------
int random_(const int x) { return rand()%x; }
//  -------
//============================================== store =================================================

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

void freeSquares() {
//   -----------
  freeSquare(&xSquare, allocatedSize); freeSquare(&bSquare, allocatedSize);
  free(top); free(right); top=NULL; right=NULL; allocatedSize=0;
} // freeSquares

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

bool allocateSquares(int size) {
//   ---------------
  bool ok=T; if (size<startSize) size=startSize;
  if (size>allocatedSize) {
    freeSquares();
    if ((ok=allocateSquare(&xSquare, size))) {
      if ((ok=allocateSquare(&bSquare, size))) {
        allocatedSize=size; top=(int*) malloc(size*sizeof(int));
        right=(int*) malloc(size*sizeof(int));
        ok=(top!=NULL)&(right!=NULL); if (!ok) freeSquares();
      } else {
       freeSquare(&xSquare, size); allocatedSize=0;
      }
    }
  }
  return ok;
} //allocateSquares
//============================================= input ===============================================

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

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

bool getYorOrder(int *n) { // 'y' or 'n' or the order
//   -----------
  bool result=F; int c; *n=0; do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n') );
  if ( (c=='Y')|(c=='y') ) result=T;
  else if ( (c!='N')&(c!='n') )
    if ( ('1'<=c)&(c<='9') ) {
      int i=c-'0'; while ( ('0'<=(c=getchar()))&&(c<='9') ) i=i*10+c-'0'; *n=i; result=T;
    }   
  clearLine(c); return result;
} // getYorOrder

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

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

FILE *open_File(const int n) {
//    ---------
  FILE *wfp=NULL; char buf[bufSize]; snprintf(buf, bufSize, "./Order%d.txt", n);
  if ((wfp=fopen(buf, "a"))==NULL) {
    char msg[bufSize+50]; snprintf(msg, bufSize+50, "\a\nCan't open for write %s", buf); perror(msg);
  }
  return wfp; ; 
} // open_File

int fieldWidth(const int n) {
//  ----------
  if (n==1) return 1; int i=n*n, width=1; while ((i=i/10)!=0) ++width; return width;
} // fieldWidth

typedef bool (*t_fprintFW)(FILE *fp, const int i);

bool fprintFW1(FILE *fp, const int i) { return fprintf(fp, "%1d",  i)>0; }
bool fprintFW2(FILE *fp, const int i) { return fprintf(fp, "%2d",  i)>0; }
bool fprintFW3(FILE *fp, const int i) { return fprintf(fp, "%3d",  i)>0; }
bool fprintFW4(FILE *fp, const int i) { return fprintf(fp, "%4d",  i)>0; }
bool fprintFW5(FILE *fp, const int i) { return fprintf(fp, "%5d",  i)>0; }
bool fprintFW6(FILE *fp, const int i) { return fprintf(fp, "%6d",  i)>0; }
bool fprintFW7(FILE *fp, const int i) { return fprintf(fp, "%7d",  i)>0; }
bool fprintFW8(FILE *fp, const int i) { return fprintf(fp, "%8d",  i)>0; }
bool fprintFW9(FILE *fp, const int i) { return fprintf(fp, "%9d",  i)>0; }
bool fprintFWa(FILE *fp, const int i) { return fprintf(fp, "%10d", i)>0; }

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

bool printSquare(FILE *wfp, const int n) {
//   -----------
  const int fw0=fieldWidth(n), fw=fw0+1; if (fw>maxFieldWidth) return F;
  for (int i=0; i<n; i++) {
    if (!fprintFW[fw0](wfp, xSquare[i][0])) return F;
    for (int j=1; j<n; j++) if (!fprintFW[fw](wfp, xSquare[i][j])) return F;
    if (fputc('\n', wfp)==EOF) return F;
  }
  return fputc('\n', wfp)!=EOF;
} // printSquare
//============================================= check ===============================================

bool isCorrect(int **x, const int n) {
//   ---------
  if (smallestValue!=0) return F;
  const int chkSum=(n*n+1)*(n/2); int sumX, sumY, sumXY=0, sumYX=0;

  for (int i=0; i<n; i++) {
    sumX=0; sumY=0; for (int j=0; j<n; j++) { sumX+=x[i][j]; sumY+=x[j][i]; }
    if ((sumX!=chkSum)|(sumY!=chkSum)) return F; sumXY+=x[i][n-i-1]; sumYX+=x[i][i];
  }
  return ((sumXY==chkSum)&(sumYX==chkSum));
} // isCorrect
//============================================== make ===============================================

typedef void (*t_Turn)(const int, const int, const int);
void no_Turn   (const int n, const int i, const int j) { xSquare[i][j]    =bSquare[i][j]; };
void rotate_90 (const int n, const int i, const int j) { xSquare[j][n-i]  =bSquare[i][j]; };
void rotate_180(const int n, const int i, const int j) { xSquare[n-i][n-j]=bSquare[i][j]; };
void rotate_270(const int n, const int i, const int j) { xSquare[n-j][i]  =bSquare[i][j]; };
void rotate_Y  (const int n, const int i, const int j) { xSquare[i][n-j]  =bSquare[i][j]; };
void rotate_XY (const int n, const int i, const int j) { xSquare[n-j][n-i]=bSquare[i][j]; };
void rotate_X  (const int n, const int i, const int j) { xSquare[n-i][j]  =bSquare[i][j]; };
void rotate_YX (const int n, const int i, const int j) { xSquare[j][i]    =bSquare[i][j]; };
static t_Turn turnCell[]={ no_Turn,  rotate_90, rotate_180, rotate_270,
                           rotate_Y, rotate_XY, rotate_X,   rotate_YX };
const int numRotations=8;
void Turn(const int o, const int n) {
//   ----
  const int nmo=n-o, npo=n+o, k=random_(numRotations);
  if ((n-o==0)|(n-o==3)) { // turn all 1x1, 4x4
    for (int i=o; i<=n; i++) for (int j=o; j<=n; j++) turnCell[k](npo, i, j);
  } else { // turn only border rows and columns  
    for (int i=o; i<=n; i+=nmo) for (int j=o; j<=n; j++) turnCell[k](npo, i, j);
    for (int j=o; j<=n; j+=nmo) for (int i=o+1; i<n; i++) turnCell[k](npo, i, j);
  }
} // Turn

void makeActual(int **x, const int n) {
//   ----------
  const int nn=n*n, pPlus=nn/2, mPlus=pPlus+1;
  for (int i=0; i<n; i++) for (int j=0; j<n; j++) x[i][j]+=x[i][j]>0 ? pPlus : mPlus;
} // makeActual

const int num4x4=712;
const int code4x4[]={
0x02f7e1ca,0x02fbe1c6,0x02fce17a,0x02fce1b6,0x03c7d2eb,0x03c7e1db,0x03cbd2e7,0x03cbe1d7,
0x03cde17b,0x03cde1b7,0x03ced27b,0x03ced2b7,0x03dcf17a,0x03dcf1b6,0x03dfc26b,0x03dfc2a7,
0x03ecf279,0x03ecf2b5,0x03efc15b,0x03efc197,0x03fde269,0x03fde2a5,0x03fed15a,0x03fed196,
0x04f7d29c,0x04f9d27c,0x04f9d2e5,0x04fed295,0x05a6e1dc,0x05a7b4ed,0x05a7e1bd,0x05abe17d,
0x05abe1d7,0x05adb4e7,0x05ade16c,0x05ade1b7,0x05aeb47d,0x05aeb4d7,0x05baf17c,0x05baf1d6,
0x05bfa46d,0x05bfa4c7,0x05eaf479,0x05eaf4d3,0x05efa13d,0x05efa197,0x05fbe469,0x05fbe4c3,
0x05feb13c,0x05feb196,0x0697b4de,0x0697d2be,0x069bd27e,0x069bd2e7,0x069db47e,0x069db4e7,
0x069eb4d7,0x069ed2b7,0x06b9f27c,0x06b9f2e5,0x06bf945e,0x06bf94c7,0x06d7c18e,0x06d8e37c,
0x06d8e3f4,0x06d9f47a,0x06d9f4e3,0x06dac15e,0x06df923e,0x06df92a7,0x06fac3d4,0x06fbd45a,
0x06fbd4c3,0x06fdb23c,0x06fdb2a5,0x06fdc3a4,0x078dc36e,0x079ad3f6,0x079bc26f,0x079cb5f6,
0x079da46f,0x079fc24d,0x07a9e3f5,0x07abc15f,0x07acb6f5,0x07ae945f,0x07b9e26d,0x07bad15e,
0x07bda6e5,0x07be95d6,0x07c9e5f3,0x07cad6f3,0x07cda13f,0x07ce923f,0x07cfa12e,0x07d9e46b,
0x07dbc6e3,0x07dcb13e,0x07de93b6,0x07ead45b,0x07ebc5d3,0x07ecb23d,0x07eda3b5,0x087de16c,
0x096b78ed,0x096be17d,0x096d78eb,0x096de17b,0x096e78bd,0x096e78db,0x097f68ad,0x097f68cb,
0x097fc24d,0x09ef524d,0x09ef613d,0x09ef615b,0x09fe713c,0x09fe715a,0x0a5b78de,0x0a5bd27e,
0x0a5d78be,0x0a5d78eb,0x0a5dc36e,0x0a5e78db,0x0a5ed27b,0x0a7f589e,0x0a7f58cb,0x0adf436c,
0x0adf523e,0x0adf526b,0x0afd723c,0x0afd7269,0x0b5c79fa,0x0b5d68af,0x0b6c7af9,0x0b6e589f,
0x0b7d6ae9,0x0b7e59da,0x0bcd613f,0x0bce523f,0x0bcf612e,0x0bdc713e,0x0bde537a,0x0bec723d,
0x0bed6379,0x0c3d78be,0x0c3db47e,0x0c3e78bd,0x0c3eb47d,0x0c7f389e,0x0c7f38ad,0x0c7fa12e,
0x0cbf345e,0x0cbf346d,0x0cbf612e,0x0d6e389f,0x0d6f498e,0x0d7e39bc,0x0dae345f,0x0daf436c,
0x0dbe357c,0x0e9f524d,0x12cde06b,0x12cde0a7,0x12ced37a,0x12ced3b6,0x12d6c3fa,0x12d6f0ca,
0x12d7f0ab,0x12dac3f6,0x12daf07b,0x12daf0c6,0x12dcf06a,0x12dcf0a6,0x12dfc36a,0x12dfc3a6,
0x12ecf378,0x12ecf3b4,0x12efc04b,0x12efc087,0x12fde368,0x12fde3a4,0x12fed04a,0x12fed086,
0x13cdf269,0x13cdf2a5,0x13cfd04b,0x13cfd087,0x13e4d2f9,0x13e8d2f5,0x13efd249,0x13efd285,
0x14abe06d,0x14abe0c7,0x14aeb57c,0x14aeb5d6,0x14b6a5fc,0x14b6f0ac,0x14baf06c,0x14baf0c6,
0x14bca5f6,0x14bcf0a6,0x14bdf2a5,0x14bfa56c,0x14bfa5c6,0x14bfd087,0x14eaf578,0x14eaf5d2,
0x14efa02d,0x14efa087,0x14f7b08d,0x14fbe568,0x14fbe5c2,0x14fcb03d,0x14fe923d,0x14feb02c,
0x14feb086,0x15e6c38d,0x15e8c36d,0x15e8c3f4,0x15efc384,0x168ad37e,0x168bc2e7,0x168cb57e,
0x168da4e7,0x168ed35c,0x1697b4af,0x169ab47f,0x16a7c08f,0x16a8f37c,0x16abc04f,0x16acb7f4,
0x16af84c7,0x16b8f2e4,0x16bad04e,0x16bda7e4,0x16bf854e,0x16c7a08f,0x16c8f57a,0x16cad7f2,
0x16cda02f,0x16cf82a7,0x16d7928f,0x16d8f4e2,0x16dbc7e2,0x16dc923f,0x16dcb02e,0x16df832e,
0x16fad4c2,0x16fbc54a,0x16fcb2a4,0x16fda32c,0x178ac36f,0x178ac3f6,0x178ca56f,0x178ca5f6,
0x178fa5c6,0x178fc3a6,0x17a8e36d,0x17a8e3f4,0x17a9f46b,0x17a9f4e3,0x17adb02f,0x17ae854f,
0x17ae85d6,0x17c8e56b,0x17c8e5f2,0x17ce832f,0x17ce83b6,0x17eab4d3,0x17eac54b,0x17eac5d2,
0x17eca32d,0x17eca3b4,0x17edb4a3,0x186e79bc,0x186e79da,0x186ed35c,0x187a69fc,0x187af06c,
0x187c69fa,0x187cf06a,0x187df269,0x187f69ac,0x187f69ca,0x187fd04b,0x18ef602d,0x18ef604b,
0x18fb704d,0x18fc703d,0x18fe435c,0x18fe523d,0x18fe702c,0x18fe704a,0x196af07b,0x1a4c79be,
0x1a4d68eb,0x1a6bc04f,0x1a6c7bf8,0x1a6f48cb,0x1a7d6be8,0x1a7db02f,0x1a7e389f,0x1a7f498e,
0x1acb604f,0x1acd602f,0x1acf426b,0x1adb524f,0x1adc523f,0x1adc702e,0x1ade347b,0x1adf432e,
0x1afc7268,0x1afd632c,0x1b4c69af,0x1b4c69fa,0x1b4f69ca,0x1b4fc36a,0x1b6e498f,0x1b6e49da,
0x1bce432f,0x1bce437a,0x1bec632d,0x1bec6378,0x1c6da02f,0x1c6f28ad,0x1c7f298e,0x1cad602f,
0x1caf246d,0x1cbf254e,0x1d2f69ac,0x1d2fa56c,0x1d6e298f,0x1d6e29bc,0x1dae254f,0x1dae257c,
0x1dae347b,0x1e3f5a8d,0x1e3f964d,0x1e5f4b8c,0x20d7e1ca,0x20dbe1c6,0x20dce17a,0x20dce1b6,
0x20fce378,0x20fce3b4,0x20fec15a,0x20fec196,0x21cde379,0x21cde3b5,0x21ced05b,0x21ced097,
0x21dcf378,0x21dcf3b4,0x21dfc04b,0x21dfc087,0x21e5c3f9,0x21e5f0c9,0x21e9c3f5,0x21e9f0c5,
0x21ebf4c3,0x21ecf059,0x21ecf095,0x21efb087,0x21efc359,0x21efc395,0x21f6b5da,0x21f7e08b,
0x21f9e06b,0x21fda46b,0x21fde049,0x21fde085,0x21fed358,0x21fed394,0x249bd0c7,0x249db67c,
0x24b596fc,0x24b5a7ec,0x24b785ce,0x24b9f0c5,0x24bcf095,0x24bf965c,0x24d9f678,0x24d9f6e1,
0x24df901e,0x24df9087,0x24fbd658,0x24fbd6c1,0x24fdb01c,0x24fdb085,0x258bc1d7,0x258cb67d,
0x25b7a48f,0x25b8f1d4,0x25b9a46f,0x25bf864d,0x25c7908f,0x25c8f679,0x25c9e7f1,0x25ce901f,
0x25cf8197,0x25e8f4d1,0x25ebc7d1,0x25ecb01d,0x25ef831d,0x25efa10c,0x25f9e4c1,0x25fbc649,
0x25fcb194,0x25fe931c,0x269bf45a,0x269bf4c3,0x269fb01e,0x269fb087,0x26d8b4f3,0x26dfb41a,
0x26dfb483,0x2789c3f5,0x278c965f,0x278ce3b4,0x278ec196,0x278fc395,0x2798d3f4,0x279d864f,
0x27c8d65b,0x27c8d6f1,0x27cd831f,0x27cd83b5,0x27cda10e,0x27cf830e,0x27d9c64b,0x27d9c6e1,
0x27dc931e,0x27dc93b4,0x285d7abc,0x28795afc,0x28796bec,0x287b49ce,0x287bf45a,0x287cf059,
0x287f5a9c,0x287fb01e,0x28df501e,0x28df504b,0x28f9706b,0x28fd346b,0x28fd701c,0x28fd7049,
0x28fe701b,0x294c7abd,0x296c38bf,0x297f4a8d,0x29cb504f,0x29ce501f,0x29cf415b,0x29ec701d,
0x29ef431d,0x29ef610c,0x29fc7158,0x29fe531c,0x2b4c5a9f,0x2b4ce378,0x2b4ec15a,0x2b4fc359,
0x2b5d4a8f,0x2bcd431f,0x2bcd4379,0x2bcd610e,0x2bcf430e,0x2bdc531e,0x2bdc5378,0x2c5e901f,
0x2c5f189e,0x2c7da10e,0x2c7f1a8d,0x2c9e501f,0x2c9f145e,0x2cbd610e,0x2cbe341f,0x2cbf164d,
0x2d4e189f,0x2d6f3c8b,0x2d6fa51b,0x2d7e1b9c,0x2d8e145f,0x2dbe175c,0x2e1f5a9c,0x2e1f965c,
0x30cde269,0x30cde2a5,0x30ced15a,0x30ced196,0x30dcf268,0x30dcf2a4,0x30dfc14a,0x30dfc186,
0x30e7a4cb,0x30ecb57a,0x30ecf158,0x30ecf194,0x30efc249,0x30efc285,0x30f4d2e8,0x30f4e1d8,
0x30f5b4e9,0x30f8d2e4,0x30f8e1d4,0x30fae5d2,0x30fde148,0x30fde184,0x30fea196,0x30feb459,
0x30fed248,0x30fed284,0x31c4d2f9,0x31c8d2f5,0x31cfd249,0x31cfd285,0x32d596eb,0x32d9f6e1,
0x32de965b,0x32df9087,0x349ad0c6,0x349da76c,0x34a9e0c5,0x34ae975c,0x34d6819e,0x34d8f6e0,
0x34d9e768,0x34de9086,0x34df810e,0x34e582ad,0x34e8f5d0,0x34ead758,0x34eda085,0x34ef820d,
0x34f9e5c0,0x34fad6c0,0x34fc921d,0x34fda10c,0x34fe920c,0x358ac1d6,0x358ca76d,0x358db0a7,
0x35a8e1d4,0x35a9f6e1,0x35ade184,0x35ae874d,0x35af9087,0x35c8e769,0x35c8e7f0,0x35ce810f,
0x35ce8196,0x35eac749,0x35eac7d0,0x35eca10d,0x35eca194,0x35edb629,0x35edb6a1,0x35ef940b,
0x3689c2e5,0x368c975e,0x3698d2e4,0x369d874e,0x369ed284,0x36c8d75a,0x36c8d7f0,0x36cd820f,
0x36cd82a5,0x36d9c74a,0x36d9c7e0,0x36dc920e,0x36dc92a4,0x378ae5d2,0x378ea196,0x37c9a5e2,
0x37cea50b,0x37cea592,0x385a7cdb,0x385d6bac,0x386e5b9c,0x38796dea,0x387d29ae,0x38da415e,
0x38de504a,0x38df410e,0x38e9426d,0x38ed6049,0x38ef420d,0x38fa7059,0x38fc521d,0x38fd610c,
0x38fd7029,0x38fe520c,0x394c6bad,0x396de148,0x396e4b8d,0x39ce410f,0x39ce415a,0x39ec257a,
0x39ec610d,0x39ec6158,0x39ef610a,0x3a4c5b9e,0x3a5d4b8e,0x3a5e189f,0x3a5ed248,0x3acd420f,
0x3acd4269,0x3adc520e,0x3adc5268,0x3ade165b,0x3c1f5a8d,0x3c1f964d,0x3c5f098e,0x3c6f0a8d,
0x3c7e2d9a,0x3c7eb40a,0x3c9f054e,0x3caf064d,0x3d2e189f,0x3d2eb459,0x3d4e098f,0x3d4f810e,
0x3d6e0b9c,0x3d8e054f,0x3d8f410e,0x3dae075c,0x3daf250e,0x3e4f820d,0x3e5f940b,0x3e8f420d,
0x40f9d678,0x40fd923c,0x41e2c3f8,0x41e3a5f9,0x41e3c7d9,0x41e7839d,0x41e9f0a3,0x41eaf093,
0x41efa539,0x41efc328,0x41fbe083,0x41feb538,0x42d396fa,0x42d9f0a3,0x42daf093,0x42df963a,
0x42f583ac,0x42fac758,0x42fbd083,0x42fdb638,0x42fe831c,0x43d8f1b2,0x43df862b,0x43dfc209,
0x43e8f2b1,0x43ef851b,0x43f9e2a1,0x43fad192,0x43fda629,0x43fe951a,0x45af872c,0x479f860d,
0x48bf301e,0x48bf302d,0x497ed318,0x49ad302f,0x49ae301f,0x49af213d,0x4a5fc328,0x4a9d302f,
0x4a9e301f,0x4a9f123e,0x4b8d213f,0x4b8e123f,0x4d2fa539,0x4d3e971c,0x4e1f872c,0x4e1f963a,
0x50eaf192,0x50efa429,0x50f2b4e8,0x50f8e1b2,0x50fbe182,0x50feb428,0x52c9e0a3,0x52ce973a,
0x52e384cb,0x52e8f3b0,0x52ebc083,0x52ecb738,0x52ef840b,0x52f9e3a0,0x52fe940a,0x53c8e1b2,
0x53cbe182,0x53ce872b,0x53eac192,0x53eca729,0x58bc213e,0x58be302c,0x58bf210e,0x59ae210f,
0x59ae213c,0x5a9c302e,0x5a9f032e,0x5b8c213e,0x5b8e032f,0x5b8f210e,0x5c3eb428,0x5e2f840b
};

void putCenter4x4(int **x, int **b, const int o, const int n, const int N) {
//   ------------
  int code=code4x4[random_(num4x4)]; const int NN=N*N, adj=NN-(4*4),
  half4x4=8, Msum4=34, /* 4*(4*4+1)/2; */ mMinus=NN/2+1, pMinus=mMinus-adj-1;

  x[2][2]=(code&0xf)+1; code>>=4; x[2][0]=(code&0xf)+1; code>>=4;
  x[1][2]=(code&0xf)+1; code>>=4; x[1][1]=(code&0xf)+1; code>>=4;
  x[1][0]=(code&0xf)+1; code>>=4; x[0][2]=(code&0xf)+1; code>>=4;
  x[0][1]=(code&0xf)+1; code>>=4; x[0][0]=(code&0xf)+1;

  x[0][3]=Msum4-x[0][0]-x[0][1]-x[0][2]; x[1][3]=Msum4-x[1][0]-x[1][1]-x[1][2];
  x[3][0]=Msum4-x[0][0]-x[1][0]-x[2][0]; x[3][2]=Msum4-x[0][2]-x[1][2]-x[2][2];
  x[3][3]=Msum4-x[0][0]-x[1][1]-x[2][2]; x[3][1]=Msum4-x[3][0]-x[3][2]-x[3][3];
  x[2][1]=Msum4-x[0][1]-x[1][1]-x[3][1]; x[2][3]=Msum4-x[2][0]-x[2][1]-x[2][2];

  for (int i=0; i<4; i++) for (int j=0; j<4; j++)
    b[i+o][j+o]=x[i][j]-(x[i][j]>half4x4 ? pMinus : mMinus);
  Turn(o, n);
} // putCenter4x4

void putBorder(int **b, const int o, const int n) { // random_ choices from top and right
//   ---------
  int length=n-o-1; // remaining choices in top, right 
  for (int i=o+1; i<(n-1); ++i) {
    int k=random_(length); b[o][i]=top[k]; b[n][i]=-top[k];
    for (int j=k; j<length; ++j) top[j]=top[j+1];
    k=random_(length); b[i][o]=right[k]; b[i][n]=-right[k];
    for (int j=k; j<length; ++j) right[j]=right[j+1]; --length;
  }
  b[o][n-1]=top[0]; b[n][n-1]=-top[0]; b[n-1][o]=right[0]; b[n-1][n]=-right[0]; Turn(o, n);
} // putBorder

bool makeEven(int **b, const int size, FILE *wfp) {
//   --------
  int v=(size*size)/2-8; // 8.5
  // fill center 4x4
  int o=(size-4)/2, n=o+3; putCenter4x4(xSquare, b, o, n, size);  
  for (int i=6; i<=size; i+=2) {
    int c=0, r=0; o=(size-i)/2; n=o+i-1;  
    if (i%4!=0) { //------------------------------ // singly even
      const int k=(i-2)/4; int j=k; while (j--) { top[c++]=-(v--); top[c++]=v--; }
      top[c++]=-(v--); right[r++]=-(v--);
      j=k-1; while (j--) { right[r++]=v--; right[r++]=-(v--); }
      b[n][n]=-v; b[o][o]=v--; /* NW */ b[n][o]=-v; b[o][n]=v--; /* NE */ right[r++]=v--;
      j=k-1; while (j--) { right[r++]=-(v--); right[r++]=v--; } right[r++]=v--; top[c++]=-(v--);
      j=k-1; while (j--) { top[c++]=v--; top[c++]=-(v--); } right[r++]=-(v--);
    } else { //------------------------------------- // doubly even
      top[c++]=-(v--); const int k=(i-4)/4; int j=k; while (j--) { top[c++]=v--; top[c++]=-(v--); }
      if (i==8) right[r++]=v--;
      else { right[r++]=-(v--); right[r++]=v--;
        j=k-2; while (j--) { right[r++]=-(v--); right[r++]=v--; } right[r++]=v--;
      } right[r++]=-(v--); right[r++]=-(v--);
      b[n][n]=-v; b[o][o]=v--; /* NW */  b[n][o]=-v; b[o][n]=v--; /* NE */
      right[r++]=v--; right[r++]=v--; j=k-1; while (j--) { right[r++]=-(v--); right[r++]=v--; }
      right[r++]=-(v--); j=k; while (j--) { top[c++]=-(v--); top[c++]=v--; } top[c++]=-(v--);
    } // if (i%4!=0)
    putBorder(b, o, n);
  } // for (i=6; ...
  smallestValue=v; makeActual(xSquare, size); return !printSquare(wfp, size);
}  // makeEven
//============================================== main ============================================

bool validOrder(const int n) {
//   ----------
  if (n<=0) {
      printf("\aERROR: N must be a positive integer.\n"); return F;
  } else if (n&1) {
    printf("\aERROR: There are no bordering magic squares of odd order.\n"); return F;
  } else if (n==2) {
    printf("\aERROR: There is no magic square of order 2.\n"); return F;
  } else if (n==4)
    printf("There is no bordering magic square of order 4.\n"
           "Making a normal magic square.\n");
  return T;
}// validOrder
//--------------------------------------------------------------------------

int main() {
//  ----
  bool another=T, inputSize=T, writeError=F, ok=F; seed_rand(); 
  if (openDir()) {
    int n=0;
    do {
      if (inputSize) { printf("\nInput the order of the square: "); n=getSize(); }
      if (validOrder(n)) {
	if (allocateSquares(n)) {
	  FILE *wfp=NULL;
          if ( (wfp=open_File(n))!=NULL) {
            printf("\nHow many? "); int howMany=getSize();
            while (howMany--) {
              writeError=makeEven(bSquare, n, wfp); if (writeError) break;
              if (!isCorrect(xSquare, n)) {
                printf("\a\nERROR. NOT a Magic Square. Please report by email\n"); break;
              }
            }
            fclose(wfp);
	  }
        } else  printf("\a\nERROR: Storage allocation failed.\n");
      }
      if (writeError) { perror("\a\nError writing file"); another=F; }
      else { ok=T; printf("\nMake another square? "
        "input y (yes) or n (no) or the order of the square: ");
        if (getYorOrder(&n)) inputSize=(n==0); else another=F; 
      }
    } while (another);
  }
  freeSquares(); printf("\nHit return to close the console.");
  while (T) if (getchar()=='\n') break; return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main