// Vector type class -*- c++ -*-

#include "snprintf.h"

#ifdef __GNUC__
# pragma implementation
#endif // __GNUC__
#include "VectorType.h"
#include "VectorValue.h"
#include "Constraint.h"
#include "Printer.h"

#include <string.h>

/** @file VectorType.C
 * Array data type
 */

/* 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. */

class Value&
VectorType::getFirstValue () const
{
  if (myConstraint)
    return *myConstraint->getFirstValue ().copy ();

  class Value* comp = &myItemType->getFirstValue ();
  class VectorValue* v = new class VectorValue (*this);

  for (card_t i = getSize (); --i; (*v)[i] = comp->copy ());
  (*v)[0] = comp;

  return *v;
}

class Value&
VectorType::getLastValue () const
{
  if (myConstraint)
    return *myConstraint->getLastValue ().copy ();

  class Value* comp = &myItemType->getLastValue ();
  class VectorValue* v = new class VectorValue (*this);

  for (card_t i = getSize (); --i; (*v)[i] = comp->copy ());
  (*v)[0] = comp;

  return *v;
}

bool
VectorType::isAssignable (const class Type& type) const
{
  if (&type == this)
    return true;

  if (type.getKind () != getKind ())
    return Type::isAssignable (type);

  const class VectorType& v = static_cast<const class VectorType&>(type);

  return
    myIndexType->isAlwaysAssignable (*v.myIndexType) &&
    myItemType->isAssignable (*v.myItemType);
}

bool
VectorType::isAlwaysAssignable (const class Type& type) const
{
  if (&type == this)
    return true;

  if (type.getKind () != getKind ())
    return false;

  const class VectorType& v = static_cast<const class VectorType&>(type);

  return
    myIndexType->isAlwaysAssignable (*v.myIndexType) &&
    myItemType->isAlwaysAssignable (*v.myItemType);
}

bool
VectorType::isConstrained (const class Value& value) const
{
  assert (value.getType ().isAssignable (*this));
  const class VectorValue& v = static_cast<const class VectorValue&>(value);
  assert (v.getSize () == getSize ());

  for (card_t i = getSize (); i--; )
    if (!myItemType->isConstrained (v[i]))
      return false;

  return Type::isConstrained (value);
}

card_t
VectorType::do_getNumValues () const
{
  assert (!myConstraint);
  card_t numValues;
  card_t numItemValues = myItemType->getNumValues ();
  numValues = 1;
  if (numItemValues == CARD_T_MAX)
    return CARD_T_MAX;
  else if (numItemValues == 1)
    return 1;

  for (card_t i = 0, size = getSize (); i < size; i++) {
    if (numItemValues < CARD_T_MAX / numValues)
      numValues *= numItemValues;
    else
      return CARD_T_MAX;
  }

  return numValues;
}

card_t
VectorType::convert (const class Value& value) const
{
  assert (value.getKind () == Value::vVector);
  assert (isConstrained (value));
  assert (getNumValues () < CARD_T_MAX);

  if (myConstraint)
    return Type::convert (value);

  card_t number = 0;
  const class VectorValue& v = static_cast<const class VectorValue&>(value);
  card_t i = getSize (), numItemValues = myItemType->getNumValues ();
  while (i--)
    (number *= numItemValues) += myItemType->convert (v[i]);

  assert (number < getNumValues ());
  return number;
}

class Value*
VectorType::convert (card_t number) const
{
  assert (number < getNumValues ());
  assert (getNumValues () < CARD_T_MAX);

  if (myConstraint)
    return Type::convert (number);

  class VectorValue* value = new class VectorValue (*this);
  const card_t numItemValues = myItemType->getNumValues ();
  const card_t size = getSize ();

  for (card_t i = 0; i < size; i++) {
    card_t num = number % numItemValues;
    number /= numItemValues;
    (*value)[i] = myItemType->convert (num);
  }

  assert (isConstrained (*value));
  return value;
}

#ifdef EXPR_COMPILE
# include "CExpression.h"
# include <stdio.h>

