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

/**
 * A <tt>Decoded</tt> object gathers information associating a binary
 * code to sorts.  It consists essentially three parts: (1) a sort (if
 * the code corresponds to one), (2) the set of sorts that have a code
 * immediately greater that the given code, and (3) the set of sorts
 * that have a code immediately lower that the given code.
 *
 * @version     Last modified on Tue Jan 21 08:08:31 2014 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.language.util.IntIterator;

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

public class Decoded
{
  /**
   * This is the code of this <tt>Decoded</tt> object.
   */
  private BitCode _code;

  /**
   * 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 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 maximal lower bounds of
   * this <tt>Decoded</tt> object's code. If the code is that of the top
   * sort, it contains all the maximal of the taxonomy. If the code is
   * that of the bottom sort, it is <tt>null</tt>.
   */
  private HashSet _mlbs;

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

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

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

  /**
   * Constructs a <tt>Decoded</tt> object for the specified code, least
   * upper bounds, and maximal 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
   * initialization time).
   */
  public Decoded (BitCode code, HashSet lubs, HashSet mlbs)
  {
    _code = code;
    _lubs = lubs;
    _mlbs = mlbs;
  }

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

  /**
   * Constructs a <tt>Decoded</tt> object for the specified code. 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 initialization time). <b><font color="red">This is now
   * rendered obsolete by the <tt>Decoded(BitCode, HashSet, HashSet)</tt>
   * constructor.</font></b>
   */
  public Decoded (BitCode code, Taxonomy taxonomy)
  {
    _code = code;
    _lubs = taxonomy.minUpperBounds(code).toHashSet(taxonomy);
    _mlbs = taxonomy.maxLowerBounds(code).toHashSet(taxonomy);
  }

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

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

  /**
   * Returns this <tt>Decoded</tt> object's ancestors in the specified
   * taxonomy as a <tt>HashSet</tt>.
   */
  public HashSet ancestors (Taxonomy taxonomy)
  {
    if (_ancestors != null)
      return _ancestors;

    if (_sort != null)
      return (_ancestors = _sort.ancestors());

    return (_ancestors = _computeAncestors(taxonomy));
  }

  /**
   * Computes and returns this <tt>Decoded</tt> object's descendants in
   * the specified taxonomy as a <tt>HashSet</tt>.
   */
  private HashSet _computeAncestors (Taxonomy taxonomy)
  {
    HashSet ancestors = new HashSet();

    // iterate through the 0s of the code:
    for (IntIterator it = BitCode.not(_code).iterator(); it.hasNext();)
      {
	Sort sort = taxonomy.getSort(it.next());
	if (_code.isContainedIn(sort.code))
	  ancestors.add(sort);
      }

    return ancestors;      
  }

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

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

  /**
   * Returns this <tt>Decoded</tt> object's descendants in the specified
   * taxonomy as a <tt>HashSet</tt>.
   */
  public HashSet descendants (Taxonomy taxonomy)
  {
    if (_descendants != null)
      return _descendants;

    if (_sort != null)
      return (_descendants = _sort.descendants());

    return (_descendants = _computeDescendants(taxonomy));
  }

  /**
   * Computes and returns this <tt>Decoded</tt> object's descendants in
   * the specified taxonomy as a <tt>HashSet</tt>.
   */
  private HashSet _computeDescendants (Taxonomy taxonomy)
  {
    HashSet descendants = new HashSet();
    
    // iterate through the 1s of the code:
    for (IntIterator it = _code.iterator(); it.hasNext();)
      {
	Sort sort = taxonomy.getSort(it.next());
	if (sort.code.isContainedIn(_code))
	  descendants.add(sort);
      }

    return descendants;      
  }

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

  /**
   * 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 code ()
  {
    return _code;
  }

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

  /**
   * 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;
  }

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

  public String toString ()
  {
    String upper = _lubs == null ? (_sort==null?"*EVERYTHING*":"") : _enum(_lubs); // _lubs.toString();
    String sort  = _sort == null ? "" : _sort.toString();
    String lower = _mlbs == null ? (_sort==null?"*NOTHING*":"") : _enum(_mlbs); // _mlbs.toString();

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

  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;
  }

  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();
  }

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

