// Data type base class -*- c++ -*-

#ifdef __GNUC__
# pragma implementation
#endif // __GNUC__
#include "UnionType.h"
#include "Constraint.h"
#include "Range.h"
#include "Value.h"
#include "Printer.h"

/** @file Type.C
 * Abstract base class for data types
 */

/* Copyright © 1998-2003 Marko Mäkelä (msmakela@tcs.hut.fi).

   This file is part of MARIA, a reachability analyzer and model checker
   for high-level Petri nets.

   MARIA 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, or (at your option)
   any later version.

   MARIA 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.

   The GNU General Public License is often shipped with GNU software, and
   is generally kept in a file called COPYING or LICENSE.  If you do not
   have a copy of the license, write to the Free Software Foundation,
   59 Temple Place, Suite 330, Boston, MA 02111 USA. */

Type::Type () :
  myNumValues (0), myName (0), myIsOrdered (true), myConstraint (0)
#ifdef EXPR_COMPILE
  , myIndex (0), myGenerated (false), myFather (0)
#endif // EXPR_COMPILE
{
}

Type::Type (const class Type& old) :
  myNumValues (0), myName (0), myIsOrdered (old.myIsOrdered),
  myConstraint (old.myConstraint
		? new class Constraint (*old.myConstraint, *this)
		: 0)
#ifdef EXPR_COMPILE
  , myIndex (old.myIndex), myGenerated (old.myGenerated), myFather (&old)
#endif // EXPR_COMPILE
{
}

Type::~Type ()
{
  delete[] myName;
  delete myConstraint;
}

const char*
Type::getSyntacticName () const
{
  switch (getKind ()) {
  case tInt:
    return "int";
  case tCard:
    return "unsigned";
  case tBool:
    return "bool";
  case tChar:
    return "char";
  case tEnum:
    return "enum";
  case tId:
    return "id";
  case tStruct:
    return "struct";
  case tVector:
    return "vector";
  case tUnion:
    return "union";
  case tBuffer:
    return "buffer";
  }
  return "";
}

bool
Type::isAssignable (const class Type& type) const
{
  if (type.getKind () == getKind ())
    return
      (!type.getConstraint () || !getConstraint () ||
       !type.getConstraint ()->isDisjoint (*getConstraint ()));
  else
    return type.getKind () == tUnion &&
      static_cast<const class UnionType&>(type).isAssignableFrom (*this);
}

bool
Type::isAlwaysAssignable (const class Type& type) const
{
  if (type.getKind () == getKind ())
    return isAssignable (type) &&
      (!type.getConstraint () ||
       (getConstraint () &&
	type.getConstraint ()->isSuperset (*getConstraint ())));
  else
    return type.getKind () == tUnion &&
      static_cast<const class UnionType&>(type).isAlwaysAssignableFrom (*this);
}

bool
Type::isConstrained (const class Value& value) const
{
  assert (value.getType ().isAssignable (*this));
  return !getConstraint () || getConstraint ()->isConstrained (value);
}

card_t
Type::getNumConstrainedValues () const
{
  assert (!!myConstraint);
  return myConstraint->getNumValues ();
}

void
Type::setConstraint (class Constraint& constraint)
{
  assert (!myConstraint && !constraint.empty ());
  (myConstraint = &constraint)->setType (*this);
}

card_t
Type::convert (const class Value& value) const
{
  assert (myConstraint && !myConstraint->empty ());
  assert (myNumValues && myNumValues < CARD_T_MAX);
  card_t number = 0;
  for (Constraint::const_iterator i = myConstraint->begin ();
       i != myConstraint->end (); i++) {
    const class Range* r = *i;
    const class Value& low = r->getLow ();
    const class Value& high = r->getHigh ();

    assert (!(high < low));
    if (high < value)
      number += high - low + 1;
    else {
      number += value - low;
      break;
    }
  }

  assert (number < myNumValues);
  return number;
}

