/***********************************
  (C) Copyright 1992-1993; dit/upm
  Distributed under the conditions stated in the
  TOPO General Public License (see file LICENSE)
  ***********************************/

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

  David Larrabeiti Lopez

  12-02-91

  Parallel expansion.

  Expansion of a parallel behaviour b.
  Both arguments ( beh1 & beh2 ) of b must be imme-
  diate .That's to say:

  Mxi                           Mxj
  b= ( SUMi( CHi Gi gi;Bi ) |[GSet]| SUMj( CHj Gj gj;Bj ) )
  1                             1

  where:

  gk:= visible-action | i-action | exit        (Mxk>1)
  gk:= visible-action | i-action | exit | stop (Mxk=1)

  COMPILATION OPTIONS: The behaviour of this module can be modified
  by the following compilation flags:

  (none)

  Gualberto Rabay 04.09.94
  Time constraints included

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



#include "limisc.h"
#include "badefca.h"
#include "baattr.h"
#include "balotosf.h"
#include "exsynchr.h"
#include "eximmed.h"
#include "expre_br.h"

/******************************************************************
 *
 * Table of actions.
 *
 *******************************************************************/

/******** table of actions *******/
typedef struct { BehTyp       gate;         /* ->gk                          */
		 BehTyp       branch;       /* ->CHk Gk gk;Bk                */
		 CellTypeTyp  type;         /* StopC | ExitC | GateC | IC    */
		 boolean      syncG;        /* (type==GateC) && (gk in GSet) */
	       } ArgIdTyp;


/******************************************************************
 *
 * Global variables.
 *
 *******************************************************************/

static BehTyp        par,beh1,beh2,beh1p,beh2p,result;
static int           numArg1,numArg2;
static GateSetTyp    gs;
static ArgIdTyp      *a1 = NULL;  /* beh1's table of operands      */
static int           a1_size = 0;
static ArgIdTyp      *a2 = NULL;  /* beh2's table of operands      */
static int           a2_size = 0;
static DescriptorTyp syncClass;   /* inter, full or partial synchr */

#ifdef MUSTF
static ExprTyp end1, end2;
#endif

/******************************************************************
 *
 * Functions to manage the table of actions.
 *
 *******************************************************************/

/* ReallocActionTable
 * Reallocate memory for the action table pointed by "pa" to "n+1" elements.
 */
static void ReallocActionTable( pa , psize , n )
     ArgIdTyp **pa;
     int      *psize, n;
{
  if (*pa == NULL)
    *pa = (ArgIdTyp *) ecalloc ( (unsigned)(n+1) , sizeof(ArgIdTyp) );
  else
    *pa = (ArgIdTyp *) erealloc ( (void*)*pa , (n+1)*sizeof(ArgIdTyp) );
  *psize = n+1;
}

/*----------------------------------------------------------------*/

/* FillInActionTable( b , pa , numArg )
 * Fills in the action table pointed by "pa" with the arguments of the
 * immediate behaviour "b".
 * "numArg" is the number of arguments of "b".
 * This table is a quick reference to every branch of "b".
 * Filling in the table we will know about argument number "k" of "b" :
 *
 *   (*pa)[k].branch := argument k.
 *   (*pa)[k].gate   := action.
 *   (*pa)[k].type   := type of action.
 *   (*pa)[k].sync   := TRUE iff the action is a gate and has to synchronize.
 *
 */
static void FillInActionTable( b , pa , numArg )
     BehTyp   b;
     ArgIdTyp **pa;
     int      numArg;
{
  int k;

  for ( k=1; k<=numArg; k++ ) {
    (*pa)[k].branch = LookArgIB( b, k );
    (*pa)[k].gate   = LookActionUB( (*pa)[k].branch );
    switch ((*pa)[k].type = LookTypeB( (*pa)[k].gate ))
      { case GateC:
	  if ( syncClass == PART_SYNC )
	    (*pa)[k].syncG = GateInGS(LookNameB((*pa)[k].gate),gs);
	  else
	    (*pa)[k].syncG = (syncClass==FULL_SYNC);
	  break;

	case ExitC:
	  (*pa)[k].syncG = TRUE;
	  break;

	case IC:
	  (*pa)[k].syncG = FALSE;
	  break;

	case TerminationC:
	case StopC:
	  break;

	default:
	  Error("FillInActionTable: unexpected cell.");
	}
  }
}

