// FILE. . . . . /home/hak/hlt/src/hlt/osfv3/util/Decoded.java
// EDIT BY . . . Hassan Ait-Kaci
// ON MACHINE. . Hak-Laptop
// STARTED ON. . Mon Sep 02 15:38:04 2013

/**
 * @version     Last modified on Wed May 01 05:48:09 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.util;

import hlt.osf.base.Sort;
import hlt.osf.util.BitCode;
import hlt.osf.exec.Taxonomy;
import hlt.osf.exec.LockedCodeArrayException;

import hlt.language.util.IntIterator;

import java.util.HashSet;
import java.util.Iterator;

/**
 * A <tt>Decoded</tt> object gathers information associating a binary
 * code to sorts.  It consists essentially of three parts:
 * <ol>
 * <li>a sort (if the code corresponds to one);</li>
 * <li>the set of sorts that have a code immediately greater than its
 *     code (<i>i.e.</i>, the LUBs); and,</li>
 * <li>the set of sorts that have a code immediately lower than its
 *     code (<i>i.e.</i>, the GLBs).</li>
 * </ol>
 *
 * <p>
 *
 * It is also the cache-repository of its code's ancestors, and
 * descendants. That is, these are computed only if accessed, and then
 * cached as appropriate attributes of the <tt>Decoded</tt> object.
 *
 * <p>
 *
 * A unique <tt>new Decoded(Sort sort)</tt> is created and associated
 * with each sort at <tt>Taxonomy</tt> initialization time (more
 * precisely, right after sort encoding when committing its
 * corresponding <tt>Sort</tt>).
 */
public class Decoded
{
  /**
   * This is the binary code decoded by this <tt>Decoded</tt> object.
   */
  private BitCode _bitcode;

  /**
   * If the code of this <tt>Decoded</tt> object is that of a sort, this
   * is the sort. Otherwise, it is <tt>null</tt>.
   */
  private Sort _sort;

  /**
   * This is the set of sorts whose codes are least upper bounds of this
   * <tt>Decoded</tt> object's code. If the code is that of a sort, it
   * the set of that sort's parents. If the code is that of the bottom
   * sort, it contains all the minimal sorts of the taxonomy. If the
   * code is that of the top sort, it is <tt>null</tt>.
   */
  private HashSet _lubs;

  /**
   * This is the set of sorts whose codes are greatest lower bounds of
   * this <tt>Decoded</tt> object's code. If the code is that of a sort,
   * it the set of that sort's children. If the code is that of the top
   * sort, it contains all the non-top maximal sorts of the taxonomy. If
   * the code is that of the bottom sort, it is <tt>null</tt>.
   */
  private HashSet _glbs;

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

  /**
   * Constructs a <tt>Decoded</tt> object for the given sort. This is
   * used only for creating <tt>Decoded</tt> forms for declared sorts
   * (as well as top and bottom) to store in a context's code cache at
   * initialization time.
   */
  public Decoded (Sort sort)
    {
      _sort = sort;
      _bitcode = sort.bitcode().setDecoded(this);
      if (!sort.isTop())
	_lubs = sort.parents();
      if (!sort.isBottom())
	_glbs = sort.children();
    }

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

  /**
   * Constructs a <tt>Decoded</tt> object for the specified code, least
   * upper bounds, and greatest lower bounds. This is used only for a
   * code that does not correspond to a sort, such a code's decoded
   * value being always found in the code cache (since a
   * <tt>Decoded</tt> object for a sort is saved in the code cache by
   * the context at taxonomy initialization time).
   */
  public Decoded (BitCode code, HashSet lubs, HashSet glbs)
  {
    _bitcode = code.setDecoded(this);
    _lubs = lubs;
    _glbs = glbs;
  }

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

  /**
   * Cached value for this <tt>Decoded</tt> object's descendants.
   */
  private HashSet _descendants;

  /**
   * Returns a <tt>HashSet</tt> containing all the sorts in the
   * specified taxonomy that are strict descendants of this
   * <tt>Decoded</tt> object.
   *
   * @param taxonomy
   * @return a non-empty <tt>HashSet</tt> or <tt>null</tt>
   */
  public HashSet descendants (Taxonomy taxonomy) throws LockedCodeArrayException
  {
    if (!taxonomy.isLocked())
      throw new LockedCodeArrayException("Attempt to perform an operation requiring prior sort encoding");

    // if it was previously computed, return that cached set
    if (_descendants != null)
      return _descendants;

    // if the sort corresponds to bottom return null
    if (isSort() && _sort.isBottom())
      return null;

    // the descendants are the sorts at 1 indices in the bitcode
    _descendants = _bitcode.toHashSet(taxonomy);

    // if the bitcode is that of a sort, this sort must be removed from
    // _descendants
    if (isSort())
      _descendants.remove(_sort);

    return _descendants;
  }

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

  /**
   * Cached value for this <tt>Decoded</tt> object's ancestors.
   */
  private HashSet _ancestors;

