// relation operator class -*- c++ -*-

#ifdef __GNUC__
# pragma implementation
#endif // __GNUC__
#include "RelopExpression.h"
#include "Net.h"
#include "BoolType.h"
#include "LeafValue.h"
#include "Constant.h"
#include "Printer.h"

/** @file RelopExpression.C
 * Comparison operations
 */

/* Copyright © 1998-2002 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. */

RelopExpression::RelopExpression (bool equal,
				  class Expression& left,
				  class Expression& right) :
  Expression (),
  myEqual (equal), myLeft (&left), myRight (&right)
{
  setType (Net::getBoolType ());
  assert (myLeft->getType () == myRight->getType ());
  assert (myEqual ||
	  (myLeft->getType ()->isOrdered () &&
	   myRight->getType ()->isOrdered ()));
  assert (myLeft->isBasic () && myRight->isBasic ());
}

RelopExpression::~RelopExpression ()
{
  myLeft->destroy ();
  myRight->destroy ();
}

class Expression*
RelopExpression::construct (bool equal,
			    class Expression& left,
			    class Expression& right)
{
  if ((left == right ||
       (left.getType () == right.getType () &&
	left.getType () && left.getType ()->getNumValues () == 1)) &&
      (left.getKind () == Expression::eConstant ||
       left.getKind () == Expression::eVariable) &&
      (right.getKind () == Expression::eConstant ||
       right.getKind () == Expression::eVariable)) {
    left.destroy ();
    right.destroy ();
    return (new class Constant
	    (*new class LeafValue (Net::getBoolType (), equal)))->cse ();
  }

  return (new class RelopExpression (equal, left, right))->cse ();
}

class Value*
RelopExpression::do_eval (const class Valuation& valuation) const
{
  class Value* left = myLeft->eval (valuation);
  if (!left)
    return NULL;
  class Value* right = myRight->eval (valuation);
  if (!right) {
    delete left;
    return NULL;
  }

  bool result = myEqual ? *left == *right : *left < *right;
  delete left; delete right;
  return constrain (valuation, new class LeafValue (*getType (), result));
}

class Expression*
RelopExpression::ground (const class Valuation& valuation,
			 class Transition* transition,
			 bool declare)
{
  class Expression* left = myLeft->ground (valuation, transition, declare);
  if (!left) return NULL;
  class Expression* right = myRight->ground (valuation, transition, declare);
  if (!right) { left->destroy (); return NULL; }

  assert (valuation.isOK ());

  if (left == myLeft && right == myRight) {
    left->destroy ();
    right->destroy ();
    return copy ();
  }
  else
    return construct (myEqual, *left, *right)->ground (valuation);
}

class Expression*
RelopExpression::substitute (class Substitution& substitution)
{
  class Expression* left = myLeft->substitute (substitution);
  class Expression* right = myRight->substitute (substitution);

  if (left == myLeft && right == myRight) {
    left->destroy ();
    right->destroy ();
    return copy ();
  }
  else {
    class Expression* expr = construct (myEqual, *left, *right);
    expr->setType (*getType ());
    return expr;
  }
}

bool
RelopExpression::depends (const class VariableSet& vars,
			  bool complement) const
{
  return
    myLeft->depends (vars, complement) ||
    myRight->depends (vars, complement);
}

bool
RelopExpression::forVariables (bool (*operation)
			       (const class Expression&,void*),
			       void* data) const
{
  return
    myLeft->forVariables (operation, data) &&
    myRight->forVariables (operation, data);
}

class Expression*
RelopExpression::disqualify (const class Transition& transition)
{
  class Expression* left = myLeft->disqualify (transition);
  class Expression* right = myRight->disqualify (transition);

  if (left == myLeft && right == myRight) {
    left->destroy (), right->destroy ();
    return copy ();
  }
  if (!left || !right) {
    left->destroy (), right->destroy ();
    return 0;
  }

  class Expression* expr = construct (myEqual, *left, *right);
  class Valuation valuation;
  if (class Value* v = expr->eval (valuation)) {
    expr->destroy ();
    return (new class Constant (*v))->cse ();
  }
  else
    return expr;
}

#ifdef EXPR_COMPILE
# include "CExpression.h"

/** Compile a comparison against a constant
 * @param cexpr		the compilation
 * @param indent	indentation level
 * @param lvalue	C expression referring to the lvalue
 * @param vars		the variables that have been assigned a value
 * @param valuation	the valuation
 * @param out		the output stream
 * @param expr		the expression
 * @param value		the value being compared against
 * @param right		flag: is the constant on the right-hand-side
 * @param equal		flag: equality comparison (instead of less-than)
 */
inline static void
compileComparison (class CExpression& cexpr,
		   unsigned indent,
		   const char* lvalue,
		   const class VariableSet* vars,
		   class StringBuffer& out,
		   const class Expression& expr,
		   const class Value& value,
		   bool right,
		   bool equal)
{
  char* var = 0;
  if (cexpr.getVariable (expr, var))
    expr.compile (cexpr, indent, var, vars);

  if (indent)
    out.indent (indent);
  if (lvalue)
    out.append (lvalue), out.append ("=");

  if (equal)
    value.compileEqual (out, indent, var, true, true, true);
  else
    value.compileOrder (out, indent, var, right, false, true, true);

  delete[] var;
  out.append (";\n");
}

