/* We're overriding all internal data, to my knowledge, so this should be okay */
 public Object clone() {
   try {
     MersenneTwisterFast f = (MersenneTwisterFast) (super.clone());
     f.mt = (int[]) (mt.clone());
     f.mag01 = (int[]) (mag01.clone());
     return f;
   } catch (CloneNotSupportedException e) {
     throw new InternalError();
   } // should never happen
 }
  int[] findEmptySiteCancer(int x, int y) {
    LinkedList vacantSitesCancer = new LinkedList();
    int[] tp1 = new int[2];
    int[] tp2 = new int[2];
    int[] tp3 = new int[2];
    int[] tp4 = new int[2];
    int[] tp5 = new int[2];
    int[] tp6 = new int[2];
    int[] tp7 = new int[2];
    int[] tp8 = new int[2];

    tp1 = convertCoordinatesNoFlux(x + 1, y - 1);
    if ((Cells[tp1[0]][tp1[1]] == 0) || (Cells[tp1[0]][tp1[1]] == 4)) vacantSitesCancer.add(tp1);
    tp2 = convertCoordinatesNoFlux(x + 1, y);
    if ((Cells[tp2[0]][tp2[1]] == 0) || (Cells[tp2[0]][tp2[1]] == 4)) vacantSitesCancer.add(tp2);
    tp3 = convertCoordinatesNoFlux(x + 1, y + 1);
    if ((Cells[tp3[0]][tp3[1]] == 0) || (Cells[tp3[0]][tp3[1]] == 4)) vacantSitesCancer.add(tp3);
    tp4 = convertCoordinatesNoFlux(x, y - 1);
    if ((Cells[tp4[0]][tp4[1]] == 0) || (Cells[tp4[0]][tp4[1]] == 4)) vacantSitesCancer.add(tp4);
    tp5 = convertCoordinatesNoFlux(x, y + 1);
    if ((Cells[tp5[0]][tp5[1]] == 0) || (Cells[tp5[0]][tp5[1]] == 4)) vacantSitesCancer.add(tp5);
    tp6 = convertCoordinatesNoFlux(x - 1, y - 1);
    if ((Cells[tp6[0]][tp6[1]] == 0) || (Cells[tp6[0]][tp6[1]] == 4)) vacantSitesCancer.add(tp6);
    tp7 = convertCoordinatesNoFlux(x - 1, y);
    if ((Cells[tp7[0]][tp7[1]] == 0) || (Cells[tp7[0]][tp7[1]] == 4)) vacantSitesCancer.add(tp7);
    tp8 = convertCoordinatesNoFlux(x - 1, y + 1);
    if ((Cells[tp8[0]][tp8[1]] == 0) || (Cells[tp8[0]][tp8[1]] == 4)) vacantSitesCancer.add(tp8);

    // Now let's see where.
    if (vacantSitesCancer.size()
        > 0) { // Now choose a vacant one, otherwise return the original location
      // pick a vacant site and return it
      int vacantElemIndexCancer = random.nextInt(vacantSitesCancer.size());
      int[] p = (int[]) vacantSitesCancer.get(vacantElemIndexCancer);
      return (int[]) p;
    } else {
      int[] p = new int[2];
      p[0] = x;
      p[1] = y; // Just return the original
      System.out.println("wrong!:" + vacantSitesCancer(x, y) + " - " + vacantSitesCancer.size());
      return p;
    }
  }
  /** Tests the code. */
  public static void main(String args[]) {
    int j;

    MersenneTwisterFast r;

    // CORRECTNESS TEST
    // COMPARE WITH http://www.math.keio.ac.jp/matumoto/CODES/MT2002/mt19937ar.out

    r = new MersenneTwisterFast(new int[] {0x123, 0x234, 0x345, 0x456});
    System.out.println("Output of MersenneTwisterFast with new (2002/1/26) seeding mechanism");
    for (j = 0; j < 1000; j++) {
      // first, convert the int from signed to "unsigned"
      long l = (long) r.nextInt();
      if (l < 0) l += 4294967296L; // max int value
      String s = String.valueOf(l);
      while (s.length() < 10) s = " " + s; // buffer
      System.out.print(s + " ");
      if (j % 5 == 4) System.out.println();
    }

    // SPEED TEST

    final long SEED = 4357;

    int xx;
    long ms;
    System.out.println("\nTime to test grabbing 100000000 ints");

    Random rr = new Random(SEED);
    xx = 0;
    ms = System.currentTimeMillis();
    for (j = 0; j < 100000000; j++) xx += rr.nextInt();
    System.out.println(
        "java.util.Random: " + (System.currentTimeMillis() - ms) + "          Ignore this: " + xx);

    r = new MersenneTwisterFast(SEED);
    ms = System.currentTimeMillis();
    xx = 0;
    for (j = 0; j < 100000000; j++) xx += r.nextInt();
    System.out.println(
        "Mersenne Twister Fast: "
            + (System.currentTimeMillis() - ms)
            + "          Ignore this: "
            + xx);

    // TEST TO COMPARE TYPE CONVERSION BETWEEN
    // MersenneTwisterFast.java AND MersenneTwister.java

    System.out.println("\nGrab the first 1000 booleans");
    r = new MersenneTwisterFast(SEED);
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextBoolean() + " ");
      if (j % 8 == 7) System.out.println();
    }
    if (!(j % 8 == 7)) System.out.println();

    System.out.println("\nGrab 1000 booleans of increasing probability using nextBoolean(double)");
    r = new MersenneTwisterFast(SEED);
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextBoolean((double) (j / 999.0)) + " ");
      if (j % 8 == 7) System.out.println();
    }
    if (!(j % 8 == 7)) System.out.println();

    System.out.println("\nGrab 1000 booleans of increasing probability using nextBoolean(float)");
    r = new MersenneTwisterFast(SEED);
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextBoolean((float) (j / 999.0f)) + " ");
      if (j % 8 == 7) System.out.println();
    }
    if (!(j % 8 == 7)) System.out.println();

    byte[] bytes = new byte[1000];
    System.out.println("\nGrab the first 1000 bytes using nextBytes");
    r = new MersenneTwisterFast(SEED);
    r.nextBytes(bytes);
    for (j = 0; j < 1000; j++) {
      System.out.print(bytes[j] + " ");
      if (j % 16 == 15) System.out.println();
    }
    if (!(j % 16 == 15)) System.out.println();

    byte b;
    System.out.println("\nGrab the first 1000 bytes -- must be same as nextBytes");
    r = new MersenneTwisterFast(SEED);
    for (j = 0; j < 1000; j++) {
      System.out.print((b = r.nextByte()) + " ");
      if (b != bytes[j]) System.out.print("BAD ");
      if (j % 16 == 15) System.out.println();
    }
    if (!(j % 16 == 15)) System.out.println();

    System.out.println("\nGrab the first 1000 shorts");
    r = new MersenneTwisterFast(SEED);
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextShort() + " ");
      if (j % 8 == 7) System.out.println();
    }
    if (!(j % 8 == 7)) System.out.println();

    System.out.println("\nGrab the first 1000 ints");
    r = new MersenneTwisterFast(SEED);
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextInt() + " ");
      if (j % 4 == 3) System.out.println();
    }
    if (!(j % 4 == 3)) System.out.println();

    System.out.println("\nGrab the first 1000 ints of different sizes");
    r = new MersenneTwisterFast(SEED);
    int max = 1;
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextInt(max) + " ");
      max *= 2;
      if (max <= 0) max = 1;
      if (j % 4 == 3) System.out.println();
    }
    if (!(j % 4 == 3)) System.out.println();

    System.out.println("\nGrab the first 1000 longs");
    r = new MersenneTwisterFast(SEED);
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextLong() + " ");
      if (j % 3 == 2) System.out.println();
    }
    if (!(j % 3 == 2)) System.out.println();

    System.out.println("\nGrab the first 1000 longs of different sizes");
    r = new MersenneTwisterFast(SEED);
    long max2 = 1;
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextLong(max2) + " ");
      max2 *= 2;
      if (max2 <= 0) max2 = 1;
      if (j % 4 == 3) System.out.println();
    }
    if (!(j % 4 == 3)) System.out.println();

    System.out.println("\nGrab the first 1000 floats");
    r = new MersenneTwisterFast(SEED);
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextFloat() + " ");
      if (j % 4 == 3) System.out.println();
    }
    if (!(j % 4 == 3)) System.out.println();

    System.out.println("\nGrab the first 1000 doubles");
    r = new MersenneTwisterFast(SEED);
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextDouble() + " ");
      if (j % 3 == 2) System.out.println();
    }
    if (!(j % 3 == 2)) System.out.println();

    System.out.println("\nGrab the first 1000 gaussian doubles");
    r = new MersenneTwisterFast(SEED);
    for (j = 0; j < 1000; j++) {
      System.out.print(r.nextGaussian() + " ");
      if (j % 3 == 2) System.out.println();
    }
    if (!(j % 3 == 2)) System.out.println();
  }
  // main CELL CA loop************
  public boolean iterateCells() {
    /*
    	// modify consumption matrix
    	    	for (int i=0;i<size;i++)
    	        for (int j=0;j<size;j++) consumption[i][j] = consumptionBasal[Cells[i][j]];
    */
    //
    if (cellList == null) cellList = new Bag(size * size);
    for (int i = 0; i < size; i++)
      for (int j = 0; j < size; j++) {
        if (Cells[i][j]
            < 4) { // All tumour cell types have Cell > 0, now 0 corresponds to 'healthy cells' that
          // consume at basal rate only
          int[] p = new int[2];
          p[0] = i;
          p[1] = j;
          cellList.add(p);
          if (Cells[i][j] == 1) {
            stem_cells_this_TS++;
          } else if (Cells[i][j] == 2 || Cells[i][j] == 3) {
            non_stem_cells_this_TS++;
          }
        }
      }

    while (cellList.size() != 0) {
      // Select the next lattice element at random
      int randomElemIndex = 0;
      if (cellList.size() > 1) randomElemIndex = random.nextInt(cellList.size() - 1);
      int[] point = (int[]) cellList.get(randomElemIndex);
      int rI = point[0];
      int rJ = point[1];

      cellList.remove(randomElemIndex); // Remove it from the cell list
      int cell = Cells[rI][rJ];

      // Cell death
      // if ((Oxygen[rI][rJ]<hypoxia)) {
      if ((random.nextFloat() < deathprob) && Cells[rI][rJ] > 0) { // x% chances of dying
        Age[rI][rJ] = 0;
        if (Cells[rI][rJ] == 1) stemDeathCounter[rI][rJ]++;
        if (Cells[rI][rJ] < 4) {
          // TACDeathCounter[rI][rJ]++;
          Cells[rI][rJ] = 4; // was 0, now making necrotic area (truly empty)
          deaths++;
          stemBirthCounter[rI][rJ] = 0;
          carriedmutation[rI][rJ] = 0; // empty space now has no mutations
          carriedGenome[rI][rJ] = initGenome; // empty space now has no mutations
        }
      } else if ((cell == 3)
          && (Age[rI][rJ]
              > 100 * maxMatureCellAge)) { // added * to allow for an update each celltimestep/x
        // **************************
        Age[rI][rJ] = 0;
        Cells[rI][rJ] = 4; // was 0, now making necrotic area (truly empty)
        // TACDeathCounter[rI][rJ]++;
        deaths++;
      } else if ((radiotherapy) && (cell == 2) && (random.nextFloat() > Oxygen[rI][rJ])) {
        // Radiotherapy
        Age[rI][rJ] = 0;
        if (Cells[rI][rJ] == 1) stemDeathCounter[rI][rJ]++;
        if ((Cells[rI][rJ] == 2) || (Cells[rI][rJ] == 3))
          // TACDeathCounter[rI][rJ]++;
          Cells[rI][rJ] = 4; // make necrotic
        stemBirthCounter[rI][rJ] = 0;
        deaths++;
      }

      // healthy division
      else if ((cell == 0) && (vacantSites(rI, rJ) > 0)) {
        if (proliferation[cell]
            >= random.nextFloat()) { // If tossing the coin we are to proliferate...
          // if (Oxygen[rI][rJ]>prolifThreshold) { // AND the oxygen concentration is enough for
          // division..
          // consumption[rI][rJ]=consumptionDivision[Cells[rI][rJ]];
          int[] daughter = findEmptySite(rI, rJ);
          births++;
          Cells[daughter[0]][daughter[1]] = 0;
          carriedmutation[daughter[0]][daughter[1]] = 0;
          carriedGenome[daughter[0]][daughter[1]] =
              initGenome; // resetting space to healthy cell with no mutations
        }
      }
      // }

      // cancer division
      else if ((vacantSitesCancer(rI, rJ) > 0) && (cell > 0))
        if (proliferation[cell]
            >= random.nextFloat()) { // If tossing the coin we are to proliferate...
          if ((cell == 1)
              || ((cell == 2)
                  && (Age[rI][rJ] < maxProDivisions))) { // AND the cell is stem or TAC ...
            // if (Oxygen[rI][rJ]>prolifThreshold) { // AND the oxygen concentration is enough for
            // division..
            // consumption[rI][rJ]=consumptionDivision[Cells[rI][rJ]];
            int[] daughter = findEmptySiteCancer(rI, rJ); // and there is space (for cancer)
            //    if ((daughter[0]==0) || (daughter[0]==size) ||
            // (daughter[1]==0)||(daughter[1]==size)) {simulationFinished=true;} // stop sim if a
            // cell hits the edge
            births++;
            if (cell == 1) { // stem cell
              stemBirthsTotal[rI][rJ]++;
              stemBirthCounter[rI][rJ]++;
              if (asymmetricRatio > random.nextFloat()) {
                Cells[daughter[0]][daughter[1]] = 1; // placing the stem daughter
                stemBirthCounter[daughter[0]][daughter[1]] =
                    stemBirthCounter[rI][rJ]; // update stem birth counter
                carriedmutation[daughter[0]][daughter[1]] =
                    carriedmutation[rI][rJ]; // inherit mutational status of parent
                carriedGenome[daughter[0]][daughter[1]] =
                    carriedGenome[rI][rJ]; // inherit mutational status of parent
                if (mutfreq > random.nextFloat()) { // small chance of mutation
                  mutationNum++; // advance mutation number
                  System.out.println(
                      +carriedmutation[rI][rJ]
                          + ", "
                          + mutationNum
                          + ", "
                          + stem_cells_this_TS
                          + ", "
                          + non_stem_cells_this_TS
                          + ", "
                          + timestep); // print (parent,child) pair
                  // tree.put(carriedmutation[rI][rJ], mutationNum);
                  // timeTree.put(mutationNum, timestep); // hash table stuff
                  if (0.5 > random.nextFloat()) {
                    carriedmutation[daughter[0]][daughter[1]] = mutationNum;
                    genomeToMod = new StringBuilder(carriedGenome[daughter[0]][daughter[1]]);
                    genomeToMod.setCharAt(
                        mutationNum - 1, '1'); //  daughter carries new carriedGenome
                    carriedGenome[daughter[0]][daughter[1]] = genomeToMod;
                    // System.out.println (carriedGenome[rI][rJ]);
                    // System.out.println (carriedGenome[daughter[0]][daughter[1]]);
                  } // 50:50 mutate new position daughter
                  else {
                    carriedmutation[rI][rJ] = mutationNum; // else mutate original position daughter
                    genomeToMod = new StringBuilder(carriedGenome[rI][rJ]);
                    // genomeToMod = carriedGenome[rI][rJ];
                    carriedGenome[rI][rJ] = genomeToMod;
                    genomeToMod.setCharAt(
                        mutationNum - 1, '1'); //  original carries new carriedGenome
                    // System.out.println (carriedGenome[rI][rJ]);
                    // System.out.println (carriedGenome[daughter[0]][daughter[1]]);
                  }
                }
              } else {
                Cells[daughter[0]][daughter[1]] = 2; // asymmetric division, daughter is TAC
                stemBirthCounter[daughter[0]][daughter[1]] = 0; // reset stem counter
                carriedmutation[daughter[0]][daughter[1]] =
                    carriedmutation[rI][rJ]; // TAC carries parental mutation flag
                carriedGenome[daughter[0]][daughter[1]] = carriedGenome[rI][rJ];
              } // TAC carries parental genome
              // } // redundant from above

              // else { // Only if there's hypoxia induced change of symmetric division ratio
              //    float newASR=asymmetricRatio+0*(hypoxia-Oxygen[rI][rJ]);
              // currently OFF by way of 0 the ^^ multiplier.
              //    if (newASR>random.nextFloat())
              // {Cells[daughter[0]][daughter[1]]=1;stemBirthCounter[daughter[0]][daughter[1]]=stemBirthCounter[rI][rJ];}
              //    else
              // {Cells[daughter[0]][daughter[1]]=2;stemBirthCounter[daughter[0]][daughter[1]]=0;}
              // // Otherwise differentiate
              // }
            } else if (cell == 2) { // non-stem division
              // TACBirthCounter[rI][rJ]++;
              if (Age[rI][rJ] < maxProDivisions - 1) {
                Cells[daughter[0]][daughter[1]] = 2;
                Age[rI][rJ]++;
                Age[daughter[0]][daughter[1]] = Age[rI][rJ];
                carriedmutation[daughter[0]][daughter[1]] =
                    carriedmutation[rI][rJ]; // TAC carries parental mutation flag
                carriedGenome[daughter[0]][daughter[1]] =
                    carriedGenome[rI][rJ]; // TAC carries parental genome
              } else {
                Cells[daughter[0]][daughter[1]] = 3;
                Cells[rI][rJ] = 3;
                Age[rI][rJ] = 0;
                Age[daughter[0]][daughter[1]] = Age[rI][rJ];
                carriedmutation[daughter[0]][daughter[1]] =
                    carriedmutation[rI][rJ]; // TAC carries parental mutation flag
                carriedGenome[daughter[0]][daughter[1]] =
                    carriedGenome[rI][rJ]; // TAC carries parental genome
              }
            }
          }
        } else if (pMotility > random.nextFloat()) { // Migration = not in use
          int[] daughter = findEmptySite(rI, rJ);
          Cells[daughter[0]][daughter[1]] = cell;
          Cells[rI][rJ] = 0;
          Age[daughter[0]][daughter[1]] = Age[rI][rJ];
          Age[rI][rJ] = 0;
          System.err.println("moving " + rI + ", " + rJ);
        }
      // Aging for mature cells
      if (cell == 3) Age[rI][rJ]++;
    }
    return true;
  }