void
VectorType::compile (class StringBuffer& out)
{
  const_cast<class Type*>(myIndexType)->compile (out);
  const_cast<class Type*>(myItemType)->compile (out);
  Type::compile (out);
}

void
VectorType::compileDefinition (class StringBuffer& out,
			       unsigned indent) const
{
  char ixname[25];

  out.indent (indent), out.append ("struct {\n");
  out.indent (indent + 2), myItemType->appendName (out);
  snprintf (ixname, sizeof ixname, " a[%u]", getSize ());
  out.append (ixname);
  out.append (";\n");
  out.indent (indent), out.append ("}");
}

bool
VectorType::compileEqual (class StringBuffer& out,
			  unsigned indent,
			  const char* left,
			  const char* right,
			  bool equal,
			  bool first,
			  bool last,
			  bool backslash) const
{
  if (getNumValues () == 1)
    return false;

  size_t llen = strlen (left), rlen = strlen (right);
  char* l = new char[llen + 25];
  char* r = new char[rlen + 25];
  memcpy (l, left, llen);
  memcpy (r, right, rlen);

  bool gen = false;

  for (card_t i = getSize (); i--; ) {
    snprintf (l + llen, 25, ".a[%u]", i);
    snprintf (r + rlen, 25, ".a[%u]", i);
    gen = myItemType->compileEqual (out, indent, l, r,
				    equal, first, !i, backslash);
    if (gen)
      first = false;
  }

  delete[] l; delete[] r;

  if (gen && !last)
    out.append (backslash
		? (equal ? "&&\\\n" : "||\\\n")
		: (equal ? "&&\n" : "||\n"));

  return gen || !last;
}

void
VectorType::compileCompare3 (class StringBuffer& out,
			     const char* condition,
			     const char* component) const
{
  const size_t len = strlen (component);
  char* const newcomp = new char[len + 25];
  char* const offset = newcomp + len;
  memcpy (newcomp, component, len);

  for (card_t i = myIndexType->getNumValues (); i--; ) {
    snprintf (offset, 25, ".a[%u]", i);
    myItemType->compileCompare3 (out, condition, newcomp);
  }
  delete[] newcomp;
}

void
VectorType::do_compileSuccessor (class StringBuffer& out,
				 unsigned indent,
				 const char* lvalue,
				 const char* rvalue,
				 const char* wrap) const
{
  size_t llen = strlen (lvalue), rlen = strlen (rvalue);
  char* lval = new char[llen + 25];
  char* rval = new char[rlen + 25];
  memcpy (lval, lvalue, llen);
  memcpy (rval, rvalue, rlen);

  out.indent (indent);
  out.append ("do {\n");
  if (!wrap) {
    wrap = "wrap";
    out.indent (indent + 2);
    out.append ("bool_t ");
    out.append (wrap);
    out.append ("=0;\n");
  }

  for (card_t i = 0;; ) {
    snprintf (lval + llen, 25, ".a[%u]", i);
    snprintf (rval + rlen, 25, ".a[%u]", i);
    myItemType->compileSuccessor (out, indent + 2, lval, rval, wrap);
    if (++i < getSize ()) {
      out.indent (indent + 2);
      out.append ("if (!");
      out.append (wrap);
      out.append (") {\n");
      if (lvalue != rvalue && strcmp (lvalue, rvalue)) {
	for (card_t j = i; j < getSize (); j++) {
	  out.indent (indent + 4);
	  out.append (lvalue);
	  out.append (".a[");
	  out.append (j);
	  out.append ("]=");
	  out.append (rvalue);
	  out.append (".a[");
	  out.append (j);
	  out.append ("];\n");
	}
      }
      out.indent (indent + 4);
      out.append ("continue;\n");
      out.indent (indent + 2);
      out.append ("}\n");
      out.indent (indent + 2);
      out.append (wrap);
      out.append ("=0;\n");
    }
    else
      break;
  }

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

  delete[] lval;
  delete[] rval;
}

