// Low-level net class -*- c++ -*-

#ifdef __GNUC__
# pragma implementation
#endif // __GNUC__
#include "LNet.h"
#include "GlobalMarking.h"
#include "Net.h"
#include "Transition.h"
#include "Constraint.h"
#include "Printer.h"
#include "Place.h"
#include "Value.h"
#include "Valuation.h"
#include "Type.h"
#include "BitVector.h"
#include "DummyReporter.h"

/** @file LNet.C
 * Low-level (unfolded) net
 */

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

bool
LNet::LArc::unfold (class LNet& lnet,
		    const class GlobalMarking& m)
{
  for (unsigned i = m.getSize (); i--; ) {
    const class PlaceMarking& p = m[i];
    if (p.empty () || p.getPlace ()->isConstant ()) continue;
    for (PlaceMarking::const_iterator pi = p.begin (); pi != p.end (); pi++)
      if (pi->second && !add (lnet.addPlace (i, *pi->first), pi->second))
	return false;
  }
  return true;
}

bool
LNet::LArc::read (FILE* f)
{
  unsigned numPlaces;
  if (1 != fread (&numPlaces, sizeof numPlaces, 1, f))
    return false;
  unsigned* places = new unsigned[numPlaces];
  unsigned* weights = new unsigned[numPlaces];
  if (places && weights &&
      numPlaces == fread (places, sizeof *places, numPlaces, f) &&
      numPlaces == fread (weights, sizeof *weights, numPlaces, f)) {
    delete[] myPlaces;
    delete[] myWeights;
    myNumPlaces = numPlaces, myPlaces = places, myWeights = weights;
    return true;
  }
  delete[] places;
  delete[] weights;
  return false;
}

bool
LNet::LArc::write (FILE* f) const
{
  return
    1 == fwrite (&myNumPlaces, sizeof myNumPlaces, 1, f) &&
    myNumPlaces == fwrite (myPlaces, sizeof *myPlaces, myNumPlaces, f) &&
    myNumPlaces == fwrite (myWeights, sizeof *myWeights, myNumPlaces, f);
}

LNet::LNet (const class Net& net) :
  myNet (net),
  myPlaceBits (net.getNumPlaces () ? log2 (net.getNumPlaces ()) : 0),
  myNumPlaces (0), myPlaces (0), myPlaceMap (), myBuf (),
  myTransitions (0)
{
  unsigned tr = myNet.getNumAllTransitions ();
  myTransitions = new FILE*[net.getNumAllTransitions ()];
  while (tr--)
    myTransitions[tr] = tmpfile ();
}

LNet::~LNet ()
{
  delete[] myPlaces;
  for (iterator i = myPlaceMap.begin (); i != myPlaceMap.end (); i++)
    delete[] i->first.name;
  for (unsigned tr = myNet.getNumAllTransitions (); tr--; )
    fclose (myTransitions[tr]);
  delete[] myTransitions;
}

bool
LNet::generateMinimal (const class Printer& printer,
		       unsigned maxerrors)
{
  assert (!myNumPlaces);
  unsigned i;
  /** Number of low-level places when high-level transitions were unfolded */
  unsigned* numPlaces = new unsigned[myNet.getNumAllTransitions ()];
  memset (numPlaces, 0, myNet.getNumAllTransitions () * sizeof *numPlaces);

  /** The coverable marking */
  class GlobalMarking cover (*myNet.getInitMarking ());

  // Introduce low-level places for the initial marking
  for (i = myNet.getNumPlaces (); i--; )
    if (myNet.getPlace (i) &&
	myNet.getPlace (i)->isConstant ())
      addPlaces (i, cover[i]);

  /** Flag: was the computation interrupted? */
  extern volatile bool interrupted;
  class DummyReporter reporter (myNet, printer, maxerrors);

  while (!interrupted && !reporter.isFatal ()) {
    unsigned n = myNumPlaces;
    for (i = myNet.getNumAllTransitions (); !interrupted && i--; ) {
      assert (myNumPlaces >= numPlaces[i]);
      // Have any new low-level places been generated?
      if (myNumPlaces <= numPlaces[i] && myNumPlaces)
	continue;
      numPlaces[i] = myNumPlaces;
      myNet.getTransition (i).unfold (cover, *this, reporter);
    }
    if (n == myNumPlaces)
      break;
  }

  delete[] numPlaces;
  return !interrupted && !reporter.isFatal ();
}

