/*
 *  File:    WaterRetentionWithComplement.cpp
 *  Author:  S Harry White
 *  Created: 2014-06-05
 *  Updated: 2022-02-06
 *    Tidy code.
 * Updated: 2023-02-22
 *   Improve openInput and openOutput.
 * Updated: 2023-03-28
 *   Fix store allocation. 
 *   Remove allocatedSquaresSize=0 and allocatedQueueSize=0 from initGlobals().
 */

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

const bool F=false, T=true; bool *numberUsed=NULL;
const int maxN=200, startSquaresSize=25;
int N,             // The order of the square.
    M,             // N-1
    NN,            // N*N
    maxNumber,     // Biggest number in the square.
    complementSum, // smallest+biggest
    squareNumber, **xSquare=NULL, **bSquare=NULL, allocatedSquaresSize, allocatedQueueSize, qIndex;
typedef unsigned int Uint;
Uint MagicConstant, numAssoc, numSelf, numOther, numAssocMax, numSelfMax,
     numPairMax, maxWaterAssoc, maxWaterSelf, maxWaterPair;
struct t_Cell { int priority, row, col; }; t_Cell *Queue=NULL;

void initGlobals() {
//   -----------
  M=N-1; NN=N*N; maxNumber=0; complementSum=0; squareNumber=0;
  qIndex=0; MagicConstant=(N&1) ? (NN+1)/2*N : N/2*(NN+1); numAssoc=0; numSelf=0; numOther=0;
  numAssocMax=0; numSelfMax=0; numPairMax=0; maxWaterAssoc=0; maxWaterSelf=0; maxWaterPair=0;
} // initGlobals
//=================================================== store =======================================================

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

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 freeUsed() { if (numberUsed!=NULL) { free(numberUsed); numberUsed=NULL; }}
//   --------

void freeQueue() { if (Queue!=NULL) { free(Queue); Queue=NULL; allocatedQueueSize=0; }}
//   ---------

void freeStore() {
//   ---------
  freeSquare(&bSquare, allocatedSquaresSize); freeSquare(&xSquare, allocatedSquaresSize);
  allocatedSquaresSize=0; freeUsed(); freeQueue();
} // freeStore

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

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

bool allocateQueue(const int size) {
//   -------------
  bool ok=T;
  if (allocatedQueueSize<size) { freeQueue();
    Queue=(t_Cell *) malloc(size*sizeof(t_Cell)); if (ok=(Queue!=NULL)) allocatedQueueSize=size;
  }
  return ok;
} // allocateQueue

bool increaseQueue() {
//   -------------
  const int size=allocatedQueueSize+allocatedQueueSize; t_Cell *tmp=(t_Cell *) malloc(size*sizeof(t_Cell));
  if (tmp==NULL) { reportError(storeAllocFail); return F; } for (int i=0; i<qIndex; i++) tmp[i]=Queue[i];
  freeQueue(); Queue=tmp; allocatedQueueSize=size; return T;
} // increaseQueue;

bool allocateStore(int size) {
//   -------------
  bool ok=T; if (size<startSquaresSize) size=startSquaresSize;
  if (size>allocatedSquaresSize) { freeStore(); if (ok=allocateSquare(&xSquare, size))
	  if (ok=allocateSquare(&bSquare, size)) if (ok=allocateQueue(size*size))
		  if (ok=allocateUsed(size*size+1)) allocatedSquaresSize=size;
    if (!ok) { reportError(storeAllocFail); freeStore(); }
  }
  return ok;
} // allocateStore
//========================================================== input ======================================================

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

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

bool 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_s("%d", &n); clearLine(getchar()); return n; }
//  -------

bool getFileName(char *buf, const int size) {
//   -----------
  int c, i=0; char *s=buf; do { c=getchar(); } while ((c==' ')|(c=='\t')|(c=='\n') ); *s=c;
  while (i++<size) if ( (*++s=getchar())=='\n') break;
  if (*s!='\n') { printf("\nFile name too long.\n"); clearLine(*s); return F; } *s='\0'; return T;
} // getFileName

