/*
 *  File:    SquareSums.cpp
 *  Author:  S Harry White
 *  Created: 2015-07-11
 *  Updated: 2022-01-30
 *    Add sums of numbers squared and numbers cubed. Tidy code.
 *  Updated: 2022-08-15
 *    Add support for non-square rectangles.
 *  Updated: 2022-09-06
 *    Add bent diagonals for non-square rectangles.
 *  Updated: 2023-01-24
 *    Open output files in current folder.
 *  Updated: 2023-01-25
 *    Fix bent diagonal summing for non-square rectangles.
 *  Updated: 2023-01-31
 *    Change to compile with g++ for macOS.
 */

/*
 *  Computes the sums of these lines of rectangles:
 *
 *    . rows
 *    . columns
 *    . diagonals
 *    . bent diagonals
 *
 *  Optionally computes sums of numbers, numbers squared, or numbers cubed.
 */

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

const bool F=false, T=true;
int R, C, B, **iSquare=NULL, allocatedR=0, allocatedC=0, *rowSums=NULL, *colSums=NULL, *bDiagSums=NULL,
  *fDiagSums=NULL, *uBentSums=NULL, *rBentSums=NULL, *dBentSums=NULL, *lBentSums=NULL;

const int bufSize=1024; bool bell=T; char fmt[bufSize];
bool reportError(const char *msg) { printf("%sError: %s.\n", bell ? "\a\n" : "", msg); return bell=F; }
//   -----------
//================================================== store ===================================================

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

void freeStore() {
//   ---------
  freeSquare(&iSquare, allocatedR); allocatedR=0; allocatedC=0;
  if (rowSums  !=NULL) { free(rowSums);   rowSums=NULL;   }
  if (colSums  !=NULL) { free(colSums);   colSums=NULL;   }
  if (bDiagSums!=NULL) { free(bDiagSums); bDiagSums=NULL; }
  if (fDiagSums!=NULL) { free(fDiagSums); fDiagSums=NULL; }
  if (uBentSums!=NULL) { free(uBentSums); uBentSums=NULL; }
  if (rBentSums!=NULL) { free(rBentSums); rBentSums=NULL; }
  if (dBentSums!=NULL) { free(dBentSums); dBentSums=NULL; }
  if (lBentSums!=NULL) { free(lBentSums); lBentSums=NULL; }
} // freeStore

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

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