bool
LNet::generate (const class Printer& printer,
		unsigned maxerrors)
{
  assert (!myNumPlaces);
  /** Flag: was the computation interrupted? */
  extern volatile bool interrupted;
  unsigned i;

  // Introduce low-level places for the initial marking
  const class GlobalMarking& m = *myNet.getInitMarking ();
  for (i = myNet.getNumPlaces (); i--; )
    if (myNet.getPlace (i) &&
	!myNet.getPlace (i)->isConstant ())
      addPlaces (i, m[i]);

  class DummyReporter reporter (myNet, printer, maxerrors);

  for (i = myNet.getNumAllTransitions ();
       !interrupted && !reporter.isFatal () && i--; )
    myNet.getTransition (i).unfold (*this, reporter);

  return !interrupted && !reporter.isFatal ();
}

unsigned
LNet::getPlace (unsigned p, const class Value& value) const
{
  assert (myBuf.empty ());
  if (myPlaceBits) myBuf.append (p, myPlaceBits);
  myBuf.append (value);
  unsigned place = getPlace (myBuf.getBuf (), myBuf.getLength ());
  myBuf.clear ();
  return place;
}

unsigned
LNet::addPlace (unsigned p, const class Value& value)
{
  assert (myBuf.empty ());
  assert (myNet.getPlace (p) && !myNet.getPlace (p)->isConstant ());
  if (myPlaceBits) myBuf.append (p, myPlaceBits);
  myBuf.append (value);
  unsigned length = myBuf.getNumWords ();
  word_t* placename = new word_t[length];
  memcpy (placename, myBuf.getBuf (), length * sizeof *placename);
  length = myBuf.getLength ();
  myBuf.clear ();
  return addPlace (placename, length);
}

void
LNet::addPlaces (unsigned place,
		 const class PlaceMarking& p)
{
  assert (myBuf.empty ());
  assert (!p.getPlace ()->isConstant ());
  if (p.empty ()) return;
  for (PlaceMarking::const_iterator i = p.begin (); i != p.end (); i++) {
    if (!i->second) continue;
    if (myPlaceBits) myBuf.append (place, myPlaceBits);
    myBuf.append (*i->first);
    unsigned length = myBuf.getNumWords ();
    word_t* placename = new word_t[length];
    memcpy (placename, myBuf.getBuf (), length * sizeof *placename);
    addPlace (placename, myBuf.getLength ());
    myBuf.clear ();
  }
}

void
LNet::removeTransitions (unsigned t)
{
  assert (t < myNet.getNumAllTransitions ());
  rewind (myTransitions[t]);
}

bool
LNet::addTransition (const struct ltrans& tr)
{
  assert (tr.t < myNet.getNumAllTransitions ());
  assert (!tr.datalen || tr.data);
  FILE* f = myTransitions[tr.t];
  return tr.in.write (f) && tr.out.write (f) &&
    1 == fwrite (&tr.datalen, sizeof tr.datalen, 1, f) &&
    tr.datalen == fwrite (tr.data, sizeof *tr.data, tr.datalen, f);
}

