/*******************************************************************/
/*                                                                 */
/*  FILE     interface.C                                           */
/*  MODULE   interface                                             */
/*  PROGRAM  SFST                                                  */
/*  AUTHOR   Helmut Schmid, IMS, University of Stuttgart           */
/*                                                                 */
/*******************************************************************/

#include <fstream>
#include <set>

#include "interface.h"

using std::ifstream;
using std::vector;

namespace SFST {
  
  /*******************************************************************/
  /*                                                                 */
  /*  error                                                          */
  /*                                                                 */
  /*******************************************************************/

  void error( const char *message )

  {
    cerr << "\nError: " << message << "\naborted.\n";
    exit(1);
  }


  /*******************************************************************/
  /*                                                                 */
  /*  error2                                                         */
  /*                                                                 */
  /*******************************************************************/

  void error2( const char *message, char *input )

  {
    cerr << "\nError: " << message << ": " << input << "\naborted.\n";
    exit(1);
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::symbol_code                                         */
  /*                                                                 */
  /*******************************************************************/

  Character Interface::symbol_code( char *symbol )

  {
    int c=TheAlphabet.symbol2code(symbol);
    if (c == EOF)
      c = TheAlphabet.add_symbol( symbol );
    free(symbol);
    return (Character)c;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::character_code                                      */
  /*                                                                 */
  /*******************************************************************/

  Character Interface::character_code( unsigned int uc )

  {
    if (TheAlphabet.utf8)
      return symbol_code(fst_strdup(int2utf8(uc)));

    unsigned char *buffer=(unsigned char*)malloc(2);
    buffer[0] = (unsigned char)uc;
    buffer[1] = 0;

    return symbol_code((char*)buffer);
  }
  
  
  /*******************************************************************/
  /*                                                                 */
  /*  Interface::add_value                                           */
  /*                                                                 */
  /*******************************************************************/

  Range *Interface::add_value( Character c, Range *r )

  {
    Range *result=new Range;
    result->character = c;
    result->next = r;
    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::add_values                                          */
  /*                                                                 */
  /*******************************************************************/

  Range *Interface::add_values( unsigned int c1, unsigned int c2, Range *r )

  {
    for( unsigned int c=c2; c>=c1; c-- )
      r = add_value(character_code(c), r);
    return r;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::append_values                                       */
  /*                                                                 */
  /*******************************************************************/

  Range *Interface::append_values( Range *r2, Range *r )

  {
    if (r2 == NULL)
      return r;
    return add_value(r2->character, append_values(r2->next, r));
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::add_var_values                                      */
  /*                                                                 */
  /*******************************************************************/

  Range *Interface::add_var_values( char *name, Range *r )

  {
    return append_values(svar_value(name), r);
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::in_range                                            */
  /*                                                                 */
  /*******************************************************************/

  bool Interface::in_range( unsigned int c, Range *r )

  {
    while (r) {
      if (r->character == c)
	return true;
      r = r->next;
    }
    return false;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  free_values                                                    */
  /*                                                                 */
  /*******************************************************************/

  static void free_values( Range *r )

  {
    if (r) {
      free_values(r->next);
      delete r;
    }
  }


  /*******************************************************************/
  /*                                                                 */
  /*  free_values                                                    */
  /*                                                                 */
  /*******************************************************************/

  static void free_values( Ranges *r )

  {
    if (r) {
      free_values(r->next);
      delete r;
    }
  }


  /*******************************************************************/
  /*                                                                 */
  /*  free_contexts                                                  */
  /*                                                                 */
  /*******************************************************************/

  static void free_contexts( Contexts *c )

  {
    if (c) {
      free_contexts(c->next);
      delete c; 
    }
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::copy_values                                         */
  /*                                                                 */
  /*******************************************************************/

  Range *Interface::copy_values( const Range *r )

  {
    if (r == NULL)
      return NULL;
    return add_value( r->character, copy_values(r->next));
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::complement_range                                    */
  /*                                                                 */
  /*******************************************************************/

  Range *Interface::complement_range( Range *r )

  {
    vector<Character> sym;
    for( Range *p=r; p; p=p->next)
      sym.push_back( p->character );
    free_values( r );

    TheAlphabet.complement(sym);
    if (sym.size() == 0)
      error("Empty character range!");
    
    Range *result=NULL;
    for( size_t i=0; i<sym.size(); i++ ) {
      Range *tmp = new Range;
      tmp->character = sym[i];
      tmp->next = result;
      result = tmp;
    }

    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::make_transducer                                     */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::make_transducer( Range *r1, Range *r2 )

  {
    Transducer *t=new Transducer();
    Node *node=t->new_node();
    node->set_final(1);

    if (r1 == NULL || r2 == NULL) {
      if (!Alphabet_Defined)
	error("The wildcard symbol '.' requires the definition of an alphabet");

      // one of the ranges was '.'
      for(Alphabet::const_iterator it=TheAlphabet.begin(); 
	  it!=TheAlphabet.end(); it++)
	if ((r1 == NULL || in_range(it->lower_char(), r1)) &&
	    (r2 == NULL || in_range(it->upper_char(), r2)))
	  t->root_node()->add_arc( *it, node, t );
    }
    else {
      for (;;) {
	Label l(r1->character, r2->character);
	// TheAlphabet.insert(l);
	t->root_node()->add_arc( l, node, t );
	if (!r1->next && !r2->next)
	  break;
	if (r1->next)
	  r1 = r1->next;
	if (r2->next)
	  r2 = r2->next;
      }
    }

    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::one_label_transducer                                */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::one_label_transducer( Label l )

  {
    Transducer *t = new Transducer();
    Node *last = t->new_node();
    t->root_node()->add_arc( l, last, t );
    last->set_final(1);

    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::new_transducer                                      */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::new_transducer( Range *r1, Range *r2 )

  {
    Transducer *t=make_transducer( r1, r2);
    if (r1 != r2)
      free_values(r1);
    free_values(r2);
    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::read_words                                          */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::read_words( char *filename )

  {
    if (Verbose)
      fprintf(stderr,"\nreading words from %s...", filename);
    ifstream is(filename);
    if (!is.is_open()) {
      static char message[1000];
      sprintf(message,"Error: Cannot open file \"%s\"!", filename);
      throw message;
    }
    free( filename );
    Transducer *t = new Transducer(is, &TheAlphabet, Verbose, LexiconComments);
    is.close();
    TheAlphabet.insert_symbols(t->alphabet);
    if (Verbose)
      fprintf(stderr,"finished\n");
    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::read_transducer                                     */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::read_transducer( char *filename )

  {
    if (Verbose)
      fprintf(stderr,"\nreading transducer from %s...", filename);
    FILE *file = fopen(filename,"rb");
    if (file == NULL) {
      static char message[1000];
      sprintf(message,"Error: Cannot open file \"%s\"!",filename);
      throw message;
    }
    Transducer t(file);
    fclose(file);
    if (t.alphabet.utf8 != TheAlphabet.utf8) {
      static char message[1000];
      sprintf(message,"Error: incompatible character encoding in file \"%s\"!",
	      filename);
      throw message;
    }
    free( filename );
    // transfer the encoding og TheAlphabet to the transducer
    Transducer *nt = &t.copy(false, &TheAlphabet);
    TheAlphabet.insert_symbols(nt->alphabet);
    if (Verbose)
      fprintf(stderr,"finished\n");
    return nt;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::def_alphabet                                        */
  /*                                                                 */
  /*******************************************************************/

  void Interface::def_alphabet( Transducer *t )

  {
    t = explode(t);
    t = minimise(t);
    t->alphabet.clear_char_pairs();
    t->complete_alphabet();
    TheAlphabet.clear_char_pairs();
    TheAlphabet.copy(t->alphabet);
    Alphabet_Defined = 1;
    delete t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::def_svar  definition of a value range variable      */
  /*                                                                 */
  /*******************************************************************/

  bool Interface::def_svar( char *name, Range *r )

  {
    // delete the old value of the variable
    SVarMap::iterator it=SVM.find(name);
    if (it != SVM.end()) {
      char *n=it->first;
      Range *v=it->second;
      SVM.erase(it);
      delete v;
      free(n);
    }
    SVM[name] = r;
    return r == NULL;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::svar_value                                          */
  /*                                                                 */
  /*******************************************************************/

  Range *Interface::svar_value( char *name )

  {
    SVarMap::iterator it=SVM.find(name);
    if (it == SVM.end())
      error2("undefined variable", name);
    free(name);
    return copy_values(it->second);
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::rsvar_value                                         */
  /*                                                                 */
  /*******************************************************************/

  Range *Interface::rsvar_value( char *name )

  {
    if (RSS.find(name) == RSS.end())
      RSS.insert(fst_strdup(name));
    return add_value(symbol_code(name), NULL);
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::def_var  definition of a transducer variable        */
  /*                                                                 */
  /*******************************************************************/

  bool Interface::def_var( char *name, Transducer *t )

  {
    // delete the old value of the variable
    VarMap::iterator it=VM.find(name);
    if (it != VM.end()) {
      char *n=it->first;
      Transducer *v=it->second;
      VM.erase(it);
      delete v;
      free(n);
    }

    t = explode(t);
    t = minimise(t);

    VM[name] = t;
    return t->is_empty();
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::def_rvar                                            */
  /*  definition of an agreement variable for automata               */
  /*                                                                 */
  /*******************************************************************/

  bool Interface::def_rvar( char *name, Transducer *t )

  {
    if (t->is_cyclic())
      error2("cyclic transducer assigned to", name);
    return def_var( name, t );
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::var_value                                           */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::var_value( char *name )

  {
    VarMap::iterator it=VM.find(name);
    if (it == VM.end())
      error2("undefined variable", name);
    free(name);
    return &(it->second->copy());
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::rvar_value                                          */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::rvar_value( char *name )

  {
    if (RS.find(name) == RS.end())
      RS.insert(fst_strdup(name));
    Range *r=add_value(symbol_code(name), NULL);
    return new_transducer(r,r);
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::explode                                             */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::explode( Transducer *t )

  {
    if (RS.size() == 0 && RSS.size() == 0)
      return t;

    t = minimise(t);

    vector<char*> name;
    for( RVarSet::iterator it=RS.begin(); it!=RS.end(); it++)
      name.push_back(*it);
    RS.clear();

    // replace all agreement variables
    for( size_t i=0; i<name.size(); i++ ) {
      Transducer *nt = NULL;
      Label l((Character)TheAlphabet.symbol2code(name[i]));
      Transducer *vt=var_value(name[i]);

      // enumerate all paths of the transducer
      vector<Transducer*> it;
      vt->enumerate_paths(it);
      delete vt;

      // insert each path
      for( size_t i=0; i<it.size(); i++ ) {

	// insertion
	Transducer *t1 = &t->splice(l, it[i]);
	delete it[i];

	if (nt == NULL)
	  nt = t1;
	else
	  nt = disjunction(nt, t1);
      }
      delete t;
      t = nt;
    }

    name.clear();
    for( RVarSet::iterator it=RSS.begin(); it!=RSS.end(); it++)
      name.push_back(*it);
    RSS.clear();

    // replace all agreement variables
    for( size_t i=0; i<name.size(); i++ ) {
      Transducer *nt = NULL;
      Character c=(Character)TheAlphabet.symbol2code(name[i]);
      Range *r=svar_value(name[i]);

      // insert each character
      while (r != NULL) {

	// insertion
	Transducer *t1 = &t->replace_char(c, r->character);

	if (nt == NULL)
	  nt = t1;
	else
	  nt = disjunction(nt, t1);

	Range *next = r->next;
	delete r;
	r = next;
      }
      delete t;
      t = nt;
    }

    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::catenate                                            */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::catenate( Transducer *t1, Transducer *t2 )

  {
    Transducer *t = &(*t1 + *t2);
    delete t1;
    delete t2;
    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::add_range                                           */
  /*                                                                 */
  /*******************************************************************/

  Ranges *Interface::add_range( Range *r, Ranges *l )

  {
    Ranges *result = new Ranges;
    result->range = r;
    result->next = l;
    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::make_mapping                                        */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::make_mapping( Ranges *list1, Ranges *list2 )

  {
    Ranges *l1=list1;
    Ranges *l2=list2;
    Transducer *t=new Transducer();

    Node *node=t->root_node();
    while (l1 && l2) {
      Node *nn=t->new_node();
      for( Range *r1=l1->range; r1; r1=r1->next )
	for( Range *r2=l2->range; r2; r2=r2->next )
	  node->add_arc( Label(r1->character, r2->character), nn, t );
      node = nn;
      l1 = l1->next;
      l2 = l2->next;
    }
    while (l1) {
      Node *nn=t->new_node();
      for( Range *r1=l1->range; r1; r1=r1->next )
	node->add_arc( Label(r1->character, Label::epsilon), nn, t );
      node = nn;
      l1 = l1->next;
    }
    while (l2) {
      Node *nn=t->new_node();
      for( Range *r2=l2->range; r2; r2=r2->next )
	node->add_arc( Label(Label::epsilon, r2->character), nn, t );
      node = nn;
      l2 = l2->next;
    }
    node->set_final(1);

    free_values(list1);
    free_values(list2);
    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::disjunction                                         */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::disjunction( Transducer *t1, Transducer *t2 )

  {
    Transducer *t = &(*t1 | *t2);
    delete t1;
    delete t2;
    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::conjunction                                         */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::conjunction( Transducer *t1, Transducer *t2 )

  {
    if (RS.size() > 0 || RSS.size() > 0)
      cerr << "\nWarning: agreement operation inside of conjunction!\n";
    Transducer *t = &(*t1 & *t2);
    delete t1;
    delete t2;
    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::subtraction                                         */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::subtraction( Transducer *t1, Transducer *t2 )

  {
    t1->alphabet.copy(TheAlphabet);

    if (RS.size() > 0 || RSS.size() > 0)
      cerr << "\nWarning: agreement operation inside of conjunction!\n";
    Transducer *t = &(*t1 / *t2);
    delete t1;
    delete t2;
    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::composition                                         */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::composition( Transducer *t1, Transducer *t2 )

  {
    if (RS.size() > 0 || RSS.size() > 0)
      cerr << "\nWarning: agreement operation inside of composition!\n";
    Transducer *t = &(*t1 || *t2);
    delete t1;
    delete t2;
    return t;
  }

  /*******************************************************************/
  /*                                                                 */
  /*  Interface::freely_insert                                       */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::freely_insert( Transducer *t, 
					Character lc, Character uc )
  {
    return &t->freely_insert(Label(lc,uc));
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::negation                                            */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::negation( Transducer *t )

  {
    if (RS.size() > 0 || RSS.size() > 0)
      cerr << "\nWarning: agreement operation inside of negation!\n";
    if (!Alphabet_Defined)
      error("Negation requires the definition of an alphabet");
    t->alphabet.clear_char_pairs();
    t->alphabet.copy(TheAlphabet);
    Transducer *nt = &(!*t);
    delete t;
    return nt;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::upper_level                                         */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::upper_level( Transducer *t )

  {
    Transducer *nt = &t->upper_level();
    delete t;
    return nt;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::lower_level                                         */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::lower_level( Transducer *t )

  {
    Transducer *nt = &t->lower_level();
    delete t;
    return nt;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::minimise                                            */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::minimise( Transducer *t )

  {
    t->alphabet.copy(TheAlphabet);
    Transducer *nt = &t->minimise( Verbose );
    delete t;
    return nt;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::switch_levels                                       */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::switch_levels( Transducer *t )

  {
    Transducer *nt = &t->switch_levels();
    delete t;
    return nt;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::repetition                                          */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::repetition( Transducer *t )

  {
    Transducer *nt = &(t->kleene_star());
    delete t;
    return nt;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::repetition2                                         */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::repetition2( Transducer *t )

  {
    Transducer *t1 = &(t->kleene_star());
    Transducer *nt = &(*t + *t1);
    delete t;
    delete t1;
    return nt;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::optional                                            */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::optional( Transducer *t )

  {
    Transducer *nt = &(t->copy());
    nt->root_node()->set_final(1);
    delete t;
    return nt;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::add_pi_transitions                                  */
  /*                                                                 */
  /*******************************************************************/

  void Interface::add_pi_transitions( Transducer *t, Node *node, Alphabet &alph)

  {
    for( Alphabet::const_iterator it=alph.begin(); it!=alph.end(); it++) {
      Label l = *it;
      node->add_arc( l, node, t );
    }
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::pi_machine                                          */
  /*                                                                 */
  /*******************************************************************/

 Transducer *Interface::pi_machine( Alphabet &alph )

  {
    Transducer *t=new Transducer();
    t->root_node()->set_final(1);
    add_pi_transitions( t, t->root_node(), alph );
    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::empty_string_transducer                             */
  /*                                                                 */
  /*******************************************************************/

 Transducer *Interface::empty_string_transducer( void )

 {
   Transducer *t=new Transducer();
   t->root_node()->set_final(1);
   return t;
 }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::cp                                                  */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::cp( Range *lower_range, Range *upper_range )

  {
    Transducer *t = make_transducer(lower_range, upper_range);
    for( ArcsIter p(t->root_node()->arcs()); p; p++ ) {
      Arc *arc=p;
      if (TheAlphabet.find(arc->label()) == TheAlphabet.end())
	fprintf(stderr,"Warning: 2-level rule mapping \"%s\" not defined in alphabet!\n",
		TheAlphabet.write_label(arc->label()));
    }
    
    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::anti_cp                                             */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::anti_cp( Range *lower_range, Range *upper_range )

  {
    Transducer *cpt = cp(lower_range, upper_range);
    Transducer *t=new Transducer();
    Node *node=t->new_node();

    node->set_final(1);
    for(Alphabet::const_iterator it=TheAlphabet.begin(); 
	it!=TheAlphabet.end(); it++){
      Label l=*it;
      if (in_range(l.lower_char(), lower_range) &&
	  !cpt->root_node()->target_node(l))
	t->root_node()->add_arc( l, node, t );
    }
    if (in_range(Label::epsilon, lower_range) &&
	!cpt->root_node()->target_node(Label()))
      t->root_node()->add_arc( Label(), node, t );

    delete cpt;
    return t;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::twol_right_rule                                     */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::twol_right_rule( Transducer *lc, Range *lower_range, 
					  Range *upper_range, Transducer *rc )
  {
    // Build the rule transducer
    Transducer *cpt = cp(lower_range, upper_range);
    Transducer *pi=pi_machine(TheAlphabet);

    // First unwanted language

    lc->alphabet.copy(TheAlphabet);
    Transducer *notlc = &(!*lc);
    Transducer *tmp = &(*notlc + *cpt);
    delete notlc;
    Transducer *t1 = &(*tmp + *pi);
    delete tmp;

    // Second unwanted language
    rc->alphabet.copy(TheAlphabet);
    Transducer *notrc = &(!*rc);
    tmp = &(*cpt + *notrc);
    delete cpt;
    delete notrc;
    Transducer *t2 = &(*pi + *tmp);
    delete pi;
    delete tmp;

    tmp = &(*t1|*t2); 
    delete t1; 
    delete t2; 

    tmp->alphabet.copy(TheAlphabet);
    t1 = &(!*tmp); 
    delete tmp; 

    return t1;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::twol_left_rule                                      */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::twol_left_rule( Transducer *lc, Range *lower_range,
					 Range *upper_range, Transducer *rc )
  {
    // check for problematic insertion operations like "$L <> <= a $R"
    // where either $L or $R includes the empty string
    if (in_range(Label::epsilon, lower_range)) {
      if (lc->generates_empty_string())
	error("in two level rule: insertion operation with deletable left context!");
      if (rc->generates_empty_string())
	error("in two level rule: insertion operation with deletable right context!");
      cerr << "\nWarning: two level rule used for insertion operation (might produce unexpected results)\n";
    }

    // Build the rule transducer
    Transducer *t1 = anti_cp(lower_range, upper_range);

    // Add the left context;
    Transducer *t2 = &(*lc + *t1);
    delete t1;

    // Add the right context;
    t1 = &(*t2 + *rc);
    delete t2;

    // Form the complement
    t1->alphabet.copy(TheAlphabet);
    t2 = &(!*t1);
    delete t1;

    return t2;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::make_rule                                           */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::make_rule( Transducer *lc, Range *lower_range, 
				    Twol_Type type, Range *upper_range, 
				    Transducer *rc )
  {
    if (RS.size() > 0 || RSS.size() > 0)
      cerr << "\nWarning: agreement operation inside of replacement rule!\n";

    if (!Alphabet_Defined)
      error("Two level rules require the definition of an alphabet");

    // expand the left and the right contexts to their full length
    Transducer *pi=pi_machine(TheAlphabet);

    if (lc == NULL)
      lc = pi_machine(TheAlphabet);
    else {
      Transducer *tmp = &(*pi + *lc);
      delete lc;
      lc = tmp;
    }
    if (rc == NULL)
      rc = pi_machine(TheAlphabet);
    else {
      Transducer *tmp = &(*rc + *pi);
      delete rc;
      rc = tmp;
    }
    delete pi;

    Transducer *result = NULL;

    switch (type) {
    case twol_left:
      result = twol_left_rule(lc, lower_range, upper_range, rc);
      break;
    case twol_right:
      result = twol_right_rule(lc, lower_range, upper_range, rc);
      break;
    case twol_both:
      {
	Transducer *t1 = twol_left_rule(lc, lower_range, upper_range, rc);
	Transducer *t2 = twol_right_rule(lc, lower_range, upper_range, rc);
	result = &(*t1 & *t2);
	delete t1;
	delete t2;
      }
    }
    delete lc;
    delete rc;
    if (lower_range != upper_range)
      free_values(lower_range);
    free_values(upper_range);

    return minimise(result);
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::make_context                                        */
  /*                                                                 */
  /*******************************************************************/

  Contexts *Interface::make_context( Transducer *l, Transducer *r )

  {
    if (l == NULL)
      l = empty_string_transducer();
    if (r == NULL)
      r = empty_string_transducer();

    Contexts *c=new Contexts();
    c->left = l;
    c->right = r;
    c->next = NULL;

    return c;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::add_context                                         */
  /*                                                                 */
  /*******************************************************************/

  Contexts *Interface::add_context( Contexts *nc, Contexts *c )

  {
    nc->next = c;
    return nc;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::restriction_transducer                              */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::restriction_transducer( Transducer *l1, Transducer *l2,
						 Character marker )
  {
    l1->alphabet.copy(TheAlphabet);
    Transducer *t1 = &(*l1 / *l2);

    Transducer *t2 = &t1->replace_char(marker, Label::epsilon);
    delete t1;

    t2->alphabet.copy(TheAlphabet);
    t1 = &(!*t2);
    delete t2;

    return t1;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::marker_transducer                                   */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::marker_transducer( Transducer *t, Contexts *c,
					    Character &marker )
  {
    marker = TheAlphabet.new_marker();
    Transducer *result = one_label_transducer( Label(marker) );

    // build the alphabet with a new marker
    result->alphabet.insert_symbols(t->alphabet);
    while (c) {
      result->alphabet.insert_symbols(c->left->alphabet);
      result->alphabet.insert_symbols(c->right->alphabet);
      c = c->next;
    }

    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::center_transducer                                   */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::center_transducer( Transducer *t, Transducer *pi, 
					    Transducer *mt )
  {
    // create the concatenation   pi + mt + *t + mt + pi
    Transducer *t1=&(*pi + *mt);
    Transducer *t2=&(*t1 + *t);
    delete t1;
    t1 = &(*t2 + *mt);
    delete t2;
    t2 = &(*t1 + *pi);
    delete t1;
    return t2;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::context_transducer                                  */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::context_transducer( Transducer *t, Transducer *pi,
					     Transducer *mt, Contexts *c )
  {
    // pi + left[i] + mt + pi + mt + right[i] + pi
  
    Transducer *t1 = &(*mt + *t);
    Transducer *tmp = &(*t1 + *mt);
    delete t1;
    Transducer *result=NULL;

    while (c) {
      t1 = &(*pi + *c->left);
      Transducer *t2 = &(*t1 + *tmp);
      delete t1;
      t1 = &(*t2 + *c->right);
      delete t2;
      t2 = &(*t1 + *pi);
      delete t1;

      if (result) {
	t1 = &(*result | *t2);
	delete t2;
	result = t1;
      }
      else 
	result = t2;

      c = c->next;
    }
    delete tmp;

    return result;
  }



  /*******************************************************************/
  /*                                                                 */
  /*  Interface::result_transducer                                   */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::result_transducer( Transducer *l1, Transducer *l2,
					    Twol_Type type, Character marker )
  {
    Transducer *result=NULL;
    if (type == twol_right)
      result = restriction_transducer( l1, l2, marker );
    else if (type == twol_left)
      result = restriction_transducer( l2, l1, marker );
    else if (type == twol_both) {
      Transducer *t1 = restriction_transducer( l1, l2, marker );
      Transducer *t2 = restriction_transducer( l2, l1, marker );
      result = &(*t1 & *t2);
      delete t1;
      delete t2;
    }

    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::restriction                                         */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::restriction( Transducer *t, Twol_Type type, 
				      Contexts *c, int direction )
  {
    Character marker;
    Transducer *mt=marker_transducer( t, c, marker );
    Transducer *pi=pi_machine(TheAlphabet);
    Transducer *l1=center_transducer( t, pi, mt );

    Transducer *tmp;
    if (direction == 0)
      tmp = pi;
    else if (direction == 1) {
      // compute  _t || .*
      Transducer *t1 = &t->lower_level();
      tmp = &(*t1 || *pi);
      delete t1;
    }
    else {
      // compute  ^t || .*
      Transducer *t1 = &t->upper_level();
      tmp = &(*pi || *t1);
      delete t1;
    }
    delete t;

    Transducer *l2=context_transducer( tmp, pi, mt, c );
    if (tmp != pi)
      delete tmp;
    delete pi;
    delete mt;

    Transducer *result=result_transducer( l1, l2, type, marker );
    delete l1;
    delete l2;

    free_contexts( c );

    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::insert_boundary_transducer                          */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::insert_boundary_transducer( Character leftm, Character rightm,
						     Alphabet &alph )
  {
    // Create the insert boundaries transducer (.|<>:<L>|<>:<R>)*
  
    Transducer *result=pi_machine( alph );
    Node *root=result->root_node();
    root->add_arc( Label(Label::epsilon, leftm), root, result);
    root->add_arc( Label(Label::epsilon, rightm),root, result);

    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::remove_boundary_transducer                          */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::remove_boundary_transducer( Character leftm, Character rightm,
						     Alphabet &alph )
  {
    // Create the remove boundaries transducer (.|<L>:<>|<R>:<>)*

    Transducer *result=pi_machine( alph );
    Node *root = result->root_node();
    root->add_arc( Label(leftm, Label::epsilon), root, result);
    root->add_arc( Label(rightm,Label::epsilon), root, result);

    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::constrain_boundary_transducer                       */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::constrain_boundary_transducer( Character leftm, 
							Character rightm,
							Alphabet &alph)
  {
    // create the transducer  (.|<L>|<R>)*

    Transducer *tmp=pi_machine( alph );

    // create the transducer  (.|<L>|<R>)* <L><R> (.|<L>|<R>)*
    Node *root = tmp->root_node();
    Node *node = tmp->new_node();
    Node *last = tmp->new_node();

    root->set_final(0);
    last->set_final(1);

    root->add_arc( Label(leftm), node, tmp);
    node->add_arc( Label(rightm), last, tmp);

    add_pi_transitions( tmp, last, alph );

    // create the transducer  !((.|<L>|<R>)* <L><R> (.|<L>|<R>)*)
    tmp->alphabet.copy(alph);
    Transducer *result = &(!*tmp);
    delete tmp;

    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::extended_left_transducer                            */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::extended_left_transducer( Transducer *t, Character m1, 
						   Character m2, Alphabet &alpha )
  {
    if (t == NULL) // empty context
      return pi_machine(alpha);

    // Extended left context transducer

    // <R> >> (<L> >> $T$)
    Transducer *tmp=&t->freely_insert( Label(m1) );
    delete t;
    t = &tmp->freely_insert( Label(m2) );
    delete tmp;

    // .* (<R> >> (<L> >> $T$))
    add_pi_transitions( t, t->root_node(), alpha );

    // !(.*<L>)
    tmp = one_label_transducer(Label(m1));
    add_pi_transitions( tmp, tmp->root_node(), alpha );
    tmp->alphabet.copy(alpha);
    Transducer *t2 = &(!*tmp);
    delete tmp;
    
    // .* (<R> >> (<L> >> $T$)) || !(.*<L>)
    tmp = &(*t || *t2);
    delete t;
    delete t2;

    return tmp;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::left_context                                        */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::left_context( Transducer *t, Character leftm, 
				       Character rightm, Alphabet &alph )
  {
    // .* (<R> >> (<L> >> $T$)) || !(.*<L>)
    Transducer *ct = extended_left_transducer(t, leftm, rightm, alph);

    // <L>
    Transducer *mt = one_label_transducer(Label(leftm));
    // <R>* <L>
    mt->root_node()->add_arc(Label(rightm), mt->root_node(), mt );
    // <R>* <L> .*
    add_pi_transitions(mt, mt->root_node()->target_node(Label(leftm)), alph);

    ct->alphabet.copy( alph );
    Transducer *no_ct = &!*ct;

    mt->alphabet.copy(alph);
    Transducer *no_mt = &!*mt;

    Transducer *t1 = &(*no_ct + *mt);
    delete no_ct;
    delete mt;

    Transducer *t2 = &(*ct + *no_mt);
    delete ct;
    delete no_mt;

    Transducer *tmp = &(*t1 | *t2);
    delete t1;
    delete t2;

    tmp->alphabet.copy( alph );
    t1 = &!*tmp;
    delete tmp;

    return t1;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::right_context                                       */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::right_context( Transducer *t, Character leftm, 
					Character rightm, Alphabet &alph )
  {
    // right context transducer:  (<R> >> (<L> >> $T$)) .* || !(<R>.*)
    Transducer *tmp = &t->reverse();
    delete t;
    Transducer *t2 = left_context(tmp, rightm, leftm, alph);
    Transducer *result = &t2->reverse();
    delete t2;
    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::make_optional                                       */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::make_optional( Transducer *t, Repl_Type type )

  {
    Transducer *t1;
    if (type == my_repl_down)
      t1 = &t->upper_level();
    else
      t1 = &t->lower_level();

    Transducer *t2 = &(*t | *t1);

    delete t;
    delete t1;

    return t2;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::replace                                             */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::replace( Transducer *ct, Repl_Type type, 
				  bool optional )
  {
    if (optional)
      ct = make_optional(ct, type);

    // compute the no-center transducer
    Transducer *t1=NULL;

    Transducer *pi = pi_machine(TheAlphabet);
    if (type == repl_up) {
      // _ct || .*
      Transducer *t2 = &ct->lower_level();
      t1 = &(*t2 || *pi);
      delete t2;
    }
    else if (type == my_repl_down) {
      // .* || ^ct
      Transducer *t2 = &ct->upper_level();
      t1 = &(*pi || *t2);
      delete t2;
    }
    else
      error("Invalid type of replace operator");

    {
      // _ct without empty string
      Transducer *t2 = empty_string_transducer();
      Transducer *t3 = &(*t1 / *t2);
      delete t1;
      delete t2;
      t1 = t3;
    }

    // .* _ct
    Transducer *t2 = &(*pi + *t1);
    delete t1;

    // .*  _ct .*
    t1 = &(*t2 + *pi);
    delete pi;
    delete t2;

    // no_ct = !(.*  _ct .*)
    t1->alphabet.copy(TheAlphabet);
    Transducer *no_ct = &(!*t1);
    delete t1;

    // compute the unconditional replacement transducer

    // no-ct ct
    t1 = &(*no_ct + *ct);
    delete ct;

    // (no-ct ct)*
    t2 = &(t1->kleene_star());
    delete t1;

    // (no-ct ct)* no-ct
    t1 = &(*t2 + *no_ct);
    delete t2;
    delete no_ct;

    return t1;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::replace_transducer                                  */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::replace_transducer( Transducer *ct, Character lm, 
					     Character rm, Repl_Type type )
  {
    // insert boundary markers into the center transducer

    // <L> >> (<R> >> $Center$)
    Transducer *tmp = &ct->freely_insert(Label(lm));
    delete ct;
    ct = &tmp->freely_insert(Label(rm));
    delete tmp;
  
    // add surrounding boundary markers to the center transducer

    // <L> (<L> >> (<R> >> $Center$))
    Transducer *t2 = one_label_transducer( Label(lm) );
    tmp = &(*t2 + *ct);
    delete t2;
    delete ct;
      
    // $CenterB$ = <L> (<L> >> (<R> >> $Center$)) <R>
    t2 = one_label_transducer( Label(rm) );
    ct = &(*tmp + *t2);
    delete tmp;
    delete t2;

    return replace(ct, type, false);
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::replace_in_context                                  */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::replace_in_context( Transducer *t, Repl_Type type, 
					     Contexts *c, bool optional )
  {
    if (optional)
      t = make_optional(t, type);

    // The implementation of the replace operators is based on
    // "The Replace Operator" by Lauri Karttunen

    if (!Alphabet_Defined)
      error("The replace operators require the definition of an alphabet");

    if (!c->left->is_automaton() || !c->right->is_automaton())
      error("The replace operators require automata as context expressions! (Do not include any character mappings x:y between the two parentheses of the operator.)");

    if (type == my_repl_down) {
      Transducer *t2 = empty_string_transducer();
      Transducer *t3 = &(*t || *t2);
      if (!t3->is_empty())
	cerr << "\nWarning: The source of the replace operation contains the empty string! (Such insertion operations do not work.)\n";
      delete t2;
      delete t3;
    }
    else {
      Transducer *t2 = empty_string_transducer();
      Transducer *t3 = &(*t2 || *t);
      if (!t3->is_empty())
	cerr << "\nWarning: The source of the replace operation contains the empty string! (Such insertion operations do not work.)\n";
      delete t2;
      delete t3;
    }

    // create the marker symbols
    Character leftm = TheAlphabet.new_marker();
    Character rightm = TheAlphabet.new_marker();

    // create the upper and lower alphabets
    Alphabet lower_alph;
    lower_alph.copy( TheAlphabet, lower );
    Alphabet upper_alph;
    upper_alph.copy( TheAlphabet, upper );

    /////////////////////////////////////////////////////////////
    // Create the insert boundaries transducer (.|<>:<L>|<>:<R>)*
    /////////////////////////////////////////////////////////////
  
    Transducer *tmp=insert_boundary_transducer( leftm, rightm, lower_alph );

    /////////////////////////////////////////////////////////////
    // Create the remove boundaries transducer (.|<L>:<>|<R>:<>)*
    /////////////////////////////////////////////////////////////

    Transducer *rbt=remove_boundary_transducer( leftm, rightm, upper_alph );

    // Add the markers to the alphabet
    TheAlphabet.insert(Label(leftm));
    TheAlphabet.insert(Label(rightm));
    lower_alph.insert(Label(leftm));
    lower_alph.insert(Label(rightm));
    upper_alph.insert(Label(leftm));
    upper_alph.insert(Label(rightm));

    /////////////////////////////////////////////////////////////
    // unconditional replace transducer
    /////////////////////////////////////////////////////////////

    Transducer *rt;
    if (type == my_repl_down)
      rt = replace_transducer( t, leftm, rightm, my_repl_down );
    else
      rt = replace_transducer( t, leftm, rightm, repl_up );

    /////////////////////////////////////////////////////////////
    // build the conditional replacement transducer
    /////////////////////////////////////////////////////////////

    if (type != my_repl_down) {
      // Create the constrain boundaries transducer !(.*<L><R>.*)
      Transducer *cbt=constrain_boundary_transducer(leftm, rightm, lower_alph);

      Transducer *t2 = &(*tmp || *cbt);
      delete tmp;
      delete cbt;
      tmp = t2;
    }

    if (type == repl_up || type == repl_left) {
      // left context transducer:  .* (<R> >> (<L> >> $T$)) || !(.*<L>)
      Transducer *lct = left_context(c->left, leftm, rightm, lower_alph);

      Transducer *t2 = &(*tmp || *lct);
      delete tmp;
      delete lct;
      tmp = t2;
    }

    if (type == repl_up || type == repl_right) {
      // right context transducer:  (<R> >> (<L> >> $T$)) .* || !(<R>.*)
      Transducer *rct = right_context(c->right, leftm, rightm, lower_alph);

      Transducer *t2 = &(*tmp || *rct);
      delete tmp;
      delete rct;
      tmp = t2;
    }

    {
      // Apply the replacement transducer
      Transducer *t2 = &(*tmp || *rt);
      delete tmp;
      delete rt;
      tmp = t2;
    }

    if (type == my_repl_down || type == repl_down || type == repl_right) {
      // left context transducer:  .* (<R> >> (<L> >> $T$)) || !(.*<L>)
      Transducer *lct = left_context(c->left, leftm, rightm, upper_alph);

      Transducer *t2 = &(*tmp || *lct);
      delete tmp;
      delete lct;
      tmp = t2;
    }
    if (type == my_repl_down || type == repl_down || type == repl_left) {
      // right context transducer:  (<R> >> (<L> >> $T$)) .* || !(<R>.*)
      Transducer *rct = right_context(c->right, leftm, rightm, upper_alph);

      Transducer *t2 = &(*tmp || *rct);
      delete tmp;
      delete rct;
      tmp = t2;
    }

    if (type == my_repl_down) {
      // Create the constrain boundaries transducer !(.*<L><R>.*)
      Transducer *cbt=constrain_boundary_transducer(leftm, rightm, upper_alph);

      Transducer *t2 = &(*tmp || *cbt);
      delete(tmp);
      delete(cbt);
      tmp = t2;
    }

    Transducer *result = &(*tmp || *rbt);
    delete tmp;
    delete rbt;

    // Remove the markers from the alphabet
    TheAlphabet.delete_markers();

    free_contexts( c );

    return result;
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::add_alphabet                                        */
  /*                                                                 */
  /*******************************************************************/

  void Interface::add_alphabet( Transducer *t )

  {
    t->alphabet.copy(TheAlphabet);
    t->complete_alphabet();
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::write_to_file                                       */
  /*                                                                 */
  /*******************************************************************/

  void Interface::write_to_file( Transducer *t, char *filename)

  {
    FILE *file;
    if ((file = fopen(filename,"wb")) == NULL) {
      fprintf(stderr,"\nError: Cannot open output file \"%s\"\n\n", filename);
      exit(1);
    }
    free( filename );

    t = explode(t);
    add_alphabet(t);
    t = minimise(t);
    t->store(file);
    fclose(file);
  }


  /*******************************************************************/
  /*                                                                 */
  /*  Interface::result                                              */
  /*                                                                 */
  /*******************************************************************/

  Transducer *Interface::result( Transducer *t, bool switch_flag )

  {
    t = explode(t);

    // delete the variable values
    vector<char*> s;
    for( VarMap::iterator it=VM.begin(); it != VM.end(); it++ ) {
      s.push_back(it->first);
      delete it->second;
      it->second = NULL;
    }
    VM.clear();
    for( size_t i=0; i<s.size(); i++ )
      free(s[i]);
    s.clear();

    if (switch_flag)
      t = switch_levels(t);
    add_alphabet(t);
    t = minimise(t);
    return t;
  }

}
