// Maria range class -*- c++ -*-

#ifdef __GNUC__
# pragma implementation
#endif // __GNUC__
#include "Range.h"
#include "LeafValue.h"
#include "Type.h"
#include "Printer.h"

/** @file Range.C
 * Closed range of values, part of a Constraint
 */

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

Range::Range (class Value& low,
	      class Value& high) :
  myLow (&low), myHigh (&high)
{
  if (*myHigh < *myLow) {
    class Value* v = myLow;
    myLow = myHigh;
    myHigh = v;
  }
  else if (myLow != myHigh && *myLow == *myHigh) {
    delete myHigh;
    myHigh = myLow;
  }
}

Range::Range (const class Range& old) :
  myLow (old.myLow->copy ()), myHigh (old.myHigh->copy ())
{
  assert (*myLow <= *myHigh);
}

Range::~Range ()
{
  delete myLow;
  if (myLow != myHigh)
    delete myHigh;
}

void
Range::setType (const class Type& type)
{
  myLow->setType (type);
  myHigh->setType (type);
}

class Range*
Range::combine (const class Range& other) const
{
  if (*other.myLow <= *myLow && *myHigh <= *other.myHigh)
    return new class Range (*other.myLow->copy (), *other.myHigh->copy ());
  if (*other.myLow <= *myLow && *myLow <= *other.myHigh)
    return new class Range (*other.myLow->copy (), *myHigh->copy ());
  if (*other.myLow <= *myHigh && *myHigh <= *other.myHigh)
    return new class Range (*myLow->copy (), *other.myHigh->copy ());
  if (*myLow <= *other.myLow && *other.myHigh <= *myHigh)
    return new class Range (*myLow->copy (), *myHigh->copy ());

  class Value* v;
  bool adjacent;

  v = myHigh->copy ();
  adjacent = v->increment () && *v == *other.myLow;
  delete v;
  if (adjacent)
    return new class Range (*myLow->copy (), *other.myHigh->copy ());

  v = other.myHigh->copy ();
  adjacent = v->increment () && *v == *myLow;
  delete v;
  if (adjacent)
    return new class Range (*other.myLow->copy (), *myHigh->copy ());

  return 0;
}

class Range*
Range::cut (const class Range& other) const
{
  if (*other.myLow <= *myLow && *myHigh <= *other.myHigh)
    return new class Range (*myLow->copy (), *myHigh->copy ());
  if (*other.myLow <= *myLow && *myLow <= *other.myHigh)
    return new class Range (*myLow->copy (), *other.myHigh->copy ());
  if (*other.myLow <= *myHigh && *myHigh <= *other.myHigh)
    return new class Range (*other.myLow->copy (), *myHigh->copy ());
  if (*myLow <= *other.myLow && *other.myHigh <= *myHigh)
    return new class Range (*other.myLow->copy (), *other.myHigh->copy ());

  return 0;
}

bool
Range::check (const class Value& value) const
{
  return myLow == myHigh
    ? value == *myLow
    : *myLow <= value && value <= *myHigh;
}

void
Range::display (const class Printer& printer) const
{
  if (myLow != myHigh &&
      myLow->getKind () == Value::vLeaf) {
    const class LeafValue& low = static_cast<class LeafValue&>(*myLow);
    const class LeafValue& high = static_cast<class LeafValue&>(*myHigh);
    switch (low.getType ().getKind ()) {
    case Type::tInt:
      if (int_t (low) != INT_T_MIN)
	low.display (printer);
      printer.printRaw ("..");
      if (int_t (high) != INT_T_MAX)
	high.display (printer);
      return;
    case Type::tCard:
      if (card_t (low))
	low.display (printer);
      printer.printRaw ("..");
      if (card_t (high) != CARD_T_MAX)
	high.display (printer);
      return;
    default:
      break;
    }
  }
  myLow->display (printer);
  if (myLow != myHigh) {
    printer.printRaw ("..");
    myHigh->display (printer);
  }
}