class Value*
Type::convert (card_t number) const
{
  assert (myConstraint && !myConstraint->empty ());
  assert (number < myNumValues);
  class Value* value = NULL;
  for (Constraint::const_iterator i = myConstraint->begin ();
       i != myConstraint->end (); i++) {
    const class Range* r = *i;
    const class Value& low = r->getLow ();
    const class Value& high = r->getHigh ();

    assert (!(high < low));
    card_t diff = high - low + 1;
    if (number >= diff)
      number -= diff;
    else {
      value = low.copy ();
      while (number--)
	if (!value->increment ())
	  assert (false);
      break;
    }
  }
  assert (isConstrained (*value));
  return value;
}

#ifdef EXPR_COMPILE
# include "Net.h"
# include "Range.h"
# include "CExpression.h"
# include "util.h"
# include <stdio.h>

void
Type::uncompile ()
{
  myIndex = 0; myGenerated = false;
  if (myFather)
    const_cast<class Type*>(myFather)->uncompile ();
}

bool
Type::isGenerated ()
{
  assert (!myFather || !myGenerated);
  if (myFather)
    return const_cast<class Type*>(myFather)->isGenerated ();
  bool g = myGenerated;
  myGenerated = true;
  return g;
}

/** Number of compiled types */
static unsigned numCompiled = 0;
void Type::uncompileAll () { numCompiled = 0; }

void
Type::compile (class StringBuffer& out)
{
  if (myIndex)
    return;
  else if (myFather) {
    const_cast<class Type*>(myFather)->compile (out);
    myIndex = myFather->myIndex;
    assert (myIndex > 0);
    return;
  }
  myIndex = ++numCompiled;

  /** add a constraint for an enumeration if there is none yet */
  if (!myConstraint && getKind () == tEnum) {
    class Constraint* c = new class Constraint ();
    class Value* low = &getFirstValue ();
    class Value* high = getNumValues () == 1 ? low : &getLastValue ();
    c->append (*new class Range (*low, *high));
    setConstraint (*c);
  }

  out.append ("#define ID(id) id##");
  out.append (myIndex);
  out.append ("\ntypedef ");
  compileDefinition (out, 0);
  out.append (" TYPE;\nDECLARE_MULTISET_OPS;\n#undef ID\n");
  compileExtraDefinitions (out, 0, true);
}

void
Type::compileExtraDefinitions (class StringBuffer& out,
			       unsigned,
			       bool interface) const
{
  if (interface) return;
  // three-way comparison
  out.append ("#define CMP3(l,r,L,G)");
  compileCompare3 (out);
  // equality comparison
  out.append ("\n#define EQ(l,r) ");
  if (!compileEqual (out, 16, "l", "r", true, true, true, true))
    out.append ("1");
  out.append ("\n");
}

bool
Type::compileEqual (class StringBuffer& out,
		    unsigned indent,
		    const char* left,
		    const char* right,
		    bool equal,
		    bool first,
		    bool last,
		    bool backslash) const
{
  assert (isLeaf ());
  if (!first)
    out.indent (indent);
  out.append (left);
  out.append (equal ? "==" : "!=");
  out.append (right);
  if (last);
  else if (backslash)
    out.append (equal ? "&&\\\n" : "||\\\n");
  else
    out.append (equal ? "&&\n" : "||\n");
  return true;
}

void
Type::compileCompare3 (class StringBuffer& out,
		       const char* condition,
		       const char* component) const
{
  assert (isLeaf ());
  compileLeafCompare3 (out, condition, component);
}

void
Type::compileLeafCompare3 (class StringBuffer& out,
			   const char* condition,
			   const char* component)
{
  out.append ("\\\n  ");

  if (condition) {
    out.append ("CC3(");
    out.append (condition);
    out.append (",l,r,");
    out.append (component);
    out.append (",L,G)");
  }
  else {
    out.append ("C3(l,r,");
    out.append (component);
    out.append (",L,G)");
  }
}