void
RelopExpression::compile (class CExpression& cexpr,
			  unsigned indent,
			  const char* lvalue,
			  const class VariableSet* vars) const
{
  class StringBuffer& out = cexpr.getOut ();
  if (myLeft->getKind () == Expression::eConstant)
    ::compileComparison (cexpr, indent, lvalue, vars, out,
			 *myRight,
			 static_cast<const class Constant*>
			 (myLeft)->getValue (),
			 false, myEqual);
  else if (myRight->getKind () == Expression::eConstant)
    ::compileComparison (cexpr, indent, lvalue, vars, out,
			 *myLeft,
			 static_cast<const class Constant*>
			 (myRight)->getValue (),
			 true, myEqual);
  else {
    char* left;
    char* right;
    if (cexpr.getVariable (*myLeft, left))
      myLeft->compile (cexpr, indent, left, vars);
    if (cexpr.getVariable (*myRight, right))
      myRight->compile (cexpr, indent, right, vars);
    if (myLeft->getType ()->getNumValues () == 1) {
      out.indent (indent);
      out.append (lvalue);
      out.append (myEqual ? "=1;\n" : "=0;\n");
    }
    else if (myEqual) {
      out.indent (indent);
      out.append (lvalue);
      out.append ("=");
      if (!myLeft->getType ()->compileEqual (out, indent + strlen (lvalue) + 1,
					     left, right,
					     true, true, true, false))
	out.append ("1");
      out.append (";\n");
    }
    else if (myLeft->getType ()->isLeaf ()) {
      out.indent (indent);
      out.append (lvalue);
      out.append ("=");
      out.append (left);
      out.append ("<");
      out.append (right);
      out.append (";\n");
    }
    else {
      out.append ("#define CMP(l,r,L,G)");
      myLeft->getType ()->compileCompare3 (out);
      out.append ("\n");
      out.indent (indent);
      out.append ("CMP (");
      out.append (left);
      out.append (", ");
      out.append (right);
      char* less = cexpr.getLabel ();
      char* greater = cexpr.getLabel ();
      char* done = cexpr.getLabel ();
      out.append (", goto "), out.append (less);
      out.append (", goto "), out.append (greater);
      out.append (");\n#undef CMP\n");
      out.indent (indent);
      out.append ("goto ");
      out.append (greater);
      out.append (";\n");
      out.indent (indent - 2);
      out.append (less);
      out.append (":\n");
      out.indent (indent);
      out.append (lvalue), out.append ("=1;\n");
      out.indent (indent);
      out.append ("goto "), out.append (done);
      out.append (";\n");
      out.indent (indent - 2);
      out.append (greater);
      out.append (":\n");
      out.indent (indent);
      out.append (lvalue), out.append ("=0;\n");
      out.indent (indent - 2);
      out.append (done);
      out.append (":\n");
      delete[] less;
      delete[] greater;
      delete[] done;
    }
    delete[] left;
    delete[] right;
  }
  compileConstraint (cexpr, indent, lvalue);
}

#endif // EXPR_COMPILE

/** Determine whether an expression needs to be enclosed in parentheses
 * @param kind	kind of the expression
 * @return	whether parentheses are necessary
 */
inline static bool
needParentheses (enum Expression::Kind kind)
{
  switch (kind) {
  case Expression::eVariable:
  case Expression::eConstant:
  case Expression::eUndefined:
  case Expression::eStruct:
  case Expression::eStructComponent:
  case Expression::eUnion:
  case Expression::eUnionComponent:
  case Expression::eUnionType:
  case Expression::eVector:
  case Expression::eVectorIndex:
  case Expression::eUnop:
  case Expression::eNot:
  case Expression::eBuffer:
  case Expression::eBufferUnop:
  case Expression::eBufferIndex:
  case Expression::eSet:
  case Expression::eTypecast:
    return false;
  case Expression::eMarking:
  case Expression::eTransitionQualifier:
  case Expression::ePlaceContents:
  case Expression::eSubmarking:
  case Expression::eMapping:
  case Expression::eEmptySet:
    assert (false);
  case Expression::eBinop:
  case Expression::eBooleanBinop:
  case Expression::eRelop:
  case Expression::eIfThenElse:
  case Expression::eTemporalBinop:
  case Expression::eTemporalUnop:
  case Expression::eCardinality:
  case Expression::eBufferWrite:
  case Expression::eBufferRemove:
  case Expression::eStructAssign:
  case Expression::eVectorAssign:
  case Expression::eVectorShift:
    break;
  }

  return true;
}

void
RelopExpression::display (const class Printer& printer) const
{
  const char* cast = myLeft->getType ()->getName ();
  switch (myLeft->getType ()->getKind ()) {
  case Type::tBool:
  case Type::tChar:
  case Type::tInt:
  case Type::tCard:
    cast = 0;
  default:
    break;
  }

  if (cast &&
      myLeft->getKind () != Expression::eVariable &&
      (myLeft->getKind () != Expression::eConstant ||
       myLeft->getType ()->getKind () == Type::tEnum)) {
    printer.printRaw ("is");
    printer.delimiter (' ');
    printer.print (cast);
    printer.delimiter (' ');
  }

  if (::needParentheses (myLeft->getKind ())) {
    printer.delimiter ('(')++;
    myLeft->display (printer);
    --printer.delimiter (')');
  }
  else
    myLeft->display (printer);

  printer.printRaw (myEqual ? "==" : "<");

  if (cast &&
      myRight->getKind () != Expression::eVariable &&
      (myRight->getKind () != Expression::eConstant ||
       myRight->getType ()->getKind () == Type::tEnum)) {
    if (const char* rcast = myRight->getType ()->getName ())
      cast = rcast;
    printer.printRaw ("is");
    printer.delimiter (' ');
    printer.print (cast);
    printer.delimiter (' ');
  }

  if (::needParentheses (myRight->getKind ())) {
    printer.delimiter ('(')++;
    myRight->display (printer);
    --printer.delimiter (')');
  }
  else
    myRight->display (printer);
}