unsigned*
LNet::unfold (const class GlobalMarking& m) const
{
  assert (myBuf.empty ());
  if (!myNumPlaces) return 0;
  unsigned* marking = new unsigned[myNumPlaces];
  memset (marking, 0, myNumPlaces * sizeof * marking);
  for (unsigned i = m.getSize (); i--; ) {
    const class PlaceMarking& p = m[i];
    if (p.empty () || p.getPlace ()->isConstant ()) continue;
    for (PlaceMarking::const_iterator pi = p.begin (); pi != p.end (); pi++) {
      if (myPlaceBits) myBuf.append (i, myPlaceBits);
      myBuf.append (*pi->first);
      unsigned lp = getPlace (myBuf.getBuf (), myBuf.getLength ());
      myBuf.clear ();
      if (lp == UINT_MAX) {
	delete[] marking;
	return 0;
      }
      assert (lp < myNumPlaces);
      marking[lp] = pi->second;
    }
  }
  return marking;
}

class GlobalMarking&
LNet::fold (const unsigned* marking) const
{
  class GlobalMarking& m = *new class GlobalMarking (myNet);
  for (unsigned lp = myNumPlaces; lp--; ) {
    if (!marking[lp]) continue;
    class BitUnpacker buf (myPlaces[lp]);
    unsigned i = myPlaceBits ? buf.extract (myPlaceBits) : 0;
    class PlaceMarking& p = m[i];
    p.add (*buf.extract (p.getPlace ()->getType ()), marking[lp]);
  }
  return m;
}

/** Read a low-level transition
 * @param t	number of the high-level transition
 * @param f	the input stream
 * @return	a low-level transition, or NULL if end of stream
 */
static const struct LNet::ltrans*
nextTransition (unsigned t, FILE* f)
{
  static struct LNet::ltrans tr;
  tr.t = t;
  delete[] tr.data; tr.data = 0;
  if (!tr.in.read (f) || !tr.out.read (f) ||
      1 != fread (&tr.datalen, sizeof tr.datalen, 1, f))
    return 0;
  tr.data = new word_t[tr.datalen];
  return
    tr.datalen == fread (tr.data, sizeof *tr.data, tr.datalen, f) ? &tr : 0;
}

const class Place&
LNet::getPlace (unsigned p) const
{
  assert (p < myNumPlaces);
  class BitUnpacker buf (myPlaces[p]);
  return *myNet.getPlace (myPlaceBits ? buf.extract (myPlaceBits) : 0);
}

const class Place&
LNet::displayPlace (class StringBuffer& out,
		    unsigned p) const
{
  assert (p < myNumPlaces);
  class BitUnpacker buf (myPlaces[p]);
  const class Place& place =
    *myNet.getPlace (myPlaceBits ? buf.extract (myPlaceBits) : 0);
  out.append ("PL_");
  p = out.getLength ();
  out.append (place.getName ());
  out.escape (p);
  if (place.getType ().getNumValues () > 1) {
    class Value* v = buf.extract (place.getType ());
    out.append ('_', 2);
    p = out.getLength ();
    class Printer printer;
    printer.setOutput (&out);
    v->display (printer);
    delete v;
    out.escape (p);
  }
  return place;
}

void
LNet::displayTransition (class StringBuffer& out,
			 const struct ltrans& t) const
{
  class BitUnpacker buf (t.data);
  const class Transition* transition;
  class Valuation valuation;
  myNet.decode (buf, transition, valuation, true);
  assert (transition == &myNet.getTransition (t.t));
  out.append ("TR_");
  unsigned i = out.getLength ();
  out.append (transition->getName ());
  out.escape (i);
  valuation.displayEscaped (out);
}