void
VectorType::do_compilePredecessor (class StringBuffer& out,
				   unsigned indent,
				   const char* lvalue,
				   const char* rvalue,
				   const char* wrap) const
{
  size_t llen = strlen (lvalue), rlen = strlen (rvalue);
  char* lval = new char[llen + 25];
  char* rval = new char[rlen + 25];
  memcpy (lval, lvalue, llen);
  memcpy (rval, rvalue, rlen);

  out.indent (indent);
  out.append ("do {\n");
  if (!wrap) {
    wrap = "wrap";
    out.indent (indent + 2);
    out.append ("bool_t ");
    out.append (wrap);
    out.append ("=0;\n");
  }

  for (card_t i = 0;; ) {
    snprintf (lval + llen, 25, ".a[%u]", i);
    snprintf (rval + rlen, 25, ".a[%u]", i);
    myItemType->compilePredecessor (out, indent + 2, lval, rval, wrap);
    if (++i < getSize ()) {
      out.indent (indent + 2);
      out.append ("if (!");
      out.append (wrap);
      out.append (") {\n");
      if (lvalue != rvalue && strcmp (lvalue, rvalue)) {
	for (card_t j = i; j < getSize (); j++) {
	  out.indent (indent + 4);
	  out.append (lvalue);
	  out.append (".a[");
	  out.append (j);
	  out.append ("]=");
	  out.append (rvalue);
	  out.append (".a[");
	  out.append (j);
	  out.append ("];\n");
	}
      }
      out.indent (indent + 4);
      out.append ("continue;\n");
      out.indent (indent + 2);
      out.append ("}\n");
      out.indent (indent + 2);
      out.append (wrap);
      out.append ("=0;\n");
    }
    else
      break;
  }

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

  delete[] lval;
  delete[] rval;
}

void
VectorType::compileCast (class CExpression& cexpr,
			 unsigned indent,
			 const class Type& target,
			 const char* lvalue,
			 const char* rvalue) const
{
  assert (isAssignable (target));
  if (target.getKind () != Type::tVector)
    Type::compileCast (cexpr, indent, target, lvalue, rvalue);
  else {
    const class VectorType& vt = static_cast<const class VectorType&>(target);
    assert (getSize () == vt.getSize ());
    size_t llen = strlen (lvalue);
    size_t rlen = strlen (rvalue);
    char* lval = new char[llen + 25];
    char* rval = new char[llen + 25];
    memcpy (lval, lvalue, llen);
    memcpy (rval, rvalue, rlen);
    for (card_t i = getSize (); i--; ) {
      snprintf (lval + llen, 25, ".a[%u]", i);
      snprintf (rval + rlen, 25, ".a[%u]", i);
      myItemType->compileCast (cexpr, indent, *vt.myItemType, lval, rval);
    }
    delete[] lval;
    delete[] rval;
    if (const class Constraint* c = target.getConstraint ())
      c->compileCheck (cexpr, indent, lvalue);
  }
}

void
VectorType::do_compileConversion (class StringBuffer& out,
				  unsigned indent,
				  const char* value,
				  const char* number,
				  bool add) const
{
  card_t i = getSize ();
  assert (getNumValues () < CARD_T_MAX && i);
  size_t length = strlen (value);
  char* val = new char[length + 25];
  char* offset = val + length;
  memcpy (val, value, length);

  if (--i) {
    const char* num = number;
    if (add) {
      out.indent (indent), out.append ("{\n");
      out.indent (indent += 2), out.append ("card_t ");
      length = strlen (number);
      char* nbr = new char[length + 2];
      memcpy (nbr, number, length);
      memcpy (nbr + length, "_", 2);
      num = nbr;
      out.append (num), out.append (";\n");
    }

    const card_t numValues = myItemType->getNumValues ();
    for (bool next = false; i; next = true) {
      snprintf (offset, 25, ".a[%u]", i--);
      myItemType->compileConversion (out, indent, val, num, next);
      out.indent (indent), out.append (num), out.append ("*=");
      out.append (numValues), out.append (";\n");
    }

    memcpy (offset, ".a[0]", 6);
    myItemType->compileConversion (out, indent, val, num, true);

    if (add) {
      out.indent (indent), out.append (number), out.append ("+=");
      out.append (num), out.append (";\n");
      out.indent (indent -= 2), out.append ("}\n");
      delete[] num;
    }
  }
  else {
    memcpy (offset, ".a[0]", 6);
    myItemType->compileConversion (out, indent, val, number, add);
  }

  delete[] val;
}