/******************************************************************
 *
 * Refused (unsuccessful) synchronizations.
 *
 *******************************************************************/

static BehTyp refusedSynchr = NULL;

/* UpdateNotSyncTree
 * Adds a new unsuccessful synchronization into "refusedSynchr"
 */
/* KJT 22/01/23: added "int" type */
static int UpdateNotSyncTree( gate1, gate2 )
     BehTyp gate1, gate2;
{
  BehTyp nst,alt;

  nst = CopyB(par);
  if ( !IsEventLeafST(gate1) )
    gate1 = (BehTyp)LookAInfo(LookA(gate1,STA));
  if ( !IsEventLeafST(gate2) )
    gate2 = (BehTyp)LookAInfo(LookA(gate2,STA));
  PutArgB( nst, gate1, 1 );
  PutArgB( nst, gate2, 2 );

  if (refusedSynchr == NULL )
    refusedSynchr = ShareB(nst);
  else
    if ( LookTypeB(refusedSynchr)!=AlternativeC ) {
      alt = ShareB( MakeB(0,AlternativeC) );
      AddArgB( alt, UnshareB(refusedSynchr) );
      AddArgB( alt, nst );
      refusedSynchr = alt;
    }
    else
      AddArgB( refusedSynchr, nst );
  LASSERT( LookCopy(refusedSynchr) >0 );
}


/* GetRefusedSync
 * Return the unsuccessful synchronizations stored in "refusedSynchr"
 * and set this variable to NULL.
 */
BehTyp GetRefusedSync()
{
  BehTyp res;

  res = refusedSynchr;
  refusedSynchr = NULL;
  return res;
}

/******************************************************************
 *
 * Expansion of the parallel operator for each actions.
 *
 *******************************************************************/

/* Par_gate_beh
 * Expands the parallel composition of branch i of beh1 with beh2
 * when branch i contains a visible action that doesn't have to synchronize.
 */
static BehTyp Par_gate_beh( i, b2 )
     int i;
     BehTyp b2;
{
  BehTyp out,pb,act;

#ifdef TIME
  PTimeTyp time;
  PAttrTyp ta;
  BehTyp act2;
  DescriptorTyp  tvar;
  ExprTyp e1;

#ifdef MUSTF
 DescriptorTyp  opmin;
 ExprTyp expmin;
#endif
#endif

  pb = CopyB(par);
  PutArgB( pb, LookArgB(a1[i].gate,1), 1);

#ifdef TIME
  act2 =    CopyUntilActB( b2);
  ShareB(act2);
  UnshareAttrsUntilActB(act2);
  UnshareB(act2);
  out =   CopyUntilActB( a1[i].branch );
   ShareB(out);
   UnshareAttrsUntilActB(out);
   UnshareB(out);

  ta =LookA(LookActionUB(out),TA);
  time = (PTimeTyp)LookAInfo(ta);

#ifdef MUSTF
  opmin = FindO_time("min");
  expmin = MakeE(opmin,OperationC);
  AddArgE(expmin,CopyE(end2));
  AddArgE(expmin,CopyE( time->upper_bound));
  Rewrite(expmin);
  FreeE(UnshareE( time->upper_bound));
  time->upper_bound = ShareE(expmin);
#endif

  if (!EqualE( time->lower_bound, time->upper_bound ) &&
      (time->tvar ==NULL)){
    if ((tvar=FindV("time", FindS("time")))==0)
      tvar =  Declare_var("time",FindS("time"));
    else
      tvar = EqualV_entry(tvar);
    time = (PTimeTyp)GetAInfo(ta);
    e1 =  MakeE(tvar ,VariableC);
    time->tvar = ShareE(e1);
    PutAInfo( (PAttrTyp)ta,(AttrValueTyp)time);
    Age1T(e1,act2);
  }
  else if   ( (time->tvar !=NULL) &&
	     (!IsConstE(time->tvar)))
    Age1T(time->tvar,act2) ;
  else
    Age1T(time->lower_bound,act2);

  PutArgB( pb,act2, 2);
#else
  PutArgB( pb,  b2 , 2 );
  out = CopyUntilActB( a1[i].branch );
#endif

  act = LookActionUB(out);
  (void)GetArgB( act, 1 );                /* Set its args to null */
  PutArgB( act, pb, 1 );

  return out;
}