void
Type::compileSuccessor (class StringBuffer& out,
			unsigned indent,
			const char* lvalue,
			const char* rvalue,
			const char* wrap) const
{
  if (isLeaf ())
    do_compileSuccessor (out, indent, lvalue, rvalue, wrap);
  else if (getNumValues () == 1) {
    if (lvalue != rvalue && strcmp (lvalue, rvalue)) {
      out.indent (indent);
      out.append (lvalue);
      out.append ("=");
      out.append (rvalue);
      out.append (";\n");
    }
    if (wrap)
      out.indent (indent), out.append (wrap), out.append ("=1;\n");
  }
  else {
    out.indent (indent);
    out.append ("if (");
    class Value* max = &getLastValue ();
    if (!max->compileEqual (out, indent + 4, rvalue, true, true, true))
      assert (false);
    delete max;
    out.append (") {\n");
    compileBottom (out, indent + 2, lvalue);
    if (wrap) {
      out.indent (indent + 2);
      out.append (wrap);
      out.append ("=1;\n");
    }
    out.indent (indent);
    out.append ("}\n");

    if (myConstraint) {
      for (Constraint::const_iterator i = myConstraint->begin ();
	   ++i != myConstraint->end (); ) {
	i--;
	out.indent (indent);
	out.append ("else if (");
	if (!(*i)->getHigh ().compileEqual (out, indent + 9, rvalue,
					    true, true, true))
	  assert (false);
	out.append (") {\n");
	i++;
	(*i)->getLow ().compileInit (lvalue, indent + 2, out);
	out.indent (indent);
	out.append ("}\n");
      }
    }

    out.indent (indent);
    out.append ("else {\n");
    do_compileSuccessor (out, indent + 2, lvalue, rvalue, wrap);
    out.indent (indent);
    out.append ("}\n");
  }
}

void
Type::compilePredecessor (class StringBuffer& out,
			  unsigned indent,
			  const char* lvalue,
			  const char* rvalue,
			  const char* wrap) const
{
  if (isLeaf ())
    do_compilePredecessor (out, indent, lvalue, rvalue, wrap);
  else if (getNumValues () == 1) {
    if (lvalue != rvalue && strcmp (lvalue, rvalue)) {
      out.indent (indent);
      out.append (lvalue);
      out.append ("=");
      out.append (rvalue);
      out.append (";\n");
    }
    if (wrap)
      out.indent (indent), out.append (wrap), out.append ("=1;\n");
  }
  else {
    out.indent (indent);
    out.append ("if (");
    class Value* min = &getFirstValue ();
    if (!min->compileEqual (out, indent + 4, rvalue, true, true, true))
      assert (false);
    delete min;
    out.append (") {\n");
    compileTop (out, indent + 2, lvalue);
    if (wrap) {
      out.indent (indent + 2);
      out.append (wrap);
      out.append ("=1;\n");
    }
    out.indent (indent);
    out.append ("}\n");

    if (myConstraint) {
      for (Constraint::const_iterator i = myConstraint->begin ();
	   ++i != myConstraint->end (); ) {
	out.indent (indent);
	out.append ("else if (");
	if (!(*i)->getLow ().compileEqual (out, indent + 9, rvalue,
					   true, true, true))
	  assert (false);
	out.append (") {\n");
	/** iterator to the previous range */
	Constraint::const_iterator j = i; j--;
	(*j)->getHigh ().compileInit (lvalue, indent + 2, out);
	out.indent (indent);
	out.append ("}\n");
      }
    }

    out.indent (indent);
    out.append ("else {\n");
    do_compilePredecessor (out, indent + 2, lvalue, rvalue, wrap);
    out.indent (indent);
    out.append ("}\n");
  }
}

