/*  -*- C++ -*-
 * rule.C
 * @(#) code for rules, startstates, and invariants for the Murphi compiler.
 *
 * Copyright (C) 1992 - 1999 by the Board of Trustees of
 * Leland Stanford Junior University.
 *
 * License to use, copy, modify, sell and/or distribute this software
 * and its documentation any purpose is hereby granted without royalty,
 * subject to the following terms and conditions:
 *
 * 1.  The above copyright notice and this permission notice must
 * appear in all copies of the software and related documentation.
 *
 * 2.  The name of Stanford University may not be used in advertising or
 * publicity pertaining to distribution of the software without the
 * specific, prior written permission of Stanford.
 *
 * 3.  This software may not be called "Murphi" if it has been modified
 * in any way, without the specific prior written permission of David L.
 * Dill.
 *
 * 4.  THE SOFTWARE IS PROVIDED "AS-IS" AND STANFORD MAKES NO
 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, BY WAY OF EXAMPLE,
 * BUT NOT LIMITATION.  STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES
 * OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE
 * USE OF THE SOFTWARE WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS
 * TRADEMARKS OR OTHER RIGHTS. STANFORD SHALL NOT BE LIABLE FOR ANY
 * LIABILITY OR DAMAGES WITH RESPECT TO ANY CLAIM BY LICENSEE OR ANY
 * THIRD PARTY ON ACCOUNT OF, OR ARISING FROM THE LICENSE, OR ANY
 * SUBLICENSE OR USE OF THE SOFTWARE OR ANY SERVICE OR SUPPORT.
 *
 * LICENSEE shall indemnify, hold harmless and defend STANFORD and its
 * trustees, officers, employees, students and agents against any and all
 * claims arising out of the exercise of any rights under this Agreement,
 * including, without limiting the generality of the foregoing, against
 * any damages, losses or liabilities whatsoever with respect to death or
 * injury to person or damage to property arising from or out of the
 * possession, use, or operation of Software or Licensed Program(s) by
 * LICENSEE or its customers.
 *
 * Read the file "license" distributed with these sources, or call
 * Murphi with the -l switch for additional information.
 * 
 */

/* 
 * Original Author: Ralph Melton
 * 
 * Update:
 *
 * Seungjoon Park
 * Subject: liveness syntax
 * Last modified: Novemeber  5, 93
 *
 */ 

#include "mu.h"
#include <iostream>

using namespace std;

/********************
  class rule
  ********************/
rule::rule( void)
: next(NULL)
{
}

/********************
  class simplerule and variable simplerulenum
  ********************/
int simplerulenum = 0;
simplerule::simplerule(ste *enclosures,
		       expr *condition,
		       ste *locals,
		       stmt *body,
		       bool unfair,
                       int priority)
:rule(), name( NULL ), condname(NULL), rulename(NULL), enclosures(enclosures),  condition(condition),
 locals(locals), body(body), unfair(unfair),
 priority(priority)
{
  if ( condition != NULL && body != NULL )
    {
      Error.CondError(condition->has_side_effects(),
		      "Rule Condition must not have side effects.");
      Error.CondError(!type_equal(condition->gettype(), booltype),
		      "Condition for rule must be a boolean expression.");
    }
  NextSimpleRule = SimpleRuleList;
  SimpleRuleList = this;

  size = CountSize(enclosures);

  // Rearrange enclosures so that the ones that are NOT
  // mentioned in the condition go first
  rearrange_enclosures();
  
}

simplerule * simplerule::SimpleRuleList = NULL;

int simplerule::CountSize(ste * enclosures)
{
  int ret;
  if ( enclosures != NULL &&
       ( enclosures->getvalue()->getclass() == decl::Quant ||
	 enclosures->getvalue()->getclass() == decl::Alias ||
	 enclosures->getvalue()->getclass() == decl::Choose ) )
    {
      ret = CountSize(enclosures->getnext() );
      if ( enclosures->getvalue()->getclass() == decl::Quant ||
	   enclosures->getvalue()->getclass() == decl::Choose )
	return ret * enclosures->getvalue()->gettype()->getsize();
      else
	return ret;
    }
  else
    return 1;
}