const int bufSize=1024, outSize=bufSize+50;
void check_txt(char *buf) {
//   ---------
  char *s=buf; bool txt=F; while (*s++!='\0');
  while (--s!=buf) if (*s=='.') { txt=(*++s=='t')&&(*++s=='x')&&(*++s=='t')&&(*++s=='\0'); break; }
  if (!txt) strcat_s(buf, bufSize, ".txt");
} // check_txt

FILE *openInput(char buf[bufSize]) {
//    ---------
  char *rFname=NULL; FILE *rfp=NULL;
  do {
    printf("\nEnter the name of the squares file: ");
    if (getFileName(buf, bufSize-4)) { rFname=buf; break; }
    printf("\a\nCan't read the file name. Try again? y (yes) or n (no) "); if (!getY()) break;
  } while (T);
  if (rFname!=NULL) {
    check_txt(buf);
    if (fopen_s(&rfp, buf, "r")!=0) {
      const int msgSize=bufSize+50; char msg[msgSize];
      sprintf_s(msg, msgSize, "\a\nCan't open for read %s", buf); perror(msg);
    }
  }
  return rfp;
} // openInput

enum magicType {notMagic, notNormal, Normal}; magicType squareType;
magicType isMagic(const bool checkZeroBased) {
//        -------
  const int min=checkZeroBased ? 0 : 1, max=NN+min-1; Uint sumX, sumY, sumXY=0, sumYX=0;
  const Uint magicSum=checkZeroBased ? MagicConstant-N : MagicConstant; Uint chkSum=0;
  for (int i=min; i<=max; i++) numberUsed[i]=F;
  for (int i=0; i<N; i++) { sumX=0; sumY=0;
    for (int j=0; j<N; j++) { const int tmp=xSquare[i][j];
	  if ((min<=tmp)&(tmp<=max)) numberUsed[tmp]=T; sumX+=tmp; sumY+=xSquare[j][i]; }
	if (i==0) chkSum=sumX;
    if ((sumX!=chkSum)|(sumY!=chkSum)) return notMagic; sumXY+=xSquare[i][M-i]; sumYX+=xSquare[i][i];
  }
  if ((sumXY!=chkSum)|(sumYX!=chkSum)) return notMagic; if (chkSum!=magicSum) return notNormal;
  for (int i=min; i<=max; i++) if (!numberUsed[i]) return notNormal; return Normal;
} // isMagic

const int maxFieldWidth=10; int fieldWidth;
int getWidth(int i) {
//  --------
  const bool isSigned=i<0; int width=1; while ((i=i/10)!=0) ++width;
  if (isSigned) ++width; if (width>9) width=9; return width;
} // getWidth

bool readError;
bool readSquare(FILE *rfp) {
//   ----------
  int smallest=LONG_MAX, biggest=LONG_MIN;
  for (int r=0; r<N; r++) for (int c=0; c<N; c++) {
	   int rv, tmp;
     if ( (rv=fscanf_s(rfp, "%d", &tmp))==1) {
      if (tmp<smallest) smallest=tmp; if (tmp>biggest) biggest=tmp; xSquare[r][c]=tmp;
    } else {
      if ( (rv!=EOF)|(r!=0)|(c!=0) ) { readError=T; printf("\a\nError reading square from file.\n"); }
      return F;
    }
  }
  ++squareNumber; squareType=isMagic(smallest==0);
  if (squareType==notMagic) { printf("\nSquare number %d is not a magic square.\n", squareNumber); }
  else if (squareType==notNormal) { printf("\nSquare number %d is not a normal magic square.\n", squareNumber); }
  maxNumber=biggest; complementSum=smallest+biggest; const int sWidth=getWidth(smallest), bWidth=getWidth(biggest);
  fieldWidth=sWidth>bWidth ? sWidth : bWidth; return T;
} // readSquare
//======================================================== output =================================================

enum fnKind { assoc, self, pair, summary }; char* kindName[4]={ "Assoc", "Self", "SqAndComp", "Summary" };
char maxAssocFN[outSize], maxSelfFN[outSize], maxPairFN[outSize];
bool openDir(char *dir) {
//   -------
  int sub=0; char baseName[bufSize];
  sprintf_s(baseName, bufSize, "WRwithComplement%d", N); strcpy_s(dir, bufSize, baseName);
  do {
    if (_mkdir(dir)!=-1) break;
    if (errno!=EEXIST) { sprintf_s(baseName, bufSize, "\a\nCan't make folder %s", dir); perror(baseName); return F; }
    sprintf_s(dir, bufSize, "%s_%d", baseName, ++sub);
  } while (T);
  printf("Output file(s) are in folder %s\n", dir); return T;
} // openDir

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

