
/*
 *  File:    makeSquare.js
 *  Author:  S Harry White
 *  Created: 2009-02-20
 *  Updated: 2009-12-07
 */

function isNumeric(s) { var bad = /[^\d]/;  return !(bad.test(s)); }

function trim(s) { return s.replace(/(?:(?:^|\n)[\s0]+|\s+(?:$|\n))/g, ""); }

function writeTo(where, s) { document.getElementById(where).innerHTML = s; }

function MagicSquare() {
  var size           = 3;      var maxSize     = 25;
  var cellSize       = 30;     var sum         = 0;
  var bones          = false;  var firstRun    = true;
  var displayIsBones = false;  var displaySize = 0;

  var R_turn    = [];
      R_turn[0] // no turn
        = function(n, i, j) { xSquare[i][j]     = bSquare[i][j] };
      R_turn[1] // rotate 90
        = function(n, i, j) { xSquare[j][n-i]   = bSquare[i][j] };
      R_turn[2] // rotate 180
        = function(n, i, j) { xSquare[n-i][n-j] = bSquare[i][j] };
      R_turn[3] // rotate 270
        = function(n, i, j) { xSquare[n-j][i]   = bSquare[i][j] };
      R_turn[4] // reflect in X-axis
        = function(n, i, j) { xSquare[n-i][j]   = bSquare[i][j] };
      R_turn[5] // reflect in Y-axis
        = function(n, i, j) { xSquare[i][n-j]   = bSquare[i][j] };
      R_turn[6] // reflect in XY diagonal
        = function(n, i, j) { xSquare[n-j][n-i] = bSquare[i][j] };
      R_turn[7] // reflect in YX diagonal
        = function(n, i, j) { xSquare[j][i]     = bSquare[i][j] };

  var none = 0;   var any = 1;
  var bSquare   = [];
  var xSquare   = [];

  this.setBones   = setBones;    this.getBones = getBones;   
  this.setSize    = setSize;     this.getSize  = getSize;
  this.getMaxSize = getMaxSize;  this.getSum   = getSum;

  this.initSquare   = initSquare;
  this.isCorrect    = isCorrect;
  this.make         = makeAny;
  this.putSquare    = putSquare;
  this.clearDisplay = clearDisplay;

  function initSquare() {
    if (firstRun) {
      var i;  var maxUPB = maxSize - 1;
  
      for (i = 0; i < maxSize; i++) {
        bSquare[i] = [];  bSquare[i][maxUPB] = 0; 
      }    
      for (i = 0; i < maxSize; i++) {
        xSquare[i] = [];  xSquare[i][maxUPB] = 0; 
      }
      firstRun = false;
    }
  }

  function setBones(tf) { bones = tf; }

  function setSize(n) { size = n; }

  function getBones() { return bones; }

  function getSize() { return size; }

  function getMaxSize() { return maxSize; }
    
  function getSum() { return sum; }

  function clearDisplay() { writeTo("message", " "); writeTo("square", " "); }
  //---------------------------------------------------------------------------

  function isCorrect() {
    var chkSum = bones ? 0 : sum;
    var sumX, sumY, sumXY, sumYX;

    sumXY = 0;  sumYX = 0;
    for (var i = 0; i < size; i++) {
      sumX = 0; sumY = 0;
      for (var j = 0; j < size; j++) {
        sumX += xSquare[i][j];
        sumY += xSquare[j][i];
      }
      if ((sumX != chkSum) || (sumY != chkSum)) { return false; }
      sumXY += xSquare[i][size - i - 1];
      sumYX += xSquare[i][i];
    }
    return ((sumXY == chkSum) && (sumYX == chkSum));
  } // function isCorrect
  //---------------------------------------------------------------------------


  function putSquare() {
    var sTable= "<table border=\"1\" cellspacing=\"1\">";

    for (var i = 0; i < size; i++) {
      var sRow = "<tr>";

      for (var j = 0; j < size; j++) { 
        var bgColor;  var fontColor;  var cell = xSquare[i][j];
        sRow += "<td align=\"center\"  width=\"" + cellSize +
                "\" height=\"" + cellSize + "\"";
        if (cell > 0) {
          if (bones) {
            if (size % 2 == 0) { cell -= 0.5; }
            bgColor="#ffcccc"; fontColor="#800000";
          } else {
            bgColor="#cfcfcf"; fontColor="#000000";
          }
        } else if (cell < 0) {
          if (size % 2 == 0) { cell += 0.5; }
          cell= -cell;
          bgColor="#ccffff"; fontColor="#000080";
        } else {
          bgColor="#ccffcc"; fontColor="#008000";
        }  
        sRow += "bgcolor=\"" + bgColor + "\"><font color=\"" + fontColor +
                        "\">" + cell + "</font></td>";
      }     
      sRow += "</tr>"; sTable += sRow;
    }
    sTable += "</table>";  writeTo("square", sTable);
    displayIsBones = bones;  displaySize = size;
  } // function putSquare
  //---------------------------------------------------------------------------


  function Rturn(o, n, kind) {
    var i, j, k;
    var nmo = n - o;  var npo = n + o;
    var which = parseInt(Math.random() * 8);

    if (which == 8) { which = 7; } // Can random return 1?
    k = (kind == none ? 0 : which);
    if ((n - o == 0) || (n - o == 3)) { // turn all 1x1 and 4x4
      for (i = o; i <= n; i++) {
        for (j = o; j <= n; j++) { 
          R_turn[k](npo, i, j);
        }
      }
    } else { // turn only border rows and columns  
      for (i = o; i <= n; i += nmo) {
        for (j = o; j <= n; j++) {
           R_turn[k](npo, i, j);
        }
      }
      for (j = o; j <= n; j += nmo) {
        for (i = o + 1; i < n; i++) {
           R_turn[k](npo, i, j);
        }
      }
    }
  } // function Rturn
  //---------------------------------------------------------------------------


  function toggleActual(plus_minus) {    
    var plus_minus_midc =  plus_minus * (size * size + 1) / 2;

    for (var i = 0; i < size; i++) {
      for (var j = 0; j < size; j++) {
        xSquare[i][j] += plus_minus_midc;
      }
    }
  } // function toggleActual
  //---------------------------------------------------------------------------


  function makeEven() {
    var i, j, k, o, n, m, v;

    v = 0.5;
                 // fill center 4x4
    o = (size - 4) / 2;  k = o + 1;  m = o + 2;  n = o + 3;
    bSquare[m][m] = -v; bSquare[k][k] = v++;
    bSquare[k][n] = -v; bSquare[m][o] = v++;
    bSquare[k][o] = -v; bSquare[m][n] = v++;
    bSquare[m][k] = -v; bSquare[k][m] = v++;
    bSquare[o][k] = -v; bSquare[n][m] = v++;
    bSquare[n][o] = -v; bSquare[o][n] = v++;
    bSquare[n][n] = -v; bSquare[o][o] = v++;
    bSquare[o][m] = -v; bSquare[n][k] = v++;
    // Rturn(o, n, size == 4 ? none : any);
    Rturn(o, n, any);
   
    for (i = 6; i <= size; i += 2) {
      o = (size - i) / 2;  n = o + i - 1;  k = (o + n - 1) / 2;  m = k + 1;
      if (i % 4 != 0) { //------------------------------  // singly even     
        for (j = k; j > o; j -= 2) {                      // SSW,NNW
            bSquare[o][j]   = -v; bSquare[n][j]   = v++;
            bSquare[n][j-1] = -v; bSquare[o][j-1] = v++;
        }
            bSquare[o][m]   = -v; bSquare[n][m]   = v++;  // S-E
            bSquare[o+1][o] = -v; bSquare[o+1][n] = v++;  // ENE  
        for (j = o + 2; j < k; j += 2) {                  // WNW,ENE
            bSquare[j][n]   = -v; bSquare[j][o]   = v++;
            bSquare[j+1][o] = -v; bSquare[j+1][n] = v++;
        }
            bSquare[n][n]   = -v; bSquare[o][o]   = v++;  // NW
            bSquare[n][o]   = -v; bSquare[o][n]   = v++;  // NE
            bSquare[m+1][n] = -v; bSquare[m+1][o] = v++;  // WSW
        for (j = m + 2; j < n; j += 2) {                  // ESE,WSW
            bSquare[j][o]   = -v; bSquare[j][n]   = v++;
            bSquare[j+1][n] = -v; bSquare[j+1][o] = v++;
        }
            bSquare[k][n]   = -v; bSquare[k][o]   = v++;  // W-N
            bSquare[o][m+1] = -v; bSquare[n][m+1] = v++;  // SSE
        for (j = m + 2; j < n; j += 2) {                  // NNE,SSE
            bSquare[n][j]   = -v; bSquare[o][j]   = v++;
            bSquare[o][j+1] = -v; bSquare[n][j+1] = v++;
        }
            bSquare[m][o]   = -v; bSquare[m][n]   = v++;  // E-S
      } else { //---------------------------------------  // doubly even
            bSquare[o][k]   = -v; bSquare[n][k]   = v++;  // SSW
        for (j = k - 1; j > o; j -= 2) {                  // NNW,SSW
            bSquare[n][j]   = -v; bSquare[o][j]   = v++;
            bSquare[o][j-1] = -v; bSquare[n][j-1] = v++;
        }
        if (i == 8) {
            bSquare[m][n]   = -v; bSquare[m][o]   = v++;  // W-S
        } else {
            bSquare[m][o]   = -v; bSquare[m][n]   = v++;  // E-S
            bSquare[o+1][n] = -v; bSquare[o+1][o] = v++;  // WNW
          for (j = o + 2; j < (k - 4); j += 2) {          // ENE,WNW
            bSquare[j][o]   = -v; bSquare[j][n]   = v++;
            bSquare[j+1][n] = -v; bSquare[j+1][o] = v++;
          }
            bSquare[k-3][n] = -v; bSquare[k-3][o] = v++;  // WNW 
        } 
            bSquare[k-2][o] = -v; bSquare[k-2][n] = v++;  // ENE
            bSquare[k-1][o] = -v; bSquare[k-1][n] = v++;  // ENE
            bSquare[n][n]   = -v; bSquare[o][o]   = v++;  // NW
            bSquare[n][o]   = -v; bSquare[o][n]   = v++;  // NE
            bSquare[m+1][n] = -v; bSquare[m+1][o] = v++;  // WSW
            bSquare[m+2][n] = -v; bSquare[m+2][o] = v++;  // WSW
        for (j = m + 3; j < n; j += 2) {                  // ESE,WSW
            bSquare[j][o]   = -v; bSquare[j][n]   = v++;
            bSquare[j+1][n] = -v; bSquare[j+1][o] = v++;
        }
            bSquare[k][o]   = -v; bSquare[k][n]   = v++;  // E-N
        for (j = m + 1; j < n; j += 2) {                  // SSE,NNE
            bSquare[o][j]   = -v; bSquare[n][j]   = v++;
            bSquare[n][j+1] = -v; bSquare[o][j+1] = v++;
        }
            bSquare[o][m]   = -v; bSquare[n][m]   = v++;  // S-E
      }
      // Rturn(o, n, i == size ? none : any);
      Rturn(o, n, any);
    }
  }  // function makeEven
  //---------------------------------------------------------------------------


  function makeOdd() {
    var i, j, o, n, m, v;

    m = (size - 1) / 2;  bSquare[m][m] = 0;  Rturn(m, m, none);  v = 1;

    for (i = 3; i <= size; i += 2) {
      o = (size - i) / 2;  n = o + i - 1;  m = (o + n) / 2;

      for (j = o + 1; j < m; j++) {
        bSquare[n][j] = -v; bSquare[o][j] = v++;   // NNW
        bSquare[j][o] = -v; bSquare[j][n] = v++;   // ENE
      }
        bSquare[n][o] = -v; bSquare[o][n] = v++;   // NE
        bSquare[m][o] = -v; bSquare[m][n] = v++;   // E
        bSquare[n][n] = -v; bSquare[o][o] = v++;   // NW
        bSquare[o][m] = -v; bSquare[n][m] = v++;   // S
      for (j = m + 1; j < n; j++) {
        bSquare[o][j] = -v; bSquare[n][j] = v++;   // SSE
        bSquare[j][n] = -v; bSquare[j][o] = v++;   // WSW
      }
      // Rturn(o, n, i == size ? none : any);
      Rturn(o, n, any);
    }
  } // function makeOdd
  //---------------------------------------------------------------------------

  function makeAny() {
    if ( (displaySize == size) && (displayIsBones != bones) ) {
      toggleActual(bones ? -1 : 1);
    } else {
      if (size % 2) { makeOdd(); } else { makeEven(); }
      if (!bones) { toggleActual(1); }
      sum = size * (size * size + 1) / 2;
    }
  } // function makeAny

} // function MagicSquare
