code: ///////////////////////////////////////////////////////////////////////////////
  // For information as to what this class does, see the Javadoc, below.       //
  // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,       //
  // 2007, 2008, 2009, 2010, 2014, 2015 by Peter Spirtes, Richard Scheines, Joseph   //
  // Ramsey, and Clark Glymour.                                                //
  //                                                                           //
  // This program is free software; you can redistribute it and/or modify      //
  // it under the terms of the GNU General Public License as published by      //
  // the Free Software Foundation; either version 2 of the License, or         //
  // (at your option) any later version.                                       //
  //                                                                           //
  // This program is distributed in the hope that it will be useful,           //
  // but WITHOUT ANY WARRANTY; without even the implied warranty of            //
  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             //
  // GNU General Public License for more details.                              //
  //                                                                           //
  // You should have received a copy of the GNU General Public License         //
  // along with this program; if not, write to the Free Software               //
  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA //
  ///////////////////////////////////////////////////////////////////////////////
  
  package edu.cmu.tetrad.util;
  
  import java.util.HashMap;
  import java.util.Map;
  
  /**
   * <p>Stores a table of cells with int values of arbitrary dimension. The
   * dimensionality of the table is set in the reset() method; if a dimensionality
   * is set in the constructor, it will be passed to the reset() method. Every
   * time the dimensionality is changed, the table is reset to zero throughout. If
   * the dimensionality is set to null, the table cannot be used until a non-null
   * dimensionality is set.</p>
   *
   * @author Joseph Ramsey
   */
  public class MultiDimIntTable {
  
      /**
       * A single-dimension array containing all of the cells of the table. Must
       * be at least long enough to contain data for each cell allowed for by the
       * given dimension array--in other words, the length must be greater than or
       * equal to dims[0] & dims[1] ... * dims[dims.length - 1].
       */
      private Map<Integer, Long> cells;
  
      /**
       * The number of cells in the table. (May be different from the length of
       * cells[].
       */
      private int numCells;
  
      /**
       * An array whose length is the number of dimensions of the cell and whose
       * contents, for each value dims[i], are the numbers of values for each
       * i'th dimension. Each of these dimensions must be an integer greater than
       * zero.
       */
      private int[] dims;
  
      /**
       * Constructs a new multidimensional table of integer cells, with the given
       * (fixed) dimensions. Each dimension must be an integer greater than zero.
       *
       * @param dims An int[] array of length > 0, each element of which specifies
       *             the number of values of that dimension (> 0).
       */
      public MultiDimIntTable(int[] dims) {
          reset(dims);
      }
  
      /**
       * @param coords The coordinates of the cell. Each value must be less
       *               than the number of possible value for the corresponding
       *               dimension in the table. (Enforced.)
       * @return the row in the table for the given node and combination of parent
       * values.
       */
      public synchronized int getCellIndex(int[] coords) {
          int cellIndex = 0;
  
          for (int i = 0; i < dims.length; i++) {
              cellIndex *= dims[i];
              cellIndex += coords[i];
          }
  
          return cellIndex;
      }
  
      /**
       * @param cellIndex an <code>int</code> value
       * @return the array representing the combination of parent values for this
       * row.
       */
      @SuppressWarnings({"SameParameterValue"})
      public synchronized int[] getCoordinates(int cellIndex) {
          int[] coords = new int[this.dims.length];
  
          for (int i = this.dims.length - 1; i >= 0; i--) {
              coords[i] = cellIndex % this.dims[i];
              cellIndex /= this.dims[i];
          }
  
          return coords;
      }
  
      /**
       * Increments the value at the given coordinates by the specified amount,
       * returning the new value.
       *
       * @param coords The coordinates of the table cell to update.
       * @param value  The amount by which the table cell at these coordinates
       *               should be incremented (an integer).
       * @return the new value at that table cell.
       */
      public synchronized long increment(int[] coords, int value) {
          int cellIndex = getCellIndex(coords);
  
          if (!cells.containsKey(cellIndex)) {
              cells.put(cellIndex, 0L);
          }
  
          cells.put(cellIndex, cells.get(cellIndex) + value);
          return cells.get(cellIndex);
      }
  
      /**
       * Sets the value at the given coordinates to the given value,
       * returning the new value.
       *
       * @param coords The coordinates of the table cell to update.
       * @param value  The amount by which the table cell at these coordinates
       *               should be incremented (an integer).
       * @return the new value at that table cell.
       */
      @SuppressWarnings({"UnusedDeclaration"})
      public synchronized long setValue(int[] coords, int value) {
          int cellIndex = getCellIndex(coords);
          cells.put(cellIndex, (long) value);
          return cells.get(cellIndex);
      }
  
      /**
       * @param coords The coordinates of the table cell to update.
       * @return the new value at that table cell.
       */
      public synchronized long getValue(int[] coords) {
          int cellIndex = getCellIndex(coords);
  
          if (cells == null || !cells.containsKey(cellIndex)) {
              return 0L;
          } else {
              return cells.get(cellIndex);
          }
      }
  
      /**
       * @return this number.
       */
      public synchronized int getNumCells() {
          return this.numCells;
      }
  
      /**
       * @return this number.
       */
      @SuppressWarnings({"UnusedDeclaration"})
      public synchronized int[] getDimensions() {
          return this.dims;
      }
  
      /**
       * Describe <code>getDimension</code> method here.
       *
       * @param var an <code>int</code> value
       * @return an <code>int</code> value
       */
      public synchronized int getDimension(int var) {
          return this.dims[var];
      }
  
      /**
       * @return an <code>int</code> value
       */
      public synchronized int getNumDimensions() {
          return this.dims.length;
      }
  
      /**
       * Resets the table, allowing a different dimensionality. All cells are
       * reset to zero. The underlying data array is reused if possible.
       *
       * @param dims an <code>int[]</code> value
       */
      public synchronized final void reset(int[] dims) {
          if (dims == null) {
              cells = null;
          } else {
              if (dims.length < 1) {
                  throw new IllegalArgumentException(
                          "Table must have at " + "least one dimension.");
              }
  
              for (int i = 0; i < dims.length; i++) {
                  if (dims[i] < 1) {
                      throw new IllegalArgumentException("Dimension " + i +
                              " has fewer than " + "one values: " + dims[i]);
                  }
              }
  
              // Calculate length of cells[] array.
              this.numCells = 1;
  
              for (int dim : dims) {
                  this.numCells *= dim;
              }
  
              // Construct (or reset) cells array.
              cells = new HashMap<>();
  
              // Store the dimensions, making a copy for security.
              this.dims = new int[dims.length];
  
              System.arraycopy(dims, 0, this.dims, 0, dims.length);
          }
      }
  
      public synchronized int getDims(int varIndex) {
          return dims[varIndex];
      }
  }
  
  
  
  