FILE *reOpen(char *dir, FILE **wfp, char *fn, fnKind kind) {
//    ------
  fclose(*wfp); *wfp=NULL; if (remove(fn)!=0) return NULL; return openOutput(dir, kind);
} // reOpen

bool openFiles(char *dir, FILE **wfp, FILE **wfpA, FILE **wfpS, FILE **wfpP) {
//   ---------
  *wfp=*wfpA=*wfpS=*wfpP=NULL; if ((*wfp =openOutput(dir, summary))==NULL) return F;
  if ((*wfpA=openOutput(dir, assoc))==NULL) { fclose(*wfp); *wfp=NULL; return F; }
  if ((*wfpS=openOutput(dir, self))==NULL) { fclose(*wfp); *wfp =NULL; fclose(*wfpA); *wfpA=NULL; return F; }
  if ((*wfpP=openOutput(dir, pair))==NULL) { fclose(*wfp); *wfp =NULL; fclose(*wfpA); *wfpA=NULL;
    fclose(*wfpS); *wfpS=NULL; return F;
  }
  return T;
} // openFiles

void closeFiles(FILE **wfp, FILE **wfpA, FILE **wfpS, FILE **wfpP) {
//   ----------
  fclose(*wfp); fclose(*wfpA); fclose(*wfpS); fclose(*wfpP);
  if (numAssoc==0) remove(maxAssocFN); if (numSelf==0) remove(maxSelfFN); if (numOther==0) remove(maxPairFN);
  *wfp=*wfpA=*wfpS=*wfpP=NULL;
} // closeFiles

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
};

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

bool writeSummary(FILE *wfp) {
//   ------------
  if (fputc('\n', wfp)==EOF) return F; char *fmt;
  if (numAssoc>0) {
    fmt="\nNumber of associative self-complement squares: %u\n"
          "  Max retention: %u x 2=%u, number of squares: %u\n";
    printf(fmt, numAssoc, maxWaterAssoc, maxWaterAssoc+maxWaterAssoc, numAssocMax);
    if (fprintf(wfp, fmt, numAssoc, maxWaterAssoc, maxWaterAssoc+maxWaterAssoc, numAssocMax)<=0) return F;
  }
  if (numSelf>0) {
    fmt="\nNumber of side-to-side symmetric self-complement squares: %u\n"
          "  Max retention: %u x 2=%u, number of squares: %u\n";
    printf(fmt, numSelf, maxWaterSelf, maxWaterSelf+maxWaterSelf, numSelfMax);
    if (fprintf(wfp, fmt, numSelf, maxWaterSelf, maxWaterSelf+maxWaterSelf, numSelfMax)<=0) return F;
  }
  if (numOther>0) {
    fmt="\nNumber of %ssquares: %u\n"
      "  Max retention square and complement: %u, number of squares: %u\n";
    printf(fmt, (numAssoc+numSelf)==0 ? "" :"other ", numOther, maxWaterPair, numPairMax);
    if (fprintf(wfp, fmt, (numAssoc+numSelf)==0 ? "" : "other ", numOther, maxWaterPair, numPairMax)<=0) return F;
  }
  return T;
} // writeSummary

//FILE *Qfp=NULL;
//void printQueue() {
////   ----------
//  int i=0;
//  while (i<qIndex) {
//    	t_Cell *q=&Queue[i]; fprintf(Qfp,"priority %d row %d col %d\n", q->priority, q->row, q->col); i++; }
//  fprintf(Qfp,"\n");
//} // printQueue
//=================================================== type =======================================================

bool isAssociative(int **x, const int n) {
//   -------------
  if ((squareType!=Normal)|((n&3)==2)) return F;
  const bool odd=((n&1)==1); const int m=n-1, mid=n/2, S2=complementSum;
  for (int r=0; r<mid; ++r) for (int c=0; c<n; ++c) { if (x[r][c]+x[m-r][m-c]!=S2) return F; }
  if (odd) for (int c=0; c<mid; ++c) if (x[mid][c]+x[mid][m-c]!=S2) return F; return T;
} // isAssociative