/*----------------------------------------------------------------*/

/* Par_beh_gate
 * Expands the parallel composition of b1 with branch j of beh2
 * when branch j contains a visible action that doesn't have to synchronize.
 */
static BehTyp Par_beh_gate( b1, j )
     BehTyp b1;
     int j;
{
  BehTyp out,pb,act;

#ifdef TIME
  PTimeTyp time;
  PAttrTyp ta;
  BehTyp  act1;
  DescriptorTyp  tvar;
  ExprTyp e2;
#ifdef MUSTF
 DescriptorTyp  opmin;
 ExprTyp expmin;
#endif
#endif

  pb = CopyB( par );

#ifdef TIME
  act1 =  CopyUntilActB( b1);
  ShareB(act1);
   UnshareAttrsUntilActB(act1);
  UnshareB(act1);
  out =  CopyUntilActB( a2[j].branch );

  ShareB(out);
  UnshareAttrsUntilActB(out);
  UnshareB(out);
  ta = LookA(LookActionUB(out),TA);
  time = (PTimeTyp)LookAInfo(ta) ;

#ifdef MUSTF
  opmin = FindO_time("min");
  expmin = MakeE(opmin,OperationC);
  AddArgE(expmin,CopyE(end1));
  AddArgE(expmin,CopyE( time->upper_bound));
  Rewrite(expmin);
  FreeE(UnshareE( time->upper_bound));
  time->upper_bound =ShareE( expmin);
#endif

  if (!EqualE( time->lower_bound, time->upper_bound) &&
      (time->tvar ==NULL)){
    if ((tvar =FindV("time", FindS("time")))==0)
      tvar =  Declare_var("time",FindS("time"));
    else
      tvar = EqualV_entry(tvar);
    e2 = MakeE(tvar,VariableC);
    time = (PTimeTyp)GetAInfo(ta) ;
    time->tvar = ShareE(e2);
    PutAInfo(  (PAttrTyp) ta, (AttrValueTyp)time);
    Age1T(e2,act1);
  }
  else if ( (time->tvar !=NULL) &&
	   (!IsConstE(time->tvar)))
    Age1T(time->tvar,act1);
  else
    Age1T(time->lower_bound,act1);

  PutArgB( pb,act1, 1);
#else

  PutArgB( pb, b1 , 1 );
  out = CopyUntilActB( a2[j].branch );
#endif

  PutArgB( pb, LookArgB(a2[j].gate,1), 2 );
  act = LookActionUB(out);
  (void)GetArgB( act, 1 );                     /* Set its args to null */
  PutArgB( act, pb, 1 );

  return out;

}

/*----------------------------------------------------------------*/

/* Par_i_beh
 * Expands the parallel composition of branch i of beh1 with beh2
 * when branch i contains an invisible action.
 */
static BehTyp Par_i_beh( i, b2 )
     int i;
     BehTyp b2;
{
  return Par_gate_beh( i, b2 );
}

/*----------------------------------------------------------------*/

/* Par_beh_i
 * Expands the parallel composition of b1 with branch j of beh2
 * when branch j contains an invisible action.
 */
static BehTyp Par_beh_i( b1, j )
     BehTyp b1;
     int j;
{
  return Par_beh_gate( b1, j );
}

/*----------------------------------------------------------------*/

/* Append_DofferGuards
 * Takes a unitary behaviour chain of choices and guards and appends
 * to it the guards derived from Doffer()
 */
static BehTyp Append_DofferGuards(b)
     BehTyp b;
{
  BehTyp guard,out;
  ExprListTyp el, elh;

  out = NULL;
  elh = Doffer();
  for ( el = elh; el!=NULL; el = Next_list(el) ){
    guard = MakeB( 0, GuardC );
    PutA(guard,
	 MakeA((AttrValueTyp)NewPred(UnshareE((ExprTyp)GetInfo_list(el)),
				     (ExprTyp)NULL),PA));
    out = ConcatUB(out,guard);
  }
  Disp_list( elh );

  return ConcatUB(b,out);
}

/*----------------------------------------------------------------*/

/* Par_exit_exit
 * Expands the parallel composition of branch i of beh1 with branch j of beh2
 * when both branches contain exits.
 */