void
Type::do_compileSuccessor (class StringBuffer& out,
			   unsigned indent,
			   const char* lvalue,
			   const char* rvalue,
			   const char* wrap) const
{
  assert (isLeaf ());
  out.indent (indent);
  out.append ("switch (");
  out.append (rvalue);
  out.append (") {\n");

  if (myConstraint) {
    for (Constraint::const_iterator i = myConstraint->begin ();
	 i != myConstraint->end (); ) {
      out.indent (indent);
      out.append ("case ");
      (*i)->getHigh ().compile (out);
      out.append (":\n");
      i++;
      if (i != myConstraint->end ())
	(*i)->getLow ().compileInit (lvalue, indent + 2, out);
      else {
	if (wrap) {
	  out.indent (indent + 2);
	  out.append (wrap);
	  out.append ("=1;\n");
	}
	compileBottom (out, indent + 2, lvalue);
      }
      out.indent (indent + 2);
      out.append ("break;\n");
    }
  }
  else {
    out.indent (indent);
    out.append ("case ");
    class Value* max = &getLastValue ();
    max->compile (out);
    delete max;
    out.append (":\n");
    if (wrap) {
      out.indent (indent + 2);
      out.append (wrap);
      out.append ("=1;\n");
    }
    compileBottom (out, indent + 2, lvalue);
    out.indent (indent + 2);
    out.append ("break;\n");
  }

  out.indent (indent);
  out.append ("default:\n");
  out.indent (indent + 2);
  out.append (lvalue);
  out.append ("=");
  out.append (rvalue);
  out.append ("+1;\n");

  out.indent (indent);
  out.append ("}\n");
}

void
Type::do_compilePredecessor (class StringBuffer& out,
			     unsigned indent,
			     const char* lvalue,
			     const char* rvalue,
			     const char* wrap) const
{
  assert (isLeaf ());
  out.indent (indent);
  out.append ("switch (");
  out.append (rvalue);
  out.append (") {\n");

  if (myConstraint) {
    for (Constraint::const_iterator i = myConstraint->end ();
	 i != myConstraint->begin (); ) {
      const class Value* high =
	i == myConstraint->end () ? NULL : &(*i)->getHigh ();
      i--;
      out.indent (indent);
      out.append ("case ");
      (*i)->getLow ().compile (out);
      out.append (":\n");
      if (!high && wrap) {
	out.indent (indent + 2);
	out.append (wrap);
	out.append ("=1;\n");
      }
      if (high)
	high->compileInit (lvalue, indent + 2, out);
      else
	compileTop (out, indent + 2, lvalue);
      out.indent (indent + 2);
      out.append ("break;\n");
    }
  }
  else {
    out.indent (indent);
    out.append ("case ");
    class Value* min = &getFirstValue ();
    min->compile (out);
    delete min;
    out.append (":\n");
    if (wrap) {
      out.indent (indent + 2);
      out.append (wrap);
      out.append ("=1;\n");
    }
    compileTop (out, indent + 2, lvalue);
    out.indent (indent + 2);
    out.append ("break;\n");
  }

  out.indent (indent);
  out.append ("default:\n");
  out.indent (indent + 2);
  out.append (lvalue);
  out.append ("=");
  out.append (rvalue);
  out.append ("-1;\n");

  out.indent (indent);
  out.append ("}\n");
}

void
Type::compileBottom (class StringBuffer& out,
		     unsigned indent,
		     const char* value) const
{
  class Value* v = &getFirstValue ();
  v->compileInit (value, indent, out);
  delete v;
}

void
Type::compileTop (class StringBuffer& out,
		  unsigned indent,
		  const char* value) const
{
  class Value* v = &getLastValue ();
  v->compileInit (value, indent, out);
  delete v;
}

void
Type::appendIndex (class StringBuffer& out) const
{
  out.append (myIndex);
}

void
Type::compileCast (class CExpression& cexpr,
		   unsigned indent,
		   const class Type& target,
		   const char* lvalue,
		   const char* rvalue) const
{
  assert (lvalue && rvalue);
  if (target.getKind () == tUnion)
    static_cast<const class UnionType&>(target).compileCastFrom
      (cexpr, indent, *this, lvalue, rvalue);
  else {
    assert (isLeaf ());
    class StringBuffer& out = cexpr.getOut ();
    out.indent (indent);
    out.append (lvalue);
    out.append ("=(");
    target.appendName (out);
    out.append (")");
    out.append (rvalue);
    out.append (";\n");
    if (target.myConstraint)
      target.myConstraint->compileCheck (cexpr, indent, lvalue);
  }
}