bool isSelfComplement(int **x, const int n, bool *assoc) {
//   ----------------
  if (squareType!=Normal) return *assoc=F; if (*assoc=isAssociative(x, n)) return T;
  if (n&1) return F; /* only even */ const int m=n-1, mid=n/2, S2=complementSum;
  if ((x[0][0]+x[0][m])==S2) {
    for (int r=0; r<n; ++r) for (int c=0; c<mid; ++c) if ((x[r][c]+x[r][m-c])!=S2) return F;
  } else if ((x[0][0]+x[m][0])==S2) {
    for (int r=0; r<mid; ++r) for (int c=0; c<n; ++c) if ((x[r][c]+x[m-r][c])!=S2) return F;
  } else return F;
  return T;
} // isSelfComplement
//================================================ get water =====================================================

void complementSquare(int **x, const int n) {
//   ----------------
  const int S2=complementSum;
  for (int r=0; r<n; ++r) for (int c=0; c<n; ++c) { const int tmp=x[r][c]; x[r][c]=S2-tmp; }
} // complementSquare

void initBounds() {
//   ----------
  for (int r=0; r<N; r++) { bSquare[r][0]=xSquare[r][0]; bSquare[r][M]=xSquare[r][M]; }
  for (int c=1; c<M; c++) {	bSquare[0][c]=xSquare[0][c]; bSquare[M][c]=xSquare[M][c]; }
  for (int r=1; r<M; r++) for (int c=1; c<M; c++) bSquare[r][c]=maxNumber; qIndex=0;
} // initBounds

void pushQueue(const int priority, const int row, const int col) {
//   ---------
  t_Cell cell={ priority, row, col}; Queue[qIndex++]=cell;
} //  pushQueue

bool insertQueue(const int priority, const int row, const int col) {
//   -----------
  if ((qIndex>=allocatedQueueSize)&&!increaseQueue()) return F;
  t_Cell cell={ priority, row, col}; int i=qIndex-1;
  while (Queue[i].priority<priority) { Queue[i+1]=Queue[i]; if (--i<0) break; }
  Queue[++i]=cell; ++qIndex; /* printQueue(); */ return T;
} // insertQueue

void popQueue(t_Cell *cell) { *cell=Queue[--qIndex]; }
//   --------

bool queueBorder() {
//   -----------
  pushQueue(bSquare[1][0], 1, 0); if (!insertQueue(bSquare[1][M], 1, M)) return F;
  for (int r=2; r<M; r++) {
	  if (!insertQueue(bSquare[r][0], r, 0)) return F; if (!insertQueue(bSquare[r][M], r, M)) return F;
  }
  for (int c=1; c<M; c++) {
	  if (!insertQueue(bSquare[0][c], 0, c)) return F; if (!insertQueue(bSquare[M][c], M, c)) return F;
  }
  return T;
} // queueBorder

bool computeBounds(const int p, const int r, const int c) {
//   -------------
  if (((0<r)&(r<(M)))&((0<c)&(c<(M)))) { int *bp=&bSquare[r][c]; const int tmp=max(xSquare[r][c],p);
	  if (tmp<*bp) { *bp=tmp; if (!insertQueue(tmp, r, c)) return F; }
  }
  return T;
} // computeBounds

void computeCapacity(Uint *unitsRetained) {
//   ---------------
  Uint units=0;
  for (int r=0; r<N; r++) for (int c=0; c<N; c++) {
	  const int delta=bSquare[r][c]-xSquare[r][c]; if (delta!=0) units+=delta;
 	} *unitsRetained=units;
} // computeCapacity

/*
 *  Based on an algorithm of Gareth McCaughan supplied by Craig Knecht.
 */
bool getWater(Uint *unitsRetained) {
//   --------
  initBounds(); if (!queueBorder()) return F; //fopen_s(&Qfp, "C:\\QueueTrace.txt", "w");
  while (qIndex!=0) { //printQueue();
  	t_Cell cell; popQueue(&cell);	const int p=cell.priority, r=cell.row, c=cell.col;
	  if (!computeBounds(p, r-1, c)) return F; if (!computeBounds(p, r+1, c)) return F;
   	if (!computeBounds(p, r, c-1)) return F; if (!computeBounds(p, r, c+1)) return F;
  }
  computeCapacity(unitsRetained); /* fclose(Qfp); */ return T;
} // getWater