static BehTyp Par_exit_exit( i, j )
     int i,j;
{
  BehTyp out,gate,exit1,exit2,st;

  out = ConcatUB(ExtractUB( a1[i].branch, a2[j].branch, ChoiceC ),
		 ExtractUB( a1[i].branch, a2[j].branch, GuardC )  );
  out = Append_DofferGuards( out );
  gate = MakeB(0,ExitC);
  if ( Merge()!=NULL ) {
    PutA( gate, MakeA((AttrValueTyp)Merge(),OLA) );
  }
#ifdef TIME
  PutA( gate, MakeA((AttrValueTyp)Global_Time(),TA) );
#endif
  LASSERT(Global_Pred()==NULL);

  FreeVAL( Assign1() );
  FreeVAL( Assign2() );

  /* Synchronization Tree */

  if ( IS_STEP_EXP ) {
    st    = ShareB(CopyB(par));
    exit1 = a1[i].gate;
    exit2 = a2[j].gate;
    if ( !IsEventLeafST(exit1) )
      exit1 = (BehTyp)LookAInfo(LookA(exit1,STA));
    if ( !IsEventLeafST(exit2) )
      exit2 = (BehTyp)LookAInfo(LookA(exit2,STA));
    PutArgB( st, exit1, 1 );
    PutArgB( st, exit2, 2 );
    PutA( gate, MakeA((AttrValueTyp)st,STA) );
  }

  return ConcatUB(out,gate);
}

/*----------------------------------------------------------------*/

/* Par_gate_gate
 * Expands the parallel composition of branch i of beh1 with branch j of beh2
 * when both branches contain visible actions that have to synchronize.
 */
static BehTyp Par_gate_gate( i, j )
     int i,j;
{
  BehTyp out,act,let,pb,st,gate1,gate2;
#ifdef TIME
 BehTyp guardt;
#endif

  out = ConcatUB( ExtractUB(a1[i].branch,a2[j].branch,ChoiceC),
		 ExtractUB(a1[i].branch,a2[j].branch,GuardC)  );
  out = Append_DofferGuards( out );

#ifdef TIME
  if ( GuardT() !=NULL){
    guardt  = MakeB (0,GuardC);
    PutA( guardt , MakeA( (AttrValueTyp)NewPred( GuardT(),(ExprTyp)NULL),PA) );
    out = ConcatUB( out, guardt);
  }
#endif
  act = MakeB( LookNameB(a1[i].gate), GateC );
  if ( Merge()!=NULL ) {
    PutA( act, MakeA( (AttrValueTyp)Merge(), OLA) );
  }
  if ( Global_Pred()!=NULL ) {
    PutA( act, MakeA( (AttrValueTyp)Global_Pred(), PA) ); /*mse*/
    /* no hacer share del predicado. ya se hace dentro de exsynch.c */
  }

#ifdef TIME
   PutA (act, MakeA( (AttrValueTyp)Global_Time(), TA));
#endif

  out = ConcatUB( out, act );

  pb = CopyB(par);

  if ( Assign1()!=NULL ) {
    let = MakeB( 0, LetC );
    PutA( let, MakeA((AttrValueTyp)Assign1(),VALA) );
    PutArgB( pb, let, 1);
    PutArgB( let, LookArgB(a1[i].gate,1), 1 );
  }
  else
    PutArgB( pb, LookArgB(a1[i].gate,1), 1 );

  if ( Assign2()!=NULL ) {
    let = MakeB( 0, LetC );
    PutA( let, MakeA((AttrValueTyp)Assign2(), VALA) );
    PutArgB( pb, let, 2);
    PutArgB( let, LookArgB(a2[j].gate,1), 1 );
  }
  else
    PutArgB( pb, LookArgB(a2[j].gate,1), 2 );

  out = ConcatUB( out, pb );

  if ( IS_STEP_EXP ){
    st = ShareB(CopyB(par));
    gate1 = a1[i].gate;
    if ( !IsEventLeafST(gate1) )
      gate1 = (BehTyp)LookAInfo(LookA(gate1,STA));
    gate2 = a2[j].gate;
    if ( !IsEventLeafST(gate2) )
      gate2 = (BehTyp)LookAInfo(LookA(gate2,STA));
    PutArgB( st, gate1, 1 );
    PutArgB( st, gate2, 2 );
    PutA( LookActionUB(out), MakeA((AttrValueTyp)st,STA) );
  }
  return out;
}