void
LNet::display (const class Printer& printer, const unsigned* marking) const
{
  unsigned i;
  if (myNumPlaces && marking) {
    for (i = 0; i < myNumPlaces; i++) {
      if (!marking[i]) continue;
      class BitUnpacker buf (myPlaces[i]);
      const class Place& p = *myNet.getPlace (myPlaceBits
					      ? buf.extract (myPlaceBits)
					      : 0);
      printer.printRaw ("place ");
      printer.printQuoted (p.getName ());
      printer.delimiter (':')++;
      class Value* v = buf.extract (p.getType ());
      v->display (printer);
      delete v;
      printer--.finish ();
    }
  }
  for (unsigned tr = 0; tr < myNet.getNumAllTransitions (); tr++) {
    rewind (myTransitions[tr]);
    while (const struct ltrans* t = nextTransition (tr, myTransitions[tr])) {
      class BitUnpacker buf (t->data);
      const class Transition* transition;
      class Valuation valuation;
      myNet.decode (buf, transition, valuation, true);
      assert (transition == &myNet.getTransition (tr));
      printer.printRaw ("trans ");
      printer.printQuoted (myNet.getTransition (tr).getName ());
      valuation.display (printer);
      printer.linebreak ();
      printer.delimiter ('<')++;
      for (i = t->in.getNumPlaces (); i--; ) {
	class BitUnpacker pbuf (myPlaces[t->in.getPlace (i)]);
	const class Place& p = *myNet.getPlace (myPlaceBits
						? pbuf.extract (myPlaceBits)
						: 0);
	printer.linebreak ();
	if (t->in.getWeight (i) != 1) {
	  printer.print (t->in.getWeight (i));
	  printer.delimiter ('#');
	}
	printer.printQuoted (p.getName ());
	printer.delimiter (':');
	class Value* v = pbuf.extract (p.getType ());
	v->display (printer);
	delete v;
      }
      printer--.linebreak ();
      printer.delimiter ('>')++;
      for (i = t->out.getNumPlaces (); i--; ) {
	class BitUnpacker pbuf (myPlaces[t->out.getPlace (i)]);
	const class Place& p = *myNet.getPlace (myPlaceBits
						? pbuf.extract (myPlaceBits)
						: 0);
	printer.linebreak ();
	if (t->out.getWeight (i) != 1) {
	  printer.print (t->out.getWeight (i));
	  printer.delimiter ('#');
	}
	printer.printQuoted (p.getName ());
	printer.delimiter (':');
	class Value* v = pbuf.extract (p.getType ());
	v->display (printer);
	delete v;
      }
      printer--.finish ();
    }
  }
}

void
LNet::toLoLA (FILE* file, const unsigned* marking) const
{
  unsigned i;
  class StringBuffer out;
  /** flag: print a comma? */
  bool comma;
  if (myNumPlaces) {
    out.append ("PLACE ");
    for (i = 0, comma = false; i < myNumPlaces; i++) {
      if (comma) out.append (',', 1); comma = true;
      displayPlace (out, i);
    }
    out.append (";\n");
    fwrite (out.getString (), 1, out.getLength (), file);
    out.create (0);
  }
  if (marking) {
    out.append ("MARKING ");
    for (i = 0, comma = false; i < myNumPlaces; i++) {
      if (!marking[i]) continue;
      if (comma) out.append (',', 1); comma = true;
      displayPlace (out, i);
      out.append (':', 1);
      out.append (marking[i]);
    }
    out.append (";\n");
    fwrite (out.getString (), 1, out.getLength (), file);
    out.create (0);
  }
  i = 0;
  for (unsigned tr = 0; tr < myNet.getNumAllTransitions (); tr++) {
    rewind (myTransitions[tr]);
    while (const struct ltrans* t = nextTransition (tr, myTransitions[tr])) {
      out.append ("TRANSITION ");
      displayTransition (out, *t);
      out.append ("\nCONSUME ");
      unsigned j;
      if ((j = t->in.getNumPlaces ())) {
	assert (j <= myNumPlaces);
	for (j--;;) {
	  assert (t->in.getPlace (j) < myNumPlaces && t->in.getWeight (j) > 0);
	  displayPlace (out, t->in.getPlace (j));
	  out.append (':', 1);
	  out.append (t->in.getWeight (j));
	  if (!j--)
	    break;
	  out.append (',', 1);
	}
      }
      out.append (";\nPRODUCE ");
      if ((j = t->out.getNumPlaces ())) {
	assert (j <= myNumPlaces);
	for (j--;;) {
	  assert (t->out.getPlace (j) < myNumPlaces &&
		  t->out.getWeight (j) > 0);
	  displayPlace (out, t->out.getPlace (j));
	  out.append (':', 1);
	  out.append (t->out.getWeight (j));
	  if (!j--)
	    break;
	  out.append (',', 1);
	}
      }
      out.append (";\n");
      fwrite (out.getString (), 1, out.getLength (), file);
      out.create (0);
    }
  }
}