void
Type::compileConversion (class StringBuffer& out,
			 unsigned indent,
			 const char* value,
			 const char* number,
			 bool add) const
{
  if (getNumValues () == 1) {
    if (!add) {
      out.indent (indent);
      out.append (number);
      out.append ("=0;\n");
    }
  }
  else if (myConstraint) {
    if (isLeaf ())
      myConstraint->compileConversion (out, indent, *this, value,
				       number, add, 0);
    else {
      out.indent (indent), out.append ("{\n");
      out.indent (indent + 2), appendName (out), out.append (" w;\n");
      myConstraint->compileConversion (out, indent + 2, *this, value,
				       number, add, "w");
      out.indent (indent), out.append ("}\n");
    }
  }
  else
    do_compileConversion (out, indent, value, number, add);
}

void
Type::compileReverseConversion (class StringBuffer& out,
				unsigned indent,
				const char* number,
				const char* value) const
{
  assert (isLeaf () || myConstraint);
  if (myConstraint)
    myConstraint->compileReverseConversion (out, indent, *this, number,
					    value, isLeaf ());
  else {
    out.indent (indent);
    out.append (value);
    out.append ("=");
    if (getKind () == tInt)
      out.append ("(signed) ");
    out.append (number);
    out.append (";\n");
  }
}

void
Type::compileEncoder (class CExpression& cexpr,
		      unsigned indent,
		      const char* func,
		      const char* value) const
{
  assert (isLeaf () || getNumValues () < CARD_T_MAX);
  if (getNumValues () > 1) {
    class StringBuffer& out = cexpr.getOut ();
    const char* number = cexpr.getVarCount ();
    compileConversion (out, indent, value, number, false);
    out.indent (indent);
    out.append (func);
    out.append (" (");
    out.append (number);
    out.append (", ");
    out.append (log2 (getNumValues ()));
    out.append (");\n");
  }
}

void
Type::compileDecoder (class CExpression& cexpr,
		      unsigned indent,
		      const char* func,
		      const char* value) const
{
  assert (isLeaf () || getNumValues () < CARD_T_MAX);
  if (getNumValues () > 1) {
    class StringBuffer& out = cexpr.getOut ();
    const char* number = cexpr.getVarCount ();
    out.indent (indent);
    out.append (number);
    out.append (" = ");
    out.append (func);
    out.append (" (");
    out.append (log2 (getNumValues ()));
    out.append (");\n");
    compileReverseConversion (out, indent, number, value);
  }
  else {
    class Value* min = &getFirstValue ();
    min->compileInit (value, indent, cexpr.getOut ());
    delete min;
  }
}

void
Type::appendName (class StringBuffer& out) const
{
  if (myIndex) {
    out.append ("t");
    out.append (myIndex);
  }
  else {
    assert (isLeaf () && !myConstraint);
    switch (getKind ()) {
    case tInt:
      out.append ("signed");
      return;
    case tBool:
      out.append ("bool_t");
      return;
    case tChar:
      out.append ("unsigned char");
      return;
    case tCard:
    case tEnum:
    case tId:
      out.append ("unsigned");
      return;
    case tStruct:
    case tVector:
    case tUnion:
    case tBuffer:
      assert (false);
    }
  }
}

void
Type::appendMSetName (class StringBuffer& out) const
{
  assert (myIndex);
  out.append ("struct tree");
  out.append (myIndex);
}

#endif // EXPR_COMPILE

void
Type::display (const class Printer& printer) const
{
  printer.printRaw (getSyntacticName ());
  if (myConstraint)
    myConstraint->display (printer);
}
