// FILE. . . . . /home/hak/hlt/src/hlt/osf/base/Sort.java
// EDIT BY . . . Hassan Ait-Kaci
// ON MACHINE. . Hak-Laptop
// STARTED ON. . Mon Sep 02 09:16:33 2013

/**
 * @version     Last modified on Thu May 09 05:18:36 2019 by hak
 * @author      <a href="mailto:hak@acm.org">Hassan A&iuml;t-Kaci</a>
 * @copyright   &copy; <a href="http://www.hassan-ait-kaci.net/">by the author</a>
 */

package hlt.osf.base;

import java.util.HashSet;

import hlt.osf.util.Custom;
import hlt.osf.util.BitCode;
import hlt.osf.util.Decoded;
import hlt.osf.exec.Context;
import hlt.osf.exec.Taxonomy;
import hlt.osf.exec.LockedCodeArrayException;

import hlt.language.util.Comparable;

/**
 * This is the class of an atomic sort's symbol name as explained in the
 * <a href="../specs/basic.html#SortClass">specification</a>.
 */
// public class Sort extends SortExpression implements Comparable
public class Sort implements Comparable
  {
    /**
     * Evaluation context for this sort as a sort expression.
     */
    private Context _context;

    /**
     * Returns this sort evaluation context for this sort as a sort
     * expression.
     */
    public Context context ()
    {
      return _context;
    }

    /**
     * Sets this sort's sort expression encoding evaluation context to
     * the given <a
     * href="./Context.html"><tt>hlt.osf.exec.Context</tt></a>.
     */
    public Sort setContext (Context context)
    {
      _context = context;
      return this;
    }

    /* ************************************************************************ */

    /**
     * SORT EXPRESSION TYPE (a symbol)
     */

    /* ************************************************************************ */

    public byte type ()
    {
      return _context.SYM();
    }

    /* ************************************************************************ */

    /**
     * NEW FIELDS, GETTERS, AND SETTERS (other than inherited from <tt>Sort Expression</tt>)
     */

    /* ************************************************************************ */

    /**
     * The private index of this <tt>Sort</tt>.
     */
    private int _index;

    /**
     * Gets this <tt>Sort</tt>'s index.
     * @return the index of this <tt>Sort</tt> in its taxonomy array
     */
    public int index ()
    {
      return _index;
    }

    /**
     * Sets the index of this <tt>Sort</tt> to the specified argument (an <tt>int</tt>).
     * @return this <tt>Sort</tt>
     */
    public Sort setIndex (int index)
    {
      _index = index;
      return this;
    }

    /* ************************************************************************ */

    /**
     * The private name of this <tt>Sort</tt>.
     */
    private String _name;

    /**
     * Gets this <tt>Sort</tt>'s name.
     * @return the name of this <tt>Sort</tt> in its taxonomy array.
     */
    public String name ()
    {
      return _name;
    }

    /**
     * Sets the name of this <tt>Sort</tt> to the specified argument (a <tt>String</tt>).
     * @return this <tt>Sort</tt>
     */
    public Sort setName (String name)
    {
      _name = name.intern();
      return this;
    }

    /* ************************************************************************ */

    /**
     * The private binary code of this <tt>Sort</tt>.
     */
    private BitCode _bitcode;

    /**
     * Gets this <tt>Sort</tt>'s binary code.
     * @return the binary code of this <tt>Sort</tt> in its taxonomy array.
     */
    public BitCode bitcode ()
    {
      return _bitcode;
    }

    /**
     * Sets this <tt>Sort</tt>'s binary code to the specified <tt>BitCode</tt>.
     * @return this <tt>Sort</tt>.
     */
    public Sort setBitcode (BitCode code)
    {
      _bitcode = code;
      return this;
    }

    /* ************************************************************************ */

    /**
     * CONSTRUCTORS
     */

    /* ************************************************************************ */

    /**
     * Constructs a <tt>Sort</tt> object with the specified name.
     */
    public Sort (String name)
    {
      setName(name);
    }
    
    /**
     * Constructs a <tt>Sort</tt> object with the specified index and name.
     */
    public Sort (int index, String name)
    {
      setIndex(index);
      setName(name);
    }
    
    /**
     * Constructs a <tt>Sort</tt> object with the specified index, name, and bitcode.
     */
    public Sort (int index, String name, BitCode code)
    {
      setIndex(index);
      setName(name);
      setBitcode(code);
    }

    /**
     * Constructs a <tt>Sort</tt> object with the specified name and context.
     */
    public Sort (String name, Context context)
    {
      setName(name);
      setContext(context);
    }
    
    /* ************************************************************************ */

    /**
     * SORT COMPARISON METHODS
     * <p/><b>N.B.</b>:
     * <ul>
 
     * <li/> all are delegated to their associated binary codes
     * in the same way as for sort expressions;

     * <li/> all test whether <tt>code</tt> is <tt>null</tt> and throw a
     * <tt>LockedCodeArrayException</tt> &mdash; may be unnecessary and a
     * source of inefficiency for large taxonomies.

     * </ul>
     */

    /* ************************************************************************ */

    /**
     * Returns true iff this sort is a subsort of the specified sort.
     */
    public boolean isSubsortOf (Sort sort) throws LockedCodeArrayException
    {
      if (_bitcode == null)
	throw new LockedCodeArrayException("Cannot compare a non-encoded sort");

      return _bitcode.isContainedIn(sort.bitcode());
    }

    /**
     * Returns true iff this sort is a strict subsort of the specified sort.
     */
    public boolean isStrictSubsortOf (Sort sort) throws LockedCodeArrayException
    {
      if (_bitcode == null)
	throw new LockedCodeArrayException("Cannot compare a non-encoded sort");

      return _bitcode.isStrictlyContainedIn(sort.bitcode());
    }

    /**
     * Returns true iff this sort is related to the specified sort.
     */
    public boolean isRelatedTo (Sort sort) throws LockedCodeArrayException
    {
      if (_bitcode == null)
	throw new LockedCodeArrayException("Cannot compare a non-encoded sort");

      return _bitcode.isRelatedTo(sort.bitcode());
    }

    /**
     * Returns true iff this sort is unrelated to the specified sort.
     */
    public boolean isUnrelatedTo (Sort sort) throws LockedCodeArrayException
    {
      if (_bitcode == null)
	throw new LockedCodeArrayException("Cannot compare a non-encoded sort");

      return _bitcode.isUnrelatedTo(sort.bitcode());
    }

    /* ************************************************************************ */

    /**
     * UNIQUE ASSOCIATED <tt>Decoded</tt> OBJECT 
     */

    /* ************************************************************************ */

    /**
     * The <tt>Decoded</tt> object corresponding to this sort.
     */
    private Decoded _decoded;

    /**
     * This is a unique <a
     * href="../util/Decoded.html"><tt>hlt.osf.util.Decoded</tt></a>
     * object that is systematically associated to every
     * sort. Conversely, the <tt>sort()</tt> method of the
     * <tt>Decoded</tt> object returned by a <tt>Sort</tt> object's
     * <tt>decoded()</tt> method returns this <tt>Sort</tt> object.
     *
     * @return the <tt>Decoded</tt> object corresponding to this <tt>Sort</tt>
     */
    public Decoded decoded () // throws LockedCodeArrayException
    {
      // if (_decoded == null)
      // 	throw new LockedCodeArrayException("Attempt to access a sort's non-initialized Decoded form");

      return _decoded;
    }

    /**
     * <a name="setDecoded"></a>
     * Sets this sort's <tt>Decoded</tt> information.
     * @param decoded a <tt>Decoded</tt> object
     * @return the argument <tt>Decoded</tt> object
     */
    public Sort setDecoded (Decoded decoded) throws LockedCodeArrayException
    {
      if (decoded == null)
	throw new LockedCodeArrayException("Attempt to set a sort's Decoded form to null");

      _decoded = decoded;

      return this;
    }

    /* ************************************************************************ */

    /**
     * This defines the sort code size for all Boolean computations on,
     * and printing of, sort codes. This is necessary for consistency
     * since only bits within this code size do matter. It is set in the
     * <tt><a href="Taxonomy.html">Taxonomy</a></tt> class
     * upon locking the defined sorts' code array.  This constant is
     * taken into account in Boolean operations (essentially by
     * '<i>not</i>') and code printing methods defined the <tt><a
     * href="BitCode.html">BitCode</a></tt> class.  For all sort codes,
     * all bits of index greater than or equal to this constant are
     * always false. This is also the index of top when it is
     * initialized and installed in the sort code array upon locking it.
     */
    private static int _BITCODESIZE;

    /**
     * Returns the sort-code's size for an encoded taxonomy. Raises a
     * LockedCodeArrayException if the sort code array is not locked.
     */
    public static int codeSize () throws LockedCodeArrayException
    {
      if (!Taxonomy.isLocked())
      	throw new LockedCodeArrayException("Attempt to access code size for a non-encoded taxonomy");

      return _BITCODESIZE;
    }

    /**
     * Sets the sort code size for an encoded taxonomy. Raises a
     * LockedCodeArrayException if the sort code array is locked.
     */
    public static void setCodeSize (int size) throws LockedCodeArrayException
    {
      if (Taxonomy.isLocked())
	throw new LockedCodeArrayException("Cannot change the sort code size for an encoded taxonomy");

      _BITCODESIZE = size;
    }      

    /* ************************************************************************ */

    /**
     * This defines top as a static constant. Note that the index is set
     * to 0.  But this is temporary, as it will updated to its final
     * index when initialized and be stored in the sort code array. Its
     * final index will then be the value of _BITCODESIZE.
     */
    private static Sort _TOP = new Sort(0,Custom.TOP_STRING,new BitCode().lock());

    /**
     * Returns the top sort as a static constant. This is safe only if
     * the taxonomy this is relative to has been encoded.
     */
    public static Sort top ()
    {
      return _TOP;
    }

    /**
     * Return true iff this sort is top.
     */
    public boolean isTop ()
    {
      return this == _TOP;
    }

    /* ************************************************************************ */

    /**
     * This defines bottom as a static constant. Note that the index is
     * set to -1.  This is because it is not stored in the sort code
     * array and therefore, its index is irrelevant. It is the only
     * sort not stored in the code array and whose height is always 0.
     */
    private static Sort _BOTTOM = new Sort(-1,Custom.BOT_STRING,new BitCode().lock()).setHeight(0);

    /**
     * Returns the bottom sort as a static constant.
     */
    public static Sort bottom ()
    {
      return _BOTTOM;
    }

    /**
     * Return true iff this sort is bottom.
     */
    public boolean isBottom ()
    {
      return this == _BOTTOM;
    }

    /* ************************************************************************ */

    /**
     * This contains the sorts that are declared as immediate parents of
     * this sort.
     */
    private HashSet _parents = new HashSet();

    /**
     * Returns the declared parents of this sort.
     */
    public HashSet parents ()
    {
      return _parents;
    }

    /**
     * Adds the specified sort as a parent of this sort, and returns
     * true iff that sort was not already a parent.
     */
    public boolean addParent (Sort s)
    {
      return _parents.add(s);
    }
   
    /**
     * Removes the specified sort as a parent of this sort, and returns
     * true iff that sort was indeed a parent.
     */
    public boolean removeParent (Sort s)
    {
      return _parents.remove(s);
    }
   
    /* ************************************************************************ */

    /**
     * This contains the sorts that are declared as immediate children
     * of this sort.
     */
    private HashSet _children = new HashSet();

    /**
     * Returns the declared children of this sort.
     */
    public HashSet children ()
    {
      return _children;
    }

    /**
     * Adds the specified sort as a child of this sort, and returns
     * true iff that sort was not already a child.
     */
    public boolean addChild (Sort s)
    {
      return _children.add(s);
    }

    /**
     * Removes the specified sort as a child of this sort, and returns
     * true iff that sort was indeed a child.
     */
    public boolean removeChild (Sort s)
    {
      return _children.remove(s);
    }
   
    /* ************************************************************************ */

    boolean minimal = true;
    boolean maximal = true;

    /**
     * Adds the specified sort to the set of parents of this sort, and
     * this sort to the set of children of the specified sort. Returns
     * true if neither was there before (i.e., already declared to be
     * so). This also maintains the correct set for the parents of
     * bottom (i.e., the minimal sorts) and the children of top (i.e.,
     * the maximal sorts).
     */
    public boolean addIsaDeclaration (Sort sort)
    {
      boolean b1 = addParent(sort);
      boolean b2 = sort.addChild(this);      
      
      if (minimal)
	{
	  _BOTTOM.addParent(this);
	  this.addChild(_BOTTOM);
	}

      if (maximal)
	{
	  maximal = false;
	  _TOP.removeChild(this);
	  this.removeParent(_TOP);
	}

      if (sort.maximal)
	{
	  _TOP.addChild(sort);
	  sort.addParent(_TOP);
	}

      if (sort.minimal)
	{
	  sort.minimal = false;
	  _BOTTOM.removeParent(sort);
	  sort.removeChild(_BOTTOM);
	}

      return b1 && b2;
    }

    /* ************************************************************************ */

    /**
     * The height of this Sort in its taxonomy. (See the <a
     * href="../specs/basic.html#height">specification</a>.)
     */
    private int _height = -1;

    /**
     * Returns the height of this Sort in its taxonomy. (See the <a
     * href="../specs/basic.html#height">specification</a>.)
     */
    public int height () throws LockedCodeArrayException
    {
      if (!Taxonomy.isLocked())
	throw new LockedCodeArrayException("Can't compute sort heights in a non-encoded taxonomy");
      
      if (_height != -1)
	return _height;

      return _height = _context.taxonomy().computeHeight(this);
    }

    public Sort setHeight (int height)
    {
      _height = height;
      return this;
    }

    public void resetHeight ()
    {
      _height = -1;
    }

    /* ************************************************************************ */

    /**
     * Returns the number of subsorts of this sort (not including itself).
     */
    public int numberOfDescendants ()
    {
      return _bitcode.cardinality()-1;
    }

    /* ************************************************************************ */

    /**
     * Returns the set of sorts that are descendants of this sort as a <tt>HashSet</tt>.
     */
    public HashSet descendants (Taxonomy taxonomy) throws LockedCodeArrayException
    {
      return decoded().descendants(taxonomy);
    }

    /* ************************************************************************ */

    // /**
    //  * Returns the number of strict supersorts of this sort.
    //  */
    // int numberOfAncestors (Sort sort)
    // {
    //   return ancestors().size();
    // }

    /**
     * Returns the set of sorts that are ancestors of this sort as a <tt>HashSet</tt>.
     */
    public HashSet ancestors (Taxonomy taxonomy) throws LockedCodeArrayException
    {
      //      System.err.println(">>> Decoded = "+decoded());
      return decoded().ancestors(taxonomy);
    }

    /* ************************************************************************ */

    /**
     * BOOKKEEPING UTILITIES
     */

    /* ************************************************************************ */

    /**
     * Returns true iff this is a strict lower sort of the specified
     * sort. This is used by the <tt>hlt.language.tools.Misc.sort</tt>
     * method used in class <tt>Taxonomy</tt> to sort the declared
     * sorts.
     */
    public boolean lessThan (Comparable sort)
    {
      return precedes((Sort)sort);
    }

    /**
     *  This defines the "precedes" ordering as described in the <a
     *  href="../specs/basic.html#precedes">specification</a>.  This is a
     *  topological ordering that respects the is-a ordering to ease the
     *  detection of potential cycles. A cycle is a set of sorts with
     *  equal codes after transitive closure has been performed. Using
     *  this "precedes" comparison to reorder the array will make all
     *  elements in a cycle be contiguous.
     */
    public boolean precedes (Sort sort)
    {
      if (_bitcode.isStrictlyContainedIn(sort.bitcode()))	// this is a proper subsort of sort
	return true;

      if (_bitcode.equals(sort.bitcode()))	// respect the index ordering for equal codes
	return _index < sort.index();

      if (!sort.bitcode().isContainedIn(_bitcode))	// for unrelated sorts
	{
	  int thisCodeSize = _bitcode.cardinality();
	  int sortCodeSize = sort.bitcode().cardinality();

	  if (thisCodeSize == sortCodeSize)
	    { // So here, the two codes have same cardinality: the "least"
	      // code is the one with the least differring true bit
	      int i = _bitcode.nextSetBit(0);
	      int j = sort.bitcode().nextSetBit(0);
	      while (i == j)
		{
		  i = _bitcode.nextSetBit(i+1);
		  j = sort.bitcode().nextSetBit(j+1);
		}
	      return (i < j);
	    }
	  else
	    return (thisCodeSize < sortCodeSize);
	}

      return false;      
    }

    /**
     * Returns true iff this sort's name is equal to the specified one's.
     */
    public boolean equals (Sort sort)
    {
      return _name == sort.name();
    }

    /**
     * Locks this sort's bit code - which means that its code will not
     * be modified by the 3 Boolean static bit code operations 'not',
     * 'and', and 'or'.
     */
    public Sort lock ()
    {
      _bitcode.lock();
      return this;
    }

    /**
     * Unlocks this sort's bit code - which means that its code may be
     * modified by the 3 Boolean static bit code operations 'not',
     * 'and', and 'or'.
     */
    public Sort unlock ()
    {
      _bitcode.unlock();
      return this;
    }

    /**
     * Returns true iff this sort's bit code is locked - which means
     * that its bit code will not be modified by the 3 Boolean static
     * operations 'not', 'and', and 'or'.
     */
    public boolean isLocked ()
    {
      return _bitcode.isLocked();
    }
    
    /**
     * Returns a string form of this Sort object. This string is its
     * name.
     */
    public String toString ()
    {
      return _name;
    }
    
  }