/*----------------------------------------------------------------*/


/* ParExpandB1
 * Expands branch i of beh1
 */
static void ParExpandB1()
{
  int i,j;

#ifdef TIME
#ifdef MUSTF
  ExprTyp tmax,negativ;
  DescriptorTyp maxind,inf;
  PTimeTyp  time;

  if (LookAInfo(LookA(a2[1].gate,TA))!=NULL) {
    time = (PTimeTyp)LookAInfo(LookA( a2[1].gate,TA));
    tmax = time->upper_bound;
    for ( j=2; j<=numArg2; j++ ){
      if (LookAInfo(LookA(a2[j].gate,TA))!=NULL)
      time = (PTimeTyp)LookAInfo(LookA( a2[j].gate,TA));
      if (IsConstE(tmax) && IsConstE( time->upper_bound))
	if  (IsGt(time->upper_bound,tmax))
	  tmax = time->upper_bound;
  }
   end2 = CopyE(tmax);
  }
  else {
    maxind= FindO_time("inf");
    end2 = MakeE(maxind, OperationC);
  }
#endif
#endif

  for ( i=1; i<=numArg1; i++ )
    switch ( a1[i].type ) {
    case TerminationC:
      break;

    case IC:
      result = AppendUB( result, Par_i_beh( i, beh2p ) );
      break;

    case GateC:
      if ( a1[i].syncG ) {
	for ( j=1; j<=numArg2; j++ )
	  if ( a2[j].syncG && Synchr( a1[i].gate , a2[j].gate ) ) {
	    result = AppendUB( result, Par_gate_gate( i, j ) );
	  }
	  else {
	    if (IS_STEP_EXP && LookNameB(a1[i].gate)==LookNameB(a2[j].gate) )
	      UpdateNotSyncTree( a1[i].gate, a2[j].gate );
	  }
      }
      else {
#ifdef TIME
#ifdef  MUSTF
      time = (PTimeTyp)LookAInfo(LookA( a1[i].gate,TA));
      if  (!IsGt(time->lower_bound,end2))
#endif
#endif
	result = AppendUB( result, Par_gate_beh( i, beh2p ) );
    }
      break;

    case ExitC:
      for ( j=1; j<=numArg2; j++ )
	if ( a2[j].type==ExitC )
	  if ( Synchr( a1[i].gate , a2[j].gate ) ) {
	    result = AppendUB( result, Par_exit_exit( i, j ) );
	  }
	  else {
	    if ( IS_STEP_EXP )
	      UpdateNotSyncTree( a1[i].gate, a2[j].gate );
	  }

    default:
      break;

    }
}

/*----------------------------------------------------------------*/

/* ParExpandB2
 * Expands branch j of beh2
 */
static void ParExpandB2()
{
  int j;

#ifdef TIME
#ifdef MUSTF
  int i;
  ExprTyp tmax,aux, negativ;
  DescriptorTyp maxind,inf;
  PTimeTyp  time;

 if (LookAInfo(LookA( a1[1].gate,TA))!=NULL) {
 time = (PTimeTyp)LookAInfo(LookA( a1[1].gate,TA));
 tmax = time->upper_bound;
  for ( i=2; i<=numArg1; i++ ){
    if (LookAInfo(LookA( a1[i].gate,TA))!=NULL)
      time = (PTimeTyp)LookAInfo(LookA(  a1[i].gate,TA));
    if (IsConstE(tmax) && IsConstE( time->upper_bound))
      if  (IsGt(time->upper_bound,tmax))
	tmax = time->upper_bound;
  }
 end1 = CopyE(tmax);
}
 else {
   maxind= FindO_time("inf");
   end1 = MakeE(maxind, OperationC);
 }
#endif
#endif
  for ( j=1; j<=numArg2; j++ )
    switch ( a2[j].type )
      {
      case TerminationC:
	break;

      case IC:
	result = AppendUB( result, Par_beh_i( beh1p, j ) );
	break;

      case GateC:
	if (!a2[j].syncG){
#ifdef TIME
#ifdef  MUSTF
	  time = (PTimeTyp)LookAInfo(LookA( a2[j].gate,TA));
	  if  (!IsGt(time->lower_bound,end1))
#endif
#endif
	    result = AppendUB( result, Par_beh_gate( beh1p, j ) );
	}
	break;
      default:
	break;
      }
}