void
LNet::toPEP (FILE* file, const unsigned* marking) const
{
  unsigned i, j;
  class StringBuffer out;
  fputs ("PEP\n"
	 "PTNet\n"
	 "FORMAT_N\n"
	 "PL\n", file);
  for (i = 0; i < myNumPlaces; i++) {
    out.append (i + 1);
    out.append ('"', 1);
    const class Place& p = displayPlace (out, i);
    out.append ("\"0@0");
    if (marking && marking[i]) {
      out.append ('M', 1), out.append (marking[i]);
      out.append ('m', 1), out.append (marking[i]);
    }
    if (const class Constraint* capacity = p.getCapacity ()) {
      out.append ('k', 1);
      class Printer printer;
      printer.setOutput (&out);
      capacity->getLastValue ().display (printer);
    }
    else
      out.append ("k-1");
    out.append ('\n', 1);
    fwrite (out.getString (), 1, out.getLength (), file);
    out.create (0);
  }
  fputs ("TR\n", file);
  unsigned tr;
  for (tr = i = 0; tr < myNet.getNumAllTransitions (); tr++) {
    rewind (myTransitions[tr]);
    while (const struct ltrans* t = nextTransition (tr, myTransitions[tr])) {
      out.append (++i);
      out.append ('"', 1);
      displayTransition (out, *t);
      out.append ("\"0@0\n");
      fwrite (out.getString (), 1, out.getLength (), file);
      out.create (0);
    }
  }
  fputs ("TP\n", file);
  for (tr = i = 0; tr < myNet.getNumAllTransitions (); tr++) {
    rewind (myTransitions[tr]);
    while (const struct ltrans* t = nextTransition (tr, myTransitions[tr])) {
      i++;
      for (j = t->out.getNumPlaces (); j--; ) {
	assert (t->out.getPlace (j) < myNumPlaces && t->out.getWeight (j) > 0);
	fprintf (file, "%u<%u", i, t->out.getPlace (j) + 1);
	if (t->out.getWeight (j) != 1)
	  fprintf (file, "w%u", t->out.getWeight (j));
	fputc ('\n', file);
      }
    }
  }
  fputs ("PT\n", file);
  for (tr = i = 0; tr < myNet.getNumAllTransitions (); tr++) {
    rewind (myTransitions[tr]);
    while (const struct ltrans* t = nextTransition (tr, myTransitions[tr])) {
      i++;
      for (j = t->in.getNumPlaces (); j--; ) {
	assert (t->in.getPlace (j) < myNumPlaces && t->in.getWeight (j) > 0);
	fprintf (file, "%u>%u", t->in.getPlace (j) + 1, i);
	if (t->in.getWeight (j) != 1)
	  fprintf (file, "w%u", t->in.getWeight (j));
	fputc ('\n', file);
      }
    }
  }
}