  /**
   * Returns a <tt>HashSet</tt> containing all the sorts in the
   * specified taxonomy that are strict ancestors of this
   * <tt>Decoded</tt> object.
   */
  public HashSet ancestors (Taxonomy taxonomy)
  {
    if (!taxonomy.isLocked())
      throw new LockedCodeArrayException("Attempt to perform an operation requiring prior sort encoding");

    // if it was previously computed, return that cached set
    if (_ancestors != null)
      return _ancestors;

    // if the sort corresponds to top return a vacuous set
    if (isSort() && _sort.isTop())
      return (_ancestors = new HashSet(0));

    // allocate a hash set
    _ancestors = new HashSet(_bitcode.size());

    // iterate through the 0s of _bitcode - i.e., through the indices of
    // the sorts whose codes are not subsumed by _bitcode (i.e., those
    // sorts that are its ancestors or are unrelated to it), keeping
    // only those sorts whose codes actually do subsume _bitcode (i.e.,
    // only those sorts that are its ancestors).
    for (IntIterator it = _bitcode.zeroIterator(); it.hasNext();)
      {
	Sort sort = taxonomy.getSort(it.next());
	if (_bitcode.isContainedIn(sort.bitcode()))
	  _ancestors.add(sort);
      }

    return _ancestors;
  }

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

  /**
   * This is a unique <tt>Decoded</tt> object corresponding to bottom.
   */
  private static Decoded _bottom = new Decoded(Sort.bottom());

  /**
   * Returns the a unique <tt>Decoded</tt> object corresponding to bottom.
   */
  public static Decoded bottom ()
  {
    return _bottom;
  }

  /**
   * This is a unique <tt>Decoded</tt> object corresponding to top.
   */
  private static Decoded _top = new Decoded(Sort.top());

  /**
   * Returns the a unique <tt>Decoded</tt> object corresponding to top.
   */
  public static Decoded top ()
  {
    return _top;
  }

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

  /**
   * Returns <tt>true</tt> iff this <tt>Decoded</tt> object corresponds
   * to a sort symbol.
   */
  public boolean isSort ()
  {
    return _sort != null;
  }

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

  /**
   * This returns the sort of this <tt>Decoded</tt> object, (which may
   * be <tt>null</tt> if this object corresponds to no sort).
   */
  public Sort sort ()
  {
    return _sort;
  }

  /**
   * This returns the code of this <tt>Decoded</tt> object.
   */
  public BitCode bitcode ()
  {
    return _bitcode;
  }

  /**
   * This returns the set of sorts whose code is a greatest lower bound of
   * this <tt>Decoded</tt> object's code, or <tt>null</tt> if it
   * corresponds to the bottom sort.
   */
  public HashSet glbs ()
  {
    return _glbs;
  }

  /**
   * This returns the set of sorts whose code is a least upper bound of
   * this <tt>Decoded</tt> object's code, or <tt>null</tt> if it
   * corresponds to the top sort.
   */
  public HashSet lubs ()
  {
    return _lubs;
  }

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

  /**
   * Returns a string form for this <tt>Decoded</tt> object indicating
   * its least supersorts, its sort (if it corresponds to any), and its
   * greatest subsorts.
   */
  public String toString ()
  {
    String upper = _lubs == null ? (_sort==null?"*EVERYTHING*":"") : _enum(_lubs); // _lubs.toString();
    String sort  = _sort == null ? "" : _sort.toString();
    String lower = _glbs == null ? (_sort==null?"*NOTHING*":"") : _enum(_glbs); // _glbs.toString();

    return "\n"
         + "LEAST SUPERSORTS:  "+upper+"\n"
         + "EQUIVALENT SORT:   "+sort +"\n"
         + "GREATEST SUBSORTS: "+lower+"\n";
  }


  /**
   * This controls the number of enumerated elements actually written
   * out for large-size sets of sorts (<i>i.e.</i>, big disjunctions).
   */
  private static int _ENUM_SIZE = 10;

  public static int enumSize ()
  {
    return _ENUM_SIZE;
  }

  public static int resetEnumSize ()
  {
    return _ENUM_SIZE = 10;
  }

  public static int setEnumSize (int size)
  {
    return _ENUM_SIZE = size;
  }

  /**
   * Returns a string enumeration of the specified hash set up to
   * _ENUM_SIZE followed by "..." and an indication of the total number
   * in the set.
   */
  private String _enum (HashSet set)
  {
    if (set.isEmpty())
      return Sort.bottom().toString();
    
    StringBuffer buf = new StringBuffer();

    int size = 0;

    for (Iterator it=set.iterator(); it.hasNext();)
      {
	buf.append(it.next().toString());

	size++;

	if (size == _ENUM_SIZE)
	  {
	    buf.append(" ... ("+set.size()+" elements in total)");
	    break;
	  }

	if (it.hasNext())
	  buf.append(" ");
      }

    return buf.toString();
  }

  /* ************************************************************************ */
}