/******************************************************************
 *
 * Expansion of a parallel behaviour when the first argument begin
 * with internal actions, and not all the internal actions have
 * guards.
 * The same for the second argument.
 * Only for the testing expansion.
 *
 *******************************************************************/

/* Any_i_without_guard_B1
 * Returns TRUE if any of the internal actions offered by beh1 is not
 * preceeded by a guard.
 */
static boolean Any_i_without_guard_B1()
{
  int i;
  BehTyp b;

  LASSERT(numArg1!=0);
  for ( i=1; i<=numArg1; i++ ) {
    LASSERT(a1[i].type == IC);
    b = a1[i].branch;
    while (LookTypeB(b)==ChoiceC)
      b = LookArgB(b,1);
    if (LookTypeB(b)!=GuardC)
      return TRUE;
  }
  return FALSE;
}

/*----------------------------------------------------------------*/

/* Any_i_without_guard_B2
 * Returns TRUE if any of the internal actions offered by beh2 is not
 * preceeded by a guard.
 */
static boolean Any_i_without_guard_B2()
{
  int i;
  BehTyp b;

  LASSERT(numArg2!=0);
  for ( i=1; i<=numArg2; i++ ) {
    LASSERT(a2[i].type == IC);
    b = a2[i].branch;
    while (LookTypeB(b)==ChoiceC)
      b = LookArgB(b,1);
    if (LookTypeB(b)!=GuardC)
      return TRUE;
  }
  return FALSE;
}

/*----------------------------------------------------------------*/

/* ParExpandOnlyIinB1
 * Expands the branches which offers a internal action of beh1.
 */
static void ParExpandOnlyIinB1()
{
  int i;

  for ( i=1; i<=numArg1; i++ ) {
    LASSERT(a1[i].type == IC);
    if (a1[i].type == IC)
      result = AppendUB( result, Par_i_beh( i, beh2 ) );
  }
}

/*----------------------------------------------------------------*/

/* ParExpandOnlyIinB2
 * Expands the branches which offers a internal action of beh2.
 */
static void ParExpandOnlyIinB2()
{
  int i;

  for ( i=1; i<=numArg2; i++ ) {
    LASSERT(a2[i].type == IC);
    if ( a2[i].type == IC)
      result = AppendUB( result, Par_beh_i( beh1, i ) );
  }
}

/******************************************************************
 *
 * Expansion of a parallel behaviour when its arguments begin
 * both with internal actions.
 * Only for the testing expansion.
 *
 *******************************************************************/

/* OnlyIinBothArg
 * Returns TRUE if all the action in the tables of actions are internal
 * actions.
 * /
 static boolean OnlyIinBothArg()
 {
 int i;

 if ((numArg1==0) || (numArg2==0))
 return FALSE;
 for ( i=1; i<=numArg1; i++ )
 if (a1[i].type!=IC)
 return FALSE;
 for ( i=1; i<=numArg2; i++ )
 if (a2[i].type!=IC)
 return FALSE;
 return TRUE;
 }
 */
/*----------------------------------------------------------------*/

/* OnlyIinArg1
 * Returns TRUE if all the action in the table of actions "a1" are internal
 * actions.
 */
static boolean OnlyIinArg1()
{
  int i;

  if (numArg1==0)
    return FALSE;
  for ( i=1; i<=numArg1; i++ )
    if (a1[i].type!=IC)
      return FALSE;
  return TRUE;
}

/*----------------------------------------------------------------*/

/* OnlyIinArg2
 * Returns TRUE if all the action in the table of actions "a2" are internal
 * actions.
 */
static boolean OnlyIinArg2()
{
  int i;

  if (numArg2==0)
    return FALSE;
  for ( i=1; i<=numArg2; i++ )
    if (a2[i].type!=IC)
      return FALSE;
  return TRUE;
}

/*----------------------------------------------------------------*/

/* MakeChoice
 * Create a Choice behaviour with the choices operators of the branches
 * "i" from the first argument and "j" from the second one.
 */