bool allocateStore() {
//   -------------
  bool ok=T; B=R>C?R:C;
  if ((R>allocatedR)|(C>allocatedC)) { freeStore();
    if ((ok=allocateSquare(&iSquare)))
      if ((ok=allocateSums(&rowSums, R)))
        if ((ok=allocateSums(&colSums, C)))
          if ((ok=allocateSums(&bDiagSums, B)))
            if ((ok=allocateSums(&fDiagSums, B)))
              if ((ok=allocateSums(&uBentSums, R)))
                if ((ok=allocateSums(&rBentSums, C)))
                  if ((ok=allocateSums(&dBentSums, R)))
                    ok=allocateSums(&lBentSums, C);
    if (ok) { allocatedR=R; allocatedC=C; } else 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');
} // 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

int getInt() { int n=0; scanf("%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

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]) {
//    ---------
  FILE *rfp=NULL; char *rFname=NULL;
  do {
    printf("\nEnter the name of the squares file: ");
    if (getFileName(buf, bufSize-6)) { 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) {
    adjustName(buf);
    if ((rfp=fopen(buf, "r"))==NULL) {
      const int msgSize=bufSize+50; char msg[msgSize];
      snprintf(msg, msgSize, "\a\nCan't open for read %s", buf); perror(msg);
    }
  }
  return rfp;
} // openInput

void writeNumberOfSquares(const char *s, const int num) {
//   --------------------
  const int msq=num/1000000, tsq=num%1000000/1000, rsq=num%1000;
  if (msq==0) if (tsq==0) printf("%s %d\n", s, rsq); else printf("%s %d,%03d\n", s, tsq, rsq);
  else printf("%s %d,%03d,%03d\n", s, msq, tsq, rsq);
} // writeNumberOFSquares

int numSquares, // Number of squares.
    printNum;   // print a message at this number of squares
bool readSquare(FILE *rfp) {
//   ----------
  for (int r=0; r<R; r++) for (int c=0; c<C; c++) {
	  int rv;
    if ( (rv=fscanf(rfp, "%d", &iSquare[r][c]))!=1) {
      if ( (rv!=EOF)|(r!=0)|(c!=0) ) printf("\a\nError reading square from file.\n"); return F;
    }
  }
  if (++numSquares==printNum) { writeNumberOfSquares("..", numSquares); printNum+=printNum; }
  return T;
} // readSquare
//============================================= output ==================================================

void stripName(char *inFname, char *obuf) {
//   ---------
  char *s=inFname; while (*s++!='\0');
  while (--s!=inFname) // Remove .txt and any directory path
    if (*s=='.') *s='\0'; else if ((*s=='\\')|(*s=='/')) { ++s; break; } strcpy(obuf, s); 
} // stripName

FILE *openOutput(char *inFname, const int which) {
//    ----------
  int sub=0; FILE *wfp=NULL; const int baseSize=bufSize+25, outSize=bufSize+50;
  char baseName[baseSize], buf[outSize]; stripName(inFname, buf);
  snprintf(baseName, baseSize, "./%s_%sSums", buf, (which==1) ? "" : (which==2) ? "Sq" : "Cu");
  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) {
    char msg[outSize+50]; snprintf(msg, outSize+50, "\a\nCan't open for write %s", buf); perror(msg);
  } else printf(".. writing sums to file %s\n", buf);
  return wfp;
} // openOutput

bool printHeaders(FILE *wfp) {
//   ------------
  if (fprintf(wfp, "Square %d sums:\n", numSquares)<0) return F;
  if (fprintf(wfp, "                                       diagonals"
                   "                         bent diagonals\n")<0) return F;
  if (fprintf(wfp, " number      row       column       back      forward"
                   "        up         right        down        left\n")<0) return F;
  if (fprintf(wfp, " ------  ----------  ----------  ----------  ----------"
                   "  ----------  ----------  ----------  ----------\n")<0) return F;
  return T;
} // printHeaders

bool printSums(FILE *wfp) {
//   ---------
  int lines=0;
  for (int i=0; i<B; ++i) {
    if (fprintf(wfp, "%7d %11d %11d %11d %11d %11d %11d %11d %11d\n", 
                i+1, i<R?rowSums[i]:0, i<C?colSums[i]:0, bDiagSums[i], fDiagSums[i],
                i<R?uBentSums[i]:0, i<C?rBentSums[i]:0, i<R?dBentSums[i]:0, i<C?lBentSums[i]:0)<0) return F;
    if (++lines==5) { if (fputc('\n', wfp)==EOF) return F; lines=0; }
  }
  return fputc('\n', wfp)!=EOF;
} // printSums
//================================================== sums ==================================================

void clearSums() {
//   ---------
  for (int i=0; i<R; ++i) { rowSums[i]=0; uBentSums[i]=0; dBentSums[i]=0; }
  for (int i=0; i<C; ++i) { colSums[i]=0; rBentSums[i]=0; lBentSums[i]=0; }
  for (int i=0; i<B; ++i) { bDiagSums[i]=0; fDiagSums[i]=0; }
} // clearSums

void getRowColSums(int **x) {
//   -------------
  for (int r=0; r<R; r++) for (int c=0; c<C; c++) rowSums[r]+=x[r][c];
  for (int c=0; c<C; c++) for (int r=0; r<R; r++) colSums[c]+=x[r][c];
} // getRowColSums

void getPanDiagSums(int **x) {
//   --------------
  const int Cm1=C-1;
  if (R<=C) {
    for (int i=0; i<C; ++i) for (int r=0, c=i; r<R; ++r, ++c) {
      if (c==C) c=0;; bDiagSums[i]+=x[r][c]; fDiagSums[i]+=x[r][Cm1-c];
    }
  } else {
    for (int i=0; i<R; ++i) for (int r=i, c=0; c<C; ++r, ++c) {
      if (r==R) r=0; bDiagSums[i]+=x[r][c]; fDiagSums[i]+=x[r][Cm1-c];
    }
  }
} // getPanDiagSums

void getBentDiagSums(int **x) {
//   ---------------
  const int Rm1=R-1, Rd2=R/2, Rd2p1=Rd2+1, Cm1=C-1, Cd2=C/2, Cd2p1=Cd2+1; const bool odd=(R&1)==1;
  if (C>2) for (int i=Rm1; i>=0; --i) { // bent up ?
    for (int c=0, r=i; c<Cd2; ++c, --r) { if (r<0) r=Rm1; uBentSums[Rm1-i]+=x[r][c]+x[r][Cm1-c]; }
    if (odd) { int j=i-Cd2; while (j<0) j+=R; uBentSums[Rm1-i]+=x[j][Cd2]; }
  }
  if (R>2) for (int i=0; i<C; ++i) {  // bent right ?
    for (int r=0, c=i; r<Rd2; ++r, ++c) { if (c==C) c=0; rBentSums[i]+=x[r][c]+x[Rm1-r][c]; }
    if (odd) { int j=i+Rd2; while (j>=C) j-=C; rBentSums[i]+=x[Rd2][j]; }
  }
  if (C>2) for (int i=0; i<R; ++i) { // bent down ?
    for (int c=0, r=i; c<Cd2; ++c, ++r) { if (r==R) r=0; dBentSums[i]+=x[r][c]+x[r][Cm1-c]; }
    if (odd) { int j=i+Cd2; while (j>=R) j-=R; dBentSums[i]+=x[j][Cd2]; }
  }
  if (R>2) for (int i=Cm1; i>=0; --i) { // bent left ?
    for (int r=0, c=i; r<Rd2; ++r, --c) { if (c<0) c=Cm1; lBentSums[Cm1-i]+=x[r][c]+x[Rm1-r][c]; }
    if (odd) { int j=i-Rd2; while (j<0) j+=C; lBentSums[Cm1-i]+=x[Rd2][j]; }
  }
} // getBentDiagSums

void getRowColSumsBi(int **x) {
//   ---------------
  for (int r=0; r<R; r++) for (int c=0; c<C; c++) { int t=x[r][c]; t*=t; rowSums[r]+=t; }
  for (int c=0; c<C; c++) for (int r=0; r<R; r++) { int t=x[r][c]; t*=t; colSums[c]+=t; }
} // getRowColSumsBi

void getPanDiagSumsBi(int **x) {
//   ----------------
  const int Cm1=C-1; int t;
  if (R<=C) {
    for (int i=0; i<C; ++i) for (int r=0, c=i; r<R; ++r, ++c) {
      if (c==C) c=0; t=x[r][c]; t*=t; bDiagSums[i]+=t; t=x[r][Cm1-c]; t*=t; fDiagSums[i]+=t;
    }
  } else {
    for (int i=0; i<R; ++i) for (int r=i, c=0; c<C; ++r, ++c) {
      if (r==R) r=0; t=x[r][c]; t*=t; bDiagSums[i]+=t; t=x[r][Cm1-c]; t*=t; fDiagSums[i]+=t;
    }
  }
} // getPanDiagSumsBi

void getBentDiagSumsBi(int **x) {
//   -----------------
  const int Rm1=R-1, Rd2=R/2, Rd2p1=Rd2+1, Cm1=C-1, Cd2=C/2, Cd2p1=Cd2+1; const bool odd=(R&1)==1; int t1, t2;
  if (C>2) for (int i=Rm1; i>=0; --i) { // bent up ?
    for (int c=0, r=i; c<Cd2; ++c, --r) {
      if (r<0) r=Rm1; t1=x[r][c]; t1*=t1; t2=x[r][Cm1-c]; t2*=t2; uBentSums[Rm1-i]+=t1+t2;
    }
    if (odd) { int j=i-Cd2; while (j<0) j+=R; t1=x[j][Cd2]; t1*=t1; uBentSums[Rm1-i]+=t1; }
  }
  if (R>2) for (int i=0; i<C; ++i) {  // bent right ?
    for (int r=0, c=i; r<Rd2; ++r, ++c) {
      if (c==C) c=0; t1=x[r][c]; t1*=t1; t2=x[Rm1-r][c]; t2*=t2; rBentSums[i]+=t1+t2;
    }
    if (odd) { int j=i+Rd2; while (j>=C) j-=C; t1=x[Rd2][j]; t1*=t1; rBentSums[i]+=t1; }
  }
  if (C>2) for (int i=0; i<R; ++i) { // bent down ?
    for (int c=0, r=i; c<Cd2; ++c, ++r) {
      if (r==R) r=0; t1=x[r][c]; t1*=t1; t2=x[r][Cm1-c]; t2*=t2; dBentSums[i]+=t1+t2;
    }
    if (odd) { int j=i+Cd2; while (j>=R) j-=R; t1=x[j][Cd2]; t1*=t1; dBentSums[i]+=t1; }
  }
  if (R>2) for (int i=Cm1; i>=0; --i) { // bent left ?
    for (int r=0, c=i; r<Rd2; ++r, --c) {
      if (c<0) c=Cm1; t1=x[r][c]; t1*=t1; t2=x[Rm1-r][c]; t2*=t2; lBentSums[Cm1-i]+=t1+t2;
    }
    if (odd) { int j=i-Rd2; while (j<0) j+=C; t1=x[Rd2][j]; t1*=t1; lBentSums[Cm1-i]+=t1; }
  }
} // getBentDiagSumsBi

void getRowColSumsTri(int **x) {
//   ----------------
  for (int r=0; r<R; r++) for (int c=0; c<C; c++) { int t=x[r][c]; t*=t*t; rowSums[r]+=t; }
  for (int c=0; c<C; c++) for (int r=0; r<R; r++) { int t=x[r][c]; t*=t*t; colSums[c]+=t; }
} // getRowColSumsTri

void getPanDiagSumsTri(int **x) {
//   -----------------
  const int Cm1=C-1; int t;
  if (R<=C) {
    for (int i=0; i<C; ++i) for (int r=0, c=i; r<R; ++r, ++c) {
      if (c==C) c=0; t=x[r][c]; t*=t*t; bDiagSums[i]+=t; t=x[r][Cm1-c]; t*=t*t; fDiagSums[i]+=t;
    }
  } else {
    for (int i=0; i<R; ++i) for (int r=i, c=0; c<C; ++r, ++c) {
      if (r==R) r=0; t=x[r][c]; t*=t*t; bDiagSums[i]+=t; t=x[r][Cm1-c]; t*=t*t; fDiagSums[i]+=t;
    }
  }
} // getPanDiagSumsTri

void getBentDiagSumsTri(int **x) {
//   ------------------
  const int Rm1=R-1, Rd2=R/2, Rd2p1=Rd2+1, Cm1=C-1, Cd2=C/2, Cd2p1=Cd2+1; const bool odd=(R&1)==1; int t1, t2;
  if (C>2) for (int i=Rm1; i>=0; --i) { // bent up ?
    for (int c=0, r=i; c<Cd2; ++c, --r) {
      if (r<0) r=Rm1; t1=x[r][c]; t1*=t1*t1; t2=x[r][Cm1-c]; t2*=t2*t2; uBentSums[Rm1-i]+=t1+t2;
    }
    if (odd) { int j=i-Cd2; while (j<0) j+=R; t1=x[j][Cd2]; t1*=t1*t1; uBentSums[Rm1-i]+=t1; }
  }
  if (R>2) for (int i=0; i<C; ++i) {  // bent right ?
    for (int r=0, c=i; r<Rd2; ++r, ++c) {
      if (c==C) c=0; t1=x[r][c]; t1*=t1*t1; t2=x[Rm1-r][c]; t2*=t2*t2; rBentSums[i]+=t1+t2;
    }
    if (odd) { int j=i+Rd2; while (j>=C) j-=C; t1=x[Rd2][j]; t1*=t1*t1; rBentSums[i]+=t1; }
  }
  if (C>2) for (int i=0; i<R; ++i) { // bent down ?
    for (int c=0, r=i; c<Cd2; ++c, ++r) {
      if (r==R) r=0; t1=x[r][c]; t1*=t1*t1; t2=x[r][Cm1-c]; t2*=t2*t2; dBentSums[i]+=t1+t2;
    }
    if (odd) { int j=i+Cd2; while (j>=R) j-=R; t1=x[j][Cd2]; t1*=t1*t1; dBentSums[i]+=t1; }
  }
  if (R>2) for (int i=Cm1; i>=0; --i) { // bent left ?
    for (int r=0, c=i; r<Rd2; ++r, --c) {
      if (c<0) c=Cm1; t1=x[r][c]; t1*=t1*t1; t2=x[Rm1-r][c]; t2*=t2*t2; lBentSums[Cm1-i]+=t1+t2;
    }
    if (odd) { int j=i-Rd2; while (j<0) j+=C; t1=x[Rd2][j]; t1*=t1*t1; lBentSums[Cm1-i]+=t1; }
  }
} // getBentDiagSumsTri

bool getSums(int **x, const int which, FILE *wfp) {
//   -------
  if (printHeaders(wfp)) {
    clearSums();
    switch (which) {
      case 1: getRowColSums(x); getPanDiagSums(x); getBentDiagSums(x); break;
      case 2: getRowColSumsBi(x); getPanDiagSumsBi(x); getBentDiagSumsBi(x); break;
      case 3: getRowColSumsTri(x); getPanDiagSumsTri(x); getBentDiagSumsTri(x); break;
      default: printf("Unsupported choice %d\n", which); return F;
    }
    return printSums(wfp);
  }
  return F;
} // getSums
//======================================================== main =======================================================

bool checkRC() { if ((R<=0)|(C<=0)) return reportError("Invalid order"); return T; }
//   -------

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 printElapsedTime(time_t startTime) {
//   ---------------- 
  const int et=(int)difftime(time(NULL), startTime);
  if (et>0) printf("\nelapsed time %d:%02d:%02d\n", et/3600, et%3600/60, et%60);
} // printElapsedTime

int main() {
//  ----
  bool anotherOrder=T, inputOrder=T, ok=F, writeError=F; char buf[bufSize]; outputLocalTime();
  do {
    if (inputOrder) { printf("\nOrder? "); if (!getInts(&R, &C, -1)) break; }
    if (checkRC()) {
      if (allocateStore()) {
        FILE *rfp=openInput(buf); 
        if (rfp!=NULL) {
          printf("Which, (1: numbers, 2: numbers squared, 3: numbers cubed)? "); const int which=getInt();
          FILE *wfp=openOutput(buf, which);
          if (wfp!=NULL) {
            numSquares=0; printNum=100000; time_t startTime=time(NULL); 
            while (readSquare(rfp)) { if ((writeError=!getSums(iSquare, which, wfp))) break; }
            if (!writeError) {
              ok=T; writeNumberOfSquares("\nNumber of Squares:", numSquares);
              printElapsedTime(startTime);
            } fclose(wfp);
          }
          fclose(rfp);
        } // rfp!=NULL
      } // allocateSquare
    } // checkRC
    if (writeError) { perror("\n\aError writing file"); anotherOrder=F; }
    else {
      printf("\nAnother order? input y (yes), n (no) or the order: ");
      if (getYorInts(&R, &C)) inputOrder=(R<0); else anotherOrder=F;
    }
    if (!anotherOrder) freeStore();
  } while (anotherOrder);
  printf("\nHit return to close the console.");
  while (T) if (getchar()=='\n') break; return ok ? EXIT_SUCCESS : EXIT_FAILURE;
} // main