bool getWaterSquareAndComplement(char *dir, FILE **wfpA, FILE **wfpS, FILE **wfpP) {
//   ---------------------------                              
  bool associative=F; Uint units=0; /* retained */ if (!getWater(&units)) return F;
  if (isSelfComplement(xSquare, N, &associative)) {
    if (associative) { ++numAssoc;
      if (units>=maxWaterAssoc) {
        if (units>maxWaterAssoc) { maxWaterAssoc=units; numAssocMax=1;
          *wfpA=reOpen(dir, wfpA, maxAssocFN, assoc); if (*wfpA==NULL) return F;
        } else ++numAssocMax; if (!writeSquare(xSquare, N, *wfpA)) return F;
      }
    } else { ++numSelf;
      if (units>=maxWaterSelf) {
        if (units>maxWaterSelf) { maxWaterSelf=units; numSelfMax=1;
          *wfpS=reOpen(dir, wfpS, maxSelfFN, self); if (*wfpS==NULL) return F;
        } else ++numSelfMax; if (!writeSquare(xSquare, N, *wfpS)) return F;
      }
    }
  } else {
    Uint unitsC=0; /* retained */ ++numOther; complementSquare(xSquare, N);
    if (!getWater(&unitsC)) return F; const Uint unitsP=units+unitsC;
    if (unitsP>=maxWaterPair) {
      if (unitsP>maxWaterPair) {
        maxWaterPair=unitsP; numPairMax=1; *wfpP=reOpen(dir, wfpP, maxPairFN, pair); if (*wfpP==NULL) return F;
      } else ++numPairMax;
      complementSquare(xSquare, N); /* to original */  if (!writeSquare(xSquare, N, *wfpP)) return F;
      complementSquare(xSquare, N); if (!writeSquare(xSquare, N, *wfpP)) return F;
    }
  }
  return T;
} // getWaterSquareAndComplement
//===================================================== main ==========================================================

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

bool checkSize() {
//   ---------
  if (N<=0) { reportError("Order must be a positive integer"); return F; }
  else if (N>maxN) { char msg[100]; sprintf_s(msg, 100, "Order limit is set at %d", maxN); reportError(msg); return F; }
  return T;
} // checkSize

void reportElapsedTime(time_t startTime) {
//   -----------------
  const int et=(int)difftime(time(NULL), startTime);
  if (et>0) printf("\nelapsed time %d:%02d:%02d\n", et/3600, et%3600/60, et%60);
} // reportElapsedTime

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

int main() {
//  ----
  outputLocalTime(); bool another=T, inputSize=T, ok=F; allocatedSquaresSize=0; allocatedQueueSize=0;
  do { // for each input file
    bool writeError=F; if (inputSize) { printf("\nInput the order of the squares: "); N=getSize(); }
    if (checkSize()) { initGlobals();
      if (allocateStore(N)) { char dir[bufSize];
        if (openDir(dir)) { char ibuf[bufSize]; FILE *rfp=openInput(ibuf);
          if (rfp!=NULL) {
            time_t startTime=time(NULL); FILE *wfp, *wfpA, *wfpS, *wfpP;
            if (openFiles(dir, &wfp, &wfpA, &wfpS, &wfpP)) { squareNumber=0;
              while (readSquare(rfp)) {
                if (!getWaterSquareAndComplement(dir, &wfpA, &wfpS, &wfpP)) { writeError=T; break; }
              }
              if (!writeError) writeError=!writeSummary(wfp); if (!writeError) ok=T;
              closeFiles(&wfp, &wfpA, &wfpS, &wfpP);
            } else writeError=T; fclose(rfp); reportElapsedTime(startTime);
          } // (rfp!=NULL
        } // if (openDir
      }
    } // (checkSize
    another=doAnother(&inputSize, writeError);
  } while (another);
  printf("\nPress a key to close the console");
  while (!_kbhit()) Sleep(250); return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main