void
VectorType::compileReverseConversion (class StringBuffer& out,
				      unsigned indent,
				      const char* number,
				      const char* value) const
{
  if (myConstraint)
    Type::compileReverseConversion (out, indent, number, value);
  else if (getNumValues () == 1)
    compileBottom (out, indent, value);
  else {
    size_t length = strlen (value);
    char* val = new char[length + 25];
    char* offset = val + length;
    memcpy (val, value, length);

    if (getSize () > 1) {
      length = strlen (number);
      char* num = new char[length + 2];
      memcpy (num, number, length);
      memcpy (num + length, "_", 2);

      out.indent (indent), out.append ("{\n");
      out.indent (indent + 2);
      out.append ("card_t "), out.append (num), out.append (";\n");

      card_t numValues = myItemType->getNumValues ();
      for (card_t i = 0; i < getSize (); i++) {
	snprintf (offset, 25, ".a[%u]", i);
	out.indent (indent + 2);
	out.append (num), out.append ("="), out.append (number);
	out.append ("%"), out.append (numValues);
	out.append (", ");
	out.append (number), out.append ("/=");
	out.append (numValues);
	out.append (";\n");
	myItemType->compileReverseConversion (out, indent + 2, num, val);
      }

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

      delete[] num;
    }
    else {
      memcpy (offset, ".a[0]", 6);
      myItemType->compileReverseConversion (out, indent, number, val);
    }

    delete[] val;
  }
}

void
VectorType::compileEncoder (class CExpression& cexpr,
			    unsigned indent,
			    const char* func,
			    const char* value) const
{
  if (getNumValues () < CARD_T_MAX)
    Type::compileEncoder (cexpr, indent, func, value);
  else {
    size_t length = strlen (value);
    char* val = new char[length + 6];
    memcpy (val, value, length);
    memcpy (val + length, ".a[i]", 6);

    class StringBuffer& out = cexpr.getOut ();
    out.indent (indent);
    out.append ("{\n");
    out.indent (indent + 2);
    out.append ("card_t i;\n");

    out.indent (indent + 2);
    out.append ("for (i=0; i<");
    out.append (getSize ());
    out.append ("; i++) {\n");

    myItemType->compileEncoder (cexpr, indent + 4, func, val);

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

    delete[] val;
  }
}

void
VectorType::compileDecoder (class CExpression& cexpr,
			    unsigned indent,
			    const char* func,
			    const char* value) const
{
  if (getNumValues () < CARD_T_MAX)
    Type::compileDecoder (cexpr, indent, func, value);
  else {
    size_t length = strlen (value);
    char* val = new char[length + 6];
    memcpy (val, value, length);
    memcpy (val + length, ".a[i]", 6);

    class StringBuffer& out = cexpr.getOut ();
    out.indent (indent);
    out.append ("{\n");
    out.indent (indent + 2);
    out.append ("card_t i;\n");

    out.indent (indent + 2);
    out.append ("for (i=0; i<");
    out.append (getSize ());
    out.append ("; i++) {\n");

    myItemType->compileDecoder (cexpr, indent + 4, func, val);

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

    delete[] val;
  }
}

#endif // EXPR_COMPILE

void
VectorType::display (const class Printer& printer) const
{
  if (const char* item = myItemType->getName ())
    printer.print (item);
  else
    myItemType->display (printer);
  printer.delimiter ('[')++;
  if (const char* i = myIndexType->getName ())
    printer.print (i);
  else
    myIndexType->display (printer);
  --printer.delimiter (']');

  if (myConstraint)
    myConstraint->display (printer);
}
