/*
 *        Hortensius - Tim Tyler 2000.
 * 
 *         A generator of Hortensius CA RNGs.
 *
 * This code has been placed in the public domain.
 * This means that you can do what you like with it.
 * Please note that this code comes with no warranty.
 *
 */

/*
 * To Do:
 *
 */

   import java.lang.Object;

   public class Hortensius extends Object {
      public static int size;
      public static int sizemo;
      public static int sizeo2;
      public static int i, j;
      public static int ret_val;
      public static int temp;
   
      public static int total_number;
      public static int iterations;
      public static int generation;
      public static int count;
   
      public static int number;
      public static int numbermo;
      public static int numbero2;
   
      public static int total_found;
   
      public static int cx;
      public static int hside;
      public static int hsidemo;
      public static int mask;
   
      public static int period;
      public static int reversibility;
      public static int total;
   
      public static Executor executor;
   
      static int[] in_grid;
      static int[] out_grid;
      static int[] tmp_grid;
      static int[] results;
   
      static String output_string;
   
      public static void set_size(int h) {
         hside = h;
      
         hsidemo = h - 1;
      
         number   = 1 << (h - 2);
         numbermo = number - 1;
         numbero2 = number >>> 1;
      
         in_grid = new int[h];
         out_grid = new int[h];
      
         results = new int[number];
      }
   
   
      public static void setUpGrid(int n) {
         for (cx = 1; cx < hsidemo; cx++) {
            mask = (n >>> (cx - 1)) & 1;
            if (mask != 0) {
               in_grid[cx] = 150 << 24; // 150 / 145
            }
            else
            {
               in_grid[cx] = 90 << 24; // 90 / 105
            }
         }
      
         in_grid[0] = 0;
         in_grid[hsidemo] = 0;
      }
   
   
      public static void setUpResults() {
         for (cx = 0; cx < number; cx++) {
            results[cx] = 0;
         }
      }
   
   
      public static void setUpCells() {
         in_grid[1] |= 1;
      }
   
   
      public static void setUpCells(int k) {
         for (cx = 1; cx < hsidemo; cx++) {
            if (((k >> (cx - 1)) & 1) != 0) {
               in_grid[cx] |= 1;
            }
            else
            {
               in_grid[cx] &= 0xffffff00;
            }
         }
      }
   
   
      public static void update() {
         // in_grid[0] = (in_grid[0] & 0xffffff00) | (in_grid[hsidemo - 1] & 1); // out_grid[hsidemo - 1];
         // in_grid[hsidemo] = (in_grid[hsidemo] & 0xffffff00) | (in_grid[1] & 1);
      
         for (cx = 1; cx < hsidemo; cx++) {
            temp = (in_grid[cx + 1] & 1) | ((in_grid[cx] & 1) << 1) | ((in_grid[cx - 1] & 1) << 2);
            out_grid[cx] = (in_grid[cx] & -256) | ((in_grid[cx] >>> 24) >>> temp);
         }
      
         // out_grid[0] = 0;
         // out_grid[hsidemo] = 0;
      
         swap_grids();
      
         generation++;
      }
   
   
      final static void swap_grids() {
         // swap them...
         tmp_grid = in_grid;
         in_grid = out_grid;
         out_grid = tmp_grid;
      }
   
   
      public static void report(int n) {
         resetPrinting();
      
         setUpGrid(n);
      
         setUpCells();
      
         generation = 0;
      
         R1D_Period.setSearch(in_grid,1);
      
         period = 0;
      
         do {
            update();
         
            if ((period == 0) && (R1D_Period.periodFound(in_grid))) {
               period = generation;
            }
         } while (generation < number); // !R1D_Period.periodFound(in_grid));
      
         if (period != 0) {
            if (period == numbermo) {
               printOut();
            }
         }
      
         executor.execute();
      }
   
   
      // Test for reversibility by exhaustively ensuring the automata is bijective
      public static void reversibilityReport(int n) {
         resetPrinting();
      
         setUpGrid(n);
      
         setUpResults();
      
         generation = 0;
      
         for (int wibble = 0; wibble < number; wibble++) {
            setUpCells(wibble);
         
            update();
         
            total = 0;
            for (cx = hsidemo; cx-- > 1; ) {
               total = (total << 1) | (in_grid[cx] & 1);
            }
         
            results[total] = 1;
         }
      
         reversibility = 1;
      
         for (cx = 0; cx < number; cx++) {
            // print("" + results[cx]);
            if (results[cx] == 0) {
               reversibility = 0;
            }
         }
      
         if (reversibility != 0) 
            printReversibility();
      
         executor.execute();
      }
   
   
      // Test for reversibility by only checking four consecutive states for possible precursors.
      public static void quickReversibilityReport(int n) {
         resetPrinting();
      
         setUpGrid(n);
      
         reversibility = 0;
      
         if ((hasAPrecursor(0)) && (hasAPrecursor(1)) && (hasAPrecursor(2)) && (hasAPrecursor(3))) {
            reversibility = 1;
         }
      
         if (reversibility != 0) 
            printReversibility();
      
         executor.execute();
      }
   
   
      public static boolean hasAPrecursor(int to_check) {
         for (int wibble = 0; wibble < number; wibble++) {
            setUpCells(wibble);
         
            update();
         
            total = 0;
            for (cx = hsidemo; cx-- > 1; ) {
               total = (total << 1) | (in_grid[cx] & 1);
            }
         
            if (total == to_check) 
               return true;
         }
      
         return false;
      }
   
   
      public static void resetPrinting() {
         output_string = "";
      }
   
   
      public static void printOut() {
         print("" + count + " - ");
      
         for (cx = hsidemo; cx-- > 1; ) {
            mask = (count >>> (cx - 1)) & 1;
            if (mask != 0) {
               print("1");
            }
            else
            {
               print("0");
            }
         }
      
         print(" - Period: " + period + "\n");
         total_found++;
      }
   
   
      public static void printReversibility() {
         print("" + count + " - ");
      
         for (cx = hsidemo; cx-- > 1; ) {
            mask = (count >>> (cx - 1)) & 1;
            if (mask != 0) {
               print("1");
            }
            else
            {
               print("0");
            }
         }
      
         print(" - a reversible rule.\n");
         total_found++;
      }
   
   
      public static void print(String x) {
         output_string += x;
      }
   
   
      public static void main(String args[]) {
         executor =
               new Executor() {
                  public void execute() {
                     System.out.println(output_string);
                  }                         
               };
      
         total_number = 6;
      
         do {
            test(total_number);
         
            total_number++;
         
         } while (true);
      }
   
   
      public static void test(int total_number) {
         set_size(total_number);
      
         total_found = 0;
      
         for (count = 0;  count < number; count++) {
            report(count);
         
         }
      
         resetPrinting();
      
         print("Total maximal period automata found: " + total_found + ".\n");
      
         executor.execute();
      }
   
   
      public static void testReversibility(int total_number) {
         set_size(total_number);
      
         total_found = 0;
      
         for (count = 0;  count < number; count++) {
            // reversibilityReport(count);
            quickReversibilityReport(count);
         }
      
         resetPrinting();
      
         print("Number of reversible rules found: " + total_found + ".\n");
      
         executor.execute();
      }
   
   }