static BehTyp MakeChoice(i,j)
     int i,j;
{
  ExprListTyp ch_el;
  BehTyp      auxb;

  LASSERT(i<=numArg1);
  LASSERT(j<=numArg2);

  ch_el = CreateEL();
  auxb =  a1[i].branch;
  while (LookTypeB(auxb)==ChoiceC) {
    ch_el = JoinEL(ch_el,CopyEL((ExprListTyp)LookAInfo(LookA(auxb,ELA))));
    auxb = LookArgB(auxb,1);
  }
  auxb =  a2[j].branch;
  while (LookTypeB(auxb)==ChoiceC) {
    ch_el = JoinEL(ch_el,CopyEL((ExprListTyp)LookAInfo(LookA(auxb,ELA))));
    auxb = LookArgB(auxb,1);
  }
  if (ch_el!=NULL) {
    auxb = MakeB(0,ChoiceC);
    PutA(auxb,MakeA((AttrValueTyp)ch_el,ELA));
    return auxb;
  }
  return NULL;
}

/*----------------------------------------------------------------*/

/* CopyGuards
 * Looks for the list of guards of the immediate behaviour "b" and
 * returns a copy of the list of guards.
 */
static BehTyp CopyGuards(b)
     BehTyp b;
{
  BehTyp res,aux1,aux2;

  while (LookTypeB(b)==ChoiceC)
    b = LookArgB(b,1);
  if (LookTypeB(b)==GuardC) {
    res = CopyUntilActB(b);
    aux1 = res;
    while (LookTypeB((aux2=LookArgB(aux1,1)))==GuardC)
      aux1 = aux2;
    FreeB(GetArgB(aux1,1));
    return res;
  }
  return NULL;
}

/*----------------------------------------------------------------*/

/* MakeBranch
 * Returns the behaviour: ch [g0]-> i ; ([g1]-> b1 |A| [g2]-> b2)
 *
 * ch,g0,g1 and g2 can be NULL.
 * g0,g1 and g2 can be a list of guards.
 */
static BehTyp MakeBranch(ch,g0,g1,b1,g2,b2)
     BehTyp ch,g0,g1,b1,g2,b2;
{
  BehTyp pb,i,aux1,aux2;

  pb = CopyB(par);

  if (g1==NULL)
    PutArgB(pb,b1,1);
  else {
    aux1 = g1;
    while ((aux2=LookArgB(aux1,1))!=NULL)
      aux1 = aux2;
    PutArgB(aux1,b1,1);
    PutArgB(pb,g1,1);
  }

  if (g2==NULL)
    PutArgB(pb,b2,2);
  else {
    aux1 = g2;
    while ((aux2=LookArgB(aux1,1))!=NULL)
      aux1 = aux2;
    PutArgB(aux1,b2,1);
    PutArgB(pb,g2,2);
  }

  i = MakeB(0,IC);
  PutArgB(i,pb,1);

  if (g0==NULL)
    g0 = i;
  else {
    aux1 = g0;
    while ((aux2=LookArgB(aux1,1))!=NULL)
      aux1 = aux2;
    PutArgB(aux1,i,1);
  }

  if (ch!=NULL){
    PutArgB(ch,g0,1);
    return ch;
  }
  return g0;
}

/*----------------------------------------------------------------*/

/* TestParExpand
 * Expansion when there are only internal actions in both arguments and
 * the mode is: testing and remove i's.
 */
static void TestParExpand()
{
  BehTyp ch,g1,b1,g2,b2,ch1;
  int i,j;

  for ( i=1; i<=numArg1; i++ )
    for ( j=1; j<=numArg2; j++ ) {

      ch = MakeChoice(i,j);
      g1 = CopyGuards(a1[i].branch);
      g2 = CopyGuards(a2[j].branch);
      b1 = LookArgB(a1[i].gate,1);
      b2 = LookArgB(a2[j].gate,1);

      if (g1==NULL)
	if (g2==NULL)
	  result = AppendUB(result,
			    MakeBranch(ch,(BehTyp)NULL,(BehTyp)NULL,
				       b1,(BehTyp)NULL,b2));
	else
	  result = AppendUB(result,MakeBranch(ch,(BehTyp)NULL,(BehTyp)NULL,
					      b1,g2,b2));
      else
	if (g2==NULL)
	  result = AppendUB(result,MakeBranch(ch,(BehTyp)NULL,g1,
					      b1,(BehTyp)NULL,b2));
	else {
	  ch1 = ch == NULL ? NULL : CopyB(ch);
	  result = AppendUB(result,
			    MakeBranch(ch1,CopyB(g1),(BehTyp)NULL,
				       b1,CopyB(g2),b2));
	  result = AppendUB(result,MakeBranch(ch,g2,g1,b1,(BehTyp)NULL,b2));
	}
    }
}