void
LNet::toPROD (FILE* file, FILE* extfile, const unsigned* marking) const
{
  bool comma;
  unsigned k;
  unsigned kt;
  unsigned ku;
  class StringBuffer out;

  /** Counter for low-level transitions */
  size_t si = 0;
  /** Counter for high-level transitions */
  unsigned tr;
  for (tr = 0; tr < myNet.getNumAllTransitions (); tr++) {
    rewind (myTransitions[tr]);
    while (nextTransition (tr, myTransitions[tr]))
      si++;
    rewind (myTransitions[tr]);
  }

  if (myNumPlaces) {
    for (unsigned vis = 0; vis < 2; vis++) {
      for (k = myNumPlaces, comma = false; k--;) {
	if (getPlace (k).isVisible () != !vis) {
	  out.append ("#enum  ");
	  displayPlace (out, k);
	  out.append ('\n', 1);
	  fwrite (out.getString (), 1, out.getLength (), file);
	  out.create (0);
	  comma = true;
	}
      }
      if (!comma) continue;
      fputs (vis ? "#place v" : "#place i", file);
      if (marking) {
	comma = false;
	for (k = 0; k < myNumPlaces; k++) {
	  if (getPlace (k).isVisible () == !vis) continue;
	  if (!marking[k]) continue;
	  fputs (comma
		 ? " \\\n          + "
		 : " \\\n       mk ( ", file);
	  comma = true;
	  if (1 != marking[k]) fprintf (file, "%u ", marking[k]);
	  out.append ("<. ");
	  displayPlace (out, k);
	  out.append (".>");
	  fwrite (out.getString (), 1, out.getLength (), file);
	  out.create (0);
	}
	fputs (comma ? " )\n" : "\n", file);
      }
    }
  }
  else if (!si)
    return;

  /** Visited and processed low-level transitions */
  class BitVector visited (si), processed (si);
  fputs ("#place q\n"
	 "extern unsigned long z[1];\n", file);
  fputs ("#include \"_enum.h\"\n"
	 "unsigned long z[1] = { 0 };\n", extfile);
  for (unsigned pattern = 0;; pattern++) {
    /** number of low-level transitions skipped inside this transition */
    unsigned ss = 0;
    const struct ltrans* t;
    for (tr = 0, si = 0; tr < myNet.getNumAllTransitions (); tr++) {
      for (ss = 0; (t = nextTransition (tr, myTransitions[tr])); ss++, si++)
	if (!visited.tset (si))
	  goto visit;
      rewind (myTransitions[tr]);
    }
    break;
  visit:
    struct ltrans u (*t);
    u.data = new word_t[u.datalen];
    memcpy (u.data, t->data, u.datalen * sizeof *u.data);
    /** number of input places */
    const unsigned uin = u.in.getNumPlaces ();
    /** number of visible input places */
    unsigned vin = 0;
    for (k = uin; k--; )
      if (getPlace (u.in.getPlace (k)).isVisible ())
	vin++;
    /** number of output places */
    const unsigned uout = u.out.getNumPlaces ();
    /** number of visible output places */
    unsigned vout = 0;
    for (k = uout; k--; )
      if (getPlace (u.out.getPlace (k)).isVisible ())
	vout++;
    /** number of low-level transitions folded in this round */
    unsigned n = 1;
    /** start and end index for the low-level transitions in this round */
    size_t sa = si++, sb = 0;
    out.append ("#enum  ");
    displayTransition (out, u);
    out.append ('\n', 1);
    /** start index of the high-level transition in this round */
    unsigned ta = tr;
    assert (ta < myNet.getNumAllTransitions ());
    for (; ta < myNet.getNumAllTransitions (); rewind (myTransitions[ta++])) {
      for (; (t = nextTransition (ta, myTransitions[ta])); si++) {
	if (visited[si]) continue;
	if (t->in.getNumPlaces () != uin) continue;
	for (k = 0, kt = 0; k < uin; k++)
	  if (getPlace (t->in.getPlace (k)).isVisible ())
	    kt++;
	if (kt != vin) continue;
	if (uin > vin) {
	  for (kt = 0, ku = 0; ; kt++, ku++) {
	    while ((kt < uin) && getPlace (t->in.getPlace (kt)).isVisible ())
	      kt++;
	    if (kt == uin) break;
	    assert (ku < uin);
	    while (getPlace (u.in.getPlace (ku)).isVisible ())
	      ku++, assert (ku < uin);
	    if (t->in.getWeight (kt) != u.in.getWeight (ku)) break;
	  }
	  if (kt != uin) continue;
	}
	if (vin) {
	  for (kt = 0, ku = 0; ; kt++, ku++) {
	    while ((kt < uin) && !getPlace (t->in.getPlace (kt)).isVisible ())
	      kt++;
	    if (kt == uin) break;
	    assert (ku < uin);
	    while (!getPlace (u.in.getPlace (ku)).isVisible ())
	      ku++, assert (ku < uin);
	    if (t->in.getWeight (kt) != u.in.getWeight (ku)) break;
	  }
	  if (kt != uin) continue;
	}
	if (t->out.getNumPlaces () != uout) continue;
	for (k = 0, kt = 0; k < uout; k++)
	  if (getPlace (t->out.getPlace (k)).isVisible ())
	    kt++;
	if (kt != vout) continue;
	if (uout > vout) {
	  for (kt = 0, ku = 0; ; kt++, ku++) {
	    while ((kt < uout) && getPlace (t->out.getPlace (kt)).isVisible ())
	      kt++;
	    if (kt == uout) break;
	    assert (ku < uout);
	    while (getPlace (u.out.getPlace (ku)).isVisible ())
	      ku++, assert (ku < uout);
	    if (t->out.getWeight (kt) != u.out.getWeight (ku)) break;
	  }
	  if (kt != uout) continue;
	}
	if (vout) {
	  for (kt = 0, ku = 0; ; kt++, ku++) {
	    while (kt < uout && !getPlace (t->out.getPlace (kt)).isVisible ())
	      kt++;
	    if (kt == uout) break;
	    assert (ku < uout);
	    while (!getPlace (u.out.getPlace (ku)).isVisible ())
	      ku++, assert (ku < uout);
	    if (t->out.getWeight (kt) != u.out.getWeight (ku)) break;
	  }
	  if (kt != uout) continue;
	}
	out.append ("#enum  ");
	displayTransition (out, *t);
	out.append ('\n', 1);
	fwrite (out.getString (), 1, out.getLength (), file);
	out.create (0);
	visited.assign (sb = si, true);
	n++;
      }
    }
    if (uout) {
      fprintf (file, "extern unsigned long y%u[%u][%u];\n",
	       pattern, n, uout);
      fprintf (extfile, "unsigned long y%u[%u][%u] =\n",
	       pattern, n, uout);
    }
    out.append ("#trans u");
    out.append (pattern);
    out.append ("\nlet <. t");
    for (k = 0; k < uin; k++)
      out.append (",\n       x"), out.append (k);
    out.append (" .>\n");
    fwrite (out.getString (), 1, out.getLength (), file);
    out.create (0);

    // rewind the transition file
    while (ss--)
      if (!nextTransition (tr, myTransitions[tr]))
	assert (false);
    for (ta = tr, si = sa, comma = false;
	 ta < myNet.getNumAllTransitions ();
	 rewind (myTransitions[ta++])) {
      for (; (t = nextTransition (ta, myTransitions[ta])); si++) {
	if (!visited[si] || processed.tset (si)) continue;
	assert (t->in.getNumPlaces () == u.in.getNumPlaces ());
	assert (t->out.getNumPlaces () == u.out.getNumPlaces ());
	if (!comma) {
	  comma = true;
	  fputs (" <= ", file);
	  if (uout) fputs ("{\n", extfile);
	}
	else
	  fputs ("\n  + ", file);
	if (uout) {
	  out.append ("  /* ");
	  displayTransition (out, *t);
	  out.append (" */\n  { ");
	  fwrite (out.getString (), 1, out.getLength (), extfile);
	  out.create (0);
	}
	out.append ("<. ");
	displayTransition (out, *t);
	for (k = 0; k < uin; k++) {
	  if (!getPlace (t->in.getPlace (k)).isVisible ()) {
	    out.append (",\n       ");
	    displayPlace (out, t->in.getPlace (k));
	  }
	}
	for (k = 0; k < uin; k++) {
	  if (getPlace (t->in.getPlace (k)).isVisible ()) {
	    out.append (",\n       ");
	    displayPlace (out, t->in.getPlace (k));
	  }
	}
	out.append (" .>");
	fwrite (out.getString (), 1, out.getLength (), file);
	out.create (0);
	if (!uout) continue;
	for (k = 0, kt = 0; k < uout; k++) {
	  if (!getPlace (t->out.getPlace (k)).isVisible ()) {
	    if (kt) out.append ("    ");
	    displayPlace (out, t->out.getPlace (k));
	    if (++kt < uout) out.append (",\n");
	  }
	}
	for (k = 0; k < uout; k++) {
	  if (getPlace (t->out.getPlace (k)).isVisible ()) {
	    if (kt) out.append ("    ");
	    displayPlace (out, t->out.getPlace (k));
	    if (++kt < uout) out.append (",\n");
	  }
	}
	out.append (" }");
	fwrite (out.getString (), 1, out.getLength (), extfile);
	out.create (0);
	if (si != sb)
	  fputs (",\n", extfile);
	else
	  break;
      }
    }

    if (uout) fputs ("\n};\n", extfile);
    fputs (" ;\nin  { q : ( z [ 0 ] ) <. t .> ;", file);
    if (uin > vin) {
      fputs ("\n      i : ", file);
      for (k = 0, kt = 0; k < uin; k++) {
	if (!getPlace (u.in.getPlace (k)).isVisible ()) {
	  if (kt) fputs ("\n        + ", file);
	  assert (0 < u.in.getWeight (k));
	  if (1 != u.in.getWeight (k))
	    fprintf (file, "%u ", u.in.getWeight (k));
	  fprintf (file, "<. x%u .>", kt);
	  kt++;
	}
      }
      fputs (" ;", file);
    }
    if (vin) {
      fputs ("\n      v : ", file);
      for (k = 0, kt = 0; k < uin; k++) {
	if (getPlace (u.in.getPlace (k)).isVisible ()) {
	  if (kt) fputs ("\n        + ", file);
	  assert (0 < u.in.getWeight (k));
	  if (1 != u.in.getWeight (k))
	    fprintf (file, "%u ", u.in.getWeight (k));
	  fprintf (file, "<. x%u .>", kt + uin - vin);
	  kt++;
	}
      }
      fputs (" ;", file);
    }
    fputs (" }\nout { q : ( z [ 0 ] ) <. t .> ;", file);
    if (uout > vout) {
      out.append ("\n      i : ");
      for (k = 0, kt = 0; k < uout; k++) {
	if (!getPlace (u.out.getPlace (k)).isVisible ()) {
	  if (kt) out.append ("\n        + ");
	  assert (0 < u.out.getWeight (k));
	  if (1 != u.out.getWeight (k))
	    out.append (u.out.getWeight (k)), out.append (' ', 1);
	  out.append ("<. y"), out.append (pattern);
	  out.append (" [ ");
	  displayTransition (out, u);
	  out.append (" - t ] [ "), out.append (kt++), out.append (" ] .>");
	}
      }
      out.append (" ;");
    }
    if (vout) {
      out.append ("\n      v : ");
      for (k = 0, kt = 0; k < uout; k++) {
	if (getPlace (u.out.getPlace (k)).isVisible ()) {
	  if (kt) out.append ("\n        + ");
	  assert (0 < u.out.getWeight (k));
	  if (1 != u.out.getWeight (k))
	    out.append (u.out.getWeight (k)), out.append (' ', 1);
	  out.append ("<. y"), out.append (pattern);
	  out.append (" [ ");
	  displayTransition (out, u);
	  out.append (" - t ] [ ");
	  out.append (uout - vout + kt++);
	  out.append (" ] .>");
	}
      }
      out.append (" ;");
    }
    fwrite (out.getString (), 1, out.getLength (), file);
    out.create (0);
    fputs (" }\n#endtr\n", file);
    delete[] u.data;
  }
}