void simplerule::rearrange_enclosures()
{
  stecoll* us = condition ? condition->used_stes() : NULL;
  ste* front = NULL;
  ste* end = NULL;
  ste* rest = NULL;
  ste* ep = enclosures;

  bool is_dep = FALSE;  // Is the current ste mentioned in rule cond?
  bool is_cp = FALSE;   // Is the current ste a choose parameter?
  ste* cp = NULL;       // Choose parameter

  // Cardinality of parameters not mentioned in the condition
  indep_card = 1;
  // Lists of choose parameters
  dep_choose = indep_choose = NULL;

  if(!us) return;

  // Iterate over all relevant ste's in enclosures
  ep = enclosures;
  while( ep != NULL &&
       ( ep->getvalue()->getclass() == decl::Quant ||
         ep->getvalue()->getclass() == decl::Alias ||
         ep->getvalue()->getclass() == decl::Choose ) ) {

    // Create a copy of ep
    ste* ns = new ste(ep->getname(), ep->getscope(), ep->getvalue());
    ns->setnext(NULL);

    // If it's a choose parameter, create another copy
    is_cp = (ep->getvalue()->getclass() == decl::Choose);
    if(is_cp) {
      cp = new ste(ep->getname(), ep->getscope(), ep->getvalue());
      cp->setnext(NULL);
    }

    if(us->includes(ep)) {
      // ep is mentioned in the condition
      is_dep = TRUE;

      if (ep->getvalue()->getclass() == decl::Alias) {
         stecoll* alias_stes = ep->getvalue()->getexpr()->used_stes();
         us->append(alias_stes); 
      }

      // if it's a choose parameter,
      // add to the list of ``dependent'' choose parameters
      if(is_cp) {
        cp->setnext(dep_choose);
        dep_choose = cp;
      }
    }

    else {
      // ep is NOT mentioned in the condition
      is_dep = FALSE;

      // Cardinality of Alias is always 1
      if (ep->getvalue()->getclass() != decl::Alias) {
         indep_card *= ns->getvalue()->gettype()->getsize();
      }

      // if it's a choose parameter,
      // add to the list of ``independent'' choose parameters
      if(is_cp) {
        cp->setnext(indep_choose);
        indep_choose = cp;
      }
    }

    // If it's not an alias and it's mentioned in the condition, 
    //    move to the front of the list;
    // otherwise add to the end of the list
    if(!front) {
      front = end = ns; }
    else {
      // if(is_dep) {
      if(is_dep && 
        (ep->getvalue()->getclass() != decl::Alias)) {
        ns->setnext(front);
        front = ns; }
      else {
        end->setnext(ns);
        end = ns; }
    }

    ep = ep->getnext();
  }
  // The rest of the enclosures
  rest = ep;

  if(front) {
    enclosures = front;
    end->setnext(rest);
  }

}


/********************
  class startstate and variable startstatenum
  ********************/
int startstatenum = 0;
startstate::startstate(ste *enclosures,
		       ste *locals,
		       stmt *body)
:simplerule(enclosures, NULL, locals, body, FALSE, 0)
{
}

/********************
  class invariant and variable invariantnum
  ********************/
int invariantnum = 0;
invariant::invariant(ste *enclosures, expr *test)
:simplerule(enclosures, test, NULL, NULL, FALSE, 0)
{
  Error.CondError(!type_equal( test->gettype(), booltype),
		  "Invariant must be a boolean expression.");
  Error.CondError(test->has_side_effects(),
		  "Invariant must not have side effects.");
}

/********************
  class fairness and variable fairnessnum
  ********************/
int fairnessnum = 0;
fairness::fairness(ste *enclosures, expr *test)
:simplerule(enclosures, test, NULL, NULL, FALSE, 0)
{
  Error.CondError(!type_equal( test->gettype(), booltype),
                  "Fairness must be a boolean expression.");
  Error.CondError(test->has_side_effects(),
                  "Fairness must not have side effects.");
}

/********************
  class liveness and variable livenessnum
  ********************/
int livenessnum = 0;
liveness::liveness(ste *enclosures, expr *ptest, expr *ltest, expr *rtest,
                        live_type livetype)
 : name(NULL), precondname(NULL), leftcondname(NULL), rightcondname(NULL),
 enclosures(enclosures), precondition(ptest), leftcondition(ltest),
 rightcondition(rtest), livetype(livetype) {};


/********************
  class quantrule
  ********************/
quantrule::quantrule(ste *quant, rule *rules)
: rule(), quant(quant), rules(rules)
{
  quantdecl *q = (quantdecl *) quant->getvalue();
  if ( q->left != NULL )
    {
      Error.CondError( !q->left->hasvalue(),
		       "Bounds for ruleset parameters must be constants.");
      Error.CondError( !q->right->hasvalue(),
		       "Bounds for ruleset parameters must be constants.");
    }
}

/********************
  class chooserule
  ********************/
chooserule::chooserule(ste *index, designator *set, rule *rules)
: rule(), index(index), set(set), rules(rules)
{
  Error.CondError( ((multisetidtypedecl *)index->getvalue()->gettype())->getparenttype() != set->gettype(),
		   "Internal Error: Parameter for Choose declared in type other than the multiset index");
  Error.CondError( set->gettype()->gettypeclass() != typedecl::MultiSet,
		   "Parameter for choose must be a multiset");
}

/********************
  class aliasrule
  ********************/
aliasrule::aliasrule(ste *aliases, rule *rules)
: rule(), aliases(aliases), rules(rules)
{
}

/********************
  variable declaration
  ********************/
simplerule *error_rule = NULL;

/****************************************
  * 8 March 94 Norris Ip:
  merge with the latest rel2.6
  * 7 OCT 94 Norris Ip:
  added multiset choose rule
****************************************/

/********************
 $Log: rule.C,v $
 Revision 1.3  1999/01/29 08:28:10  uli
 efficiency improvements for security protocols

 Revision 1.2  1999/01/29 07:49:13  uli
 bugfixes

 Revision 1.4  1996/08/07 18:54:00  ip
 last bug fix on NextRule/SetNextEnabledRule has a bug; fixed this turn

 Revision 1.3  1996/08/07 00:59:13  ip
 Fixed bug on what_rule setting during guard evaluation; otherwise, bad diagnoistic message on undefine error on guard

 Revision 1.2  1996/08/06 23:57:39  ip
 fixed while code generation bug

 Revision 1.1  1996/08/06 23:56:55  ip
 Initial revision

 ********************/
