// Maria vector indexing expression -*- c++ -*-

#ifdef __GNUC__
# pragma implementation
#endif // __GNUC__
#include "VectorIndex.h"
#include "VectorType.h"
#include "VectorValue.h"
#include "Valuation.h"
#include "VariableDefinition.h"
#include "Printer.h"

/** @file VectorIndex.C
 * Vector indexing operation
 */

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

VectorIndex::VectorIndex (class Expression& vector,
			  class Expression& i) :
  Expression (),
  myVector (&vector), myIndex (&i)
{
  assert (myVector && myVector->getType ());
  assert (myVector->getType ()->getKind () == Type::tVector);
  assert (myIndex && myIndex->getType ());
  assert (myIndex->getType ()->isAssignable
	  (static_cast<const class VectorType*>(myVector->getType ())
	   ->getIndexType ()));
  assert (myVector->isBasic () && myIndex->isBasic ());
  setType (static_cast<const class VectorType*>(myVector->getType ())
	   ->getItemType ());
}

VectorIndex::~VectorIndex ()
{
  myVector->destroy ();
  myIndex->destroy ();
}

class Value*
VectorIndex::do_eval (const class Valuation& valuation) const
{
  class Value* idx = myIndex->eval (valuation);
  if (!idx)
    return NULL;
  const class Type& indexType =
    static_cast<const class VectorType*>(myVector->getType ())
    ->getIndexType ();

  assert (idx->getType ().isAssignable (indexType));
  assert (indexType.isConstrained (*idx));

  card_t i = indexType.convert (*idx);
  assert (i < CARD_T_MAX && i < indexType.getNumValues ());
  delete idx;

  class Value* vector = myVector->eval (valuation);
  if (!vector)
    return NULL;

  assert (&vector->getType () == myVector->getType ());
  if (const class Value* v =
      (*static_cast<class VectorValue*>(vector))[i]) {
    class Value* w = v->copy ();
    delete vector;
    return w;
  }
  else {
    assert (false);
    delete vector;
    return NULL;
  }
}

class Expression*
VectorIndex::ground (const class Valuation& valuation,
		     class Transition* transition,
		     bool declare)
{
  class Expression* vector = myVector->ground (valuation, transition, declare);
  if (!vector) return NULL;
  class Expression* i = myIndex->ground (valuation, transition, declare);
  if (!i) { vector->destroy (); return NULL; }

  assert (valuation.isOK ());

  if (vector == myVector && i == myIndex) {
    vector->destroy ();
    i->destroy ();
    return copy ();
  }
  else
    return static_cast<class Expression*>
      (new class VectorIndex (*vector, *i))->ground (valuation);
}

class Expression*
VectorIndex::substitute (class Substitution& substitution)
{
  class Expression* vector = myVector->substitute (substitution);
  class Expression* i = myIndex->substitute (substitution);

  if (vector == myVector && i == myIndex) {
    vector->destroy ();
    i->destroy ();
    return copy ();
  }
  else
    return (new class VectorIndex (*vector, *i))->cse ();
}

bool
VectorIndex::depends (const class VariableSet& vars,
		      bool complement) const
{
  return
    myVector->depends (vars, complement) ||
    myIndex->depends (vars, complement);
}

bool
VectorIndex::forExpressions (bool (*operation)
			     (const class Expression&,void*),
			     void* data) const
{
  return
    (*operation) (*this, data) &&
    myVector->forExpressions (operation, data) &&
    myIndex->forExpressions (operation, data);
}

#ifdef EXPR_COMPILE
# include "CExpression.h"
# include "Constant.h"

void
VectorIndex::compile (class CExpression& cexpr,
		      unsigned indent,
		      const char* lvalue,
		      const class VariableSet* vars) const
{
  char* vvalue = 0;
  if (cexpr.getVariable (*myVector, vvalue))
    myVector->compile (cexpr, indent, vvalue, vars);
  char* ixconv = 0;
  class StringBuffer& out = cexpr.getOut ();
  if (myIndex->getKind () != Expression::eConstant) {
    char* ixvalue = 0;
    if (cexpr.getVariable (*myIndex, ixvalue))
      myIndex->compile (cexpr, indent, ixvalue, vars);
    if (cexpr.getConverted (*myIndex, ixconv))
      myIndex->getType ()->compileConversion (out, indent,
					      ixvalue, ixconv, false);
    delete[] ixvalue;
  }
  out.indent (indent);
  out.append (lvalue);
  out.append ("=");
  out.append (vvalue);
  out.append (".a[");
  if (myIndex->getKind () == Expression::eConstant)
    out.append (myIndex->getType ()->convert
		(static_cast<const class Constant&>(*myIndex).getValue ()));
  else
    out.append (ixconv);
  out.append ("];\n");
  delete[] vvalue;
  delete[] ixconv;
}

#endif // EXPR_COMPILE

void
VectorIndex::display (const class Printer& printer) const
{
  myVector->display (printer);
  printer.delimiter ('[')++;
  myIndex->display (printer);
  --printer.delimiter (']');
}