/******************************************************************
 *
 * Main function: Expansion of the parallel behaviour
 *
 *******************************************************************/

/* Par_Expand
 * Expansion of a parallel behaviour b.
 * Both arguments of b must be 1-immediate .That's to say:
 *
 *       Mxi                           Mxj
 *  b= ( SUMi( CHi Gi gi;Bi ) |[GSet]| SUMj( CHj Gj gj;Bj ) )
 *        1                             1
 *
 * where:
 *
 *   gk:= visible-action | i-action | exit        (Mxk>1)
 *   gk:= visible-action | i-action | exit | stop (Mxk=1)
 *
 *
 * This function is called with the following parameters:
 *
 *    Par_Expand( Exp(B1)|[A]|Exp(B2), B1, B2, rmI1 , rmI2 )
 *
 * where:
 *
 *  B1 and B2 are the arguments of the parallel operator before
 *  being pre_processed. They are used when a behaviour with IT
 *  operator is expanded. Example:
 *
 *      If ( IS_NOT_PRE_PROC_EXP ) then
 *         if Exp(B1)=g;B1', g not in A then
 *             Par_Expand( Exp(B1)|[A]|Exp(B2), B1, B2 ) = g;(B1'|A|B2)
 *
 *  rmI1 and rmI2 indicates that the first or the second argument of
 *  "b" only offer internal actions, such that the visible actions of
 *  the other branch can be delayed. (ONLY FOR TESTING).
 */
BehTyp Par_Expand( b , b1, b2, rmI1 , rmI2 )
     BehTyp b,b1,b2;
     boolean rmI1, rmI2;
{
  int etype;

  LASSERT( LookTypeB(b)==ParallelC );
  LASSERT ( OwnersB( b )>=0);

  result    = NULL;
  par       = b;
  syncClass = LookNameB(par);
  if ( syncClass == PART_SYNC )
    gs = (GateSetTyp)LookAInfo(LookA(b,GSA));
  beh1 = GetArgB( b, 1 );
  beh2 = GetArgB( b, 2 );

  if ( IS_NOT_PRE_PROC_EXP ){
    beh1p = b1;
    beh2p = b2;
  }
  else {
    beh1p = beh1;
    beh2p = beh2;
  }

  numArg1 = NumArgIB( beh1 );
  numArg2 = NumArgIB( beh2 );

  if (numArg1>=a1_size)
    ReallocActionTable( &a1,&a1_size,numArg1 );
  LASSERT (numArg1<a1_size);
  if (numArg2>=a2_size)
    ReallocActionTable( &a2,&a2_size,numArg2 );
  LASSERT (numArg2<a2_size);

  FillInActionTable( beh1, &a1, numArg1);
  FillInActionTable( beh2, &a2, numArg2);

  etype = 0;
  if (IS_WEAK_EQUIV_EXP && IS_TEST_EQUIV_EXP)  /* && OnlyIinBothArg()) { */
    if (OnlyIinArg1()) {
      if (OnlyIinArg2())
	etype = 3;
      else if (rmI1)
	etype = 1;
    }
    else
      if (rmI2 && OnlyIinArg2())
	etype = 2;

  if (etype==0) {      /* normal expansion */
    ParExpandB1();
    ParExpandB2();
  }
  else if (etype==1) { /* internal action in the first argument */
    ParExpandOnlyIinB1();
  }
  else if (etype==2) { /* internal action in the second argument */
    ParExpandOnlyIinB2();
  }
  else {               /* internal action in both arguments */
    TestParExpand();
  }

  LASSERT(LookCopy(par)==0); /**/
  FreeB( par );

  FreeB( beh1 );
  FreeB( beh2 );

  if ( result==NULL )
    result = MakeB( 0, StopC );

  if ( IS_NOT_PRE_PROC_EXP ) {
    FreeB(beh1p);
    FreeB(beh2p);
  }


#ifdef TIME
#ifdef MUSTF
  FreeE(end2);
  FreeE(end1);
#endif
#endif

  return result;
}

/*----------------------------------------------------------------*/



