//       This library is free software: you can redistribute it and/or modify
//       it under the terms of the GNU Lesser General Public License as published by
//       the Free Software Foundation, version 3 of the License.
//
//       This library 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 Lesser General Public License for more details.
//
//       You should have received a copy of the GNU Lesser General Public License
//       along with this program.  If not, see <http://www.gnu.org/licenses/>.

/*

   This file contains functions that are needed by the SFST programming
   language parser defined in the file 'sfst-compiler.yy'. The parser is
   used by the command line program 'hfst-calculate'.

   This file is based on SFST's file 'interface.C'. Some functions are
   copied as such and some are less or more modified so that they work
   properly with the HFST interface.

   HFST has a written agreement with the author of SFST, Helmut Schmid,
   that this file, though derived from SFST which is GPLv2+,
   may be distributed under the LGPLv3+ license as part of HFST.

 */

#include "SfstCompiler.h"
#include "SfstUtf8.h"
#include "SfstBasic.h"
#include "HfstTransducer.h"
#include "HfstInputStream.h"
#include "HfstOutputStream.h"
#include "implementations/HfstBasicTransducer.h"

using hfst::implementations::HfstTransitionGraph;
using hfst::implementations::HfstBasicTransducer;
using hfst::implementations::HfstState;

using std::cerr;

extern FILE  *sfstin;
int sfstparse( void );

namespace hfst
{

  SfstCompiler * sfst_compiler = NULL;

  SfstCompiler::SfstCompiler( ImplementationType type, bool verbose /*=false*/ )
  {
    result_ = NULL;
    Verbose = verbose;
    Alphabet_Defined = false;
    compiler_type = type;
    filename = "";
    foldername = "";
    switch_ = 0;
    sfst_compiler = this;
  };

  typedef std::pair<unsigned int, unsigned int> NumberPair;
  typedef std::set<NumberPair> NumberPairSet;
  typedef std::vector<NumberPair> NumberPairVector;

  void SfstCompiler::set_result(HfstTransducer * tr)
  {
    result_ = tr;
  }

  HfstTransducer * SfstCompiler::get_result()
  {
    return result_;
  }

  void SfstCompiler::set_input(FILE * input)
  {
    sfstin = input;
  }

  void SfstCompiler::set_filename(const std::string & name)
  {
    filename = name;
  }

  void SfstCompiler::set_foldername(const std::string & name)
  {
    foldername = name;
  }

  void SfstCompiler::set_switch(int value)
  {
    switch_ = value;
  }

  void SfstCompiler::parse()
  {
    sfstparse();
  }

  HfstTransducer * SfstCompiler::make_transducer
  (Range *r1, Range *r2, ImplementationType type)
  {
    StringPairSet sps;

    if (r1 == NULL || r2 == NULL) {
      if (!Alphabet_Defined) {
    fprintf(stderr,
        "ERROR: The wildcard symbol '.'"
        " requires the definition of an alphabet");
    HFST_THROW(HfstException);
      }

      // one of the ranges was '.'
      for(SfstAlphabet::const_iterator it=TheAlphabet.begin();
      it!=TheAlphabet.end(); it++) {
    if ((r1 == NULL || in_range(it->first, r1)) &&
        (r2 == NULL || in_range(it->second, r2))) {

      sps.insert(StringPair(
              std::string(TheAlphabet.code2symbol(it->first)),
              std::string(TheAlphabet.code2symbol(it->second)) ) );
    }
      }
    }
    else {
      for (;;) {
      sps.insert(StringPair(
              std::string(TheAlphabet.code2symbol(r1->character)),
              std::string(TheAlphabet.code2symbol(r2->character)) ) );

    if (!r1->next && !r2->next)
      break;
    if (r1->next)
      r1 = r1->next;
    if (r2->next)
      r2 = r2->next;
      }
    }

    return new HfstTransducer(sps, type);
  }

  HfstTransducer * SfstCompiler::new_transducer( Range *r1, Range *r2, ImplementationType type )
  {
    HfstTransducer * t = make_transducer(r1, r2, type);
    if (r1 != r2)
      free_values(r1);
    free_values(r2);
    return t;
  }

  Character SfstCompiler::character_code( unsigned int uc ) {
    // UTF-8 is always used
    return symbol_code(sfst_basic::fst_strdup(sfst_utf8::int2utf8(uc)));
  }

  void SfstCompiler::free_values( Range *r ) {
    if (r) {
      free_values(r->next);
      delete r;
    }
  }

  void SfstCompiler::free_values( Ranges *r ) {
    if (r) {
      free_values(r->range);
      free_values(r->next);
      delete r;
    }
  }

  void SfstCompiler::error( const char *message ) {
    std::cerr << "\nError: " << message << "\naborted.\n";
    HFST_THROW(HfstException);
  }

  void SfstCompiler::error2( const char *message, char *input ) {
    std::cerr << "\nError: " << message << ": " << input << "\naborted.\n";
    HFST_THROW(HfstException);
  }

  Character SfstCompiler::symbol_code( char *symbol )
  { // In SFST programming language epsilon is denoted as "<>"
    // but in HFST as "@_EPSILON_SYMBOL_@". That is why it must be
    // treated separately here.
    if (strcmp(symbol,"<>") == 0)
      return 0;
    int c=TheAlphabet.symbol2code(symbol);
    if (c == EOF)
      c = TheAlphabet.add_symbol( symbol );
    free(symbol);
    return (Character)c;
  }

  unsigned int SfstCompiler::utf8toint( char *s ) {
    return sfst_utf8::utf8toint(s);
  }

  bool SfstCompiler::in_range( unsigned int c, Range *r ) {
    while (r) {
      if (r->character == c)
    return true;
      r = r->next;
    }
    return false;
  }

  Range * SfstCompiler::add_value( Character c, Range *r) {
    Range *result=new Range;
    result->character = c;
    result->next = r;
    return result;
  }

  Range * SfstCompiler::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;
  }

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

  Ranges * SfstCompiler::add_range( Range *r, Ranges *l ) {
    Ranges *result = new Ranges;
    result->range = r;
    result->next = l;
    return result;
  }

  Range * SfstCompiler::complement_range( Range *r ) {
    std::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;
  }

  bool SfstCompiler::def_var( char *name, HfstTransducer *t ) {
    // delete the old value of the variable
    VarMap::iterator it=VM.find(std::string(name));
    if (it != VM.end()) {
      //char *n=it->first;
      HfstTransducer *v=it->second;
      VM.erase(it);
      delete v;
      //free(n);
    }

    t = explode(t);
    t->minimize();

    VM[std::string(name)] = t;

    return false;
  }

  bool SfstCompiler::def_rvar( char *name, HfstTransducer *t ) {
    if (t->is_cyclic())
      error2("cyclic transducer assigned to", name);
    return def_var( name, t );
  }

  HfstTransducer * SfstCompiler::var_value( char *name ) {
    VarMap::iterator it=VM.find(std::string(name));
    if (it == VM.end()) {
      printf("undefined variable %s\n", name);
      HFST_THROW(HfstException);
    }
    free(name);
    return new HfstTransducer(*(it->second));
  }

  HfstTransducer * SfstCompiler::rvar_value( char *name, ImplementationType type ) {
    if (RS.find(name) == RS.end())
      RS.insert(sfst_basic::fst_strdup(name));
    Range *r=add_value(symbol_code(name), NULL);
    return new_transducer(r,r,type);
  }

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

  Range *SfstCompiler::copy_values( const Range *r ) {
    if (r == NULL)
      return NULL;
    return add_value( r->character, copy_values(r->next));
  }

  Range *SfstCompiler::svar_value( char *name ) {
    SVarMap::iterator it=SVM.find(std::string(name));
    if (it == SVM.end())
      error2("undefined variable", name);
    free(name);
    return copy_values(it->second);
  }

  Range *SfstCompiler::rsvar_value( char *name ) {
    if (RSS.find(name) == RSS.end())
      RSS.insert(sfst_basic::fst_strdup(name));
    return add_value(symbol_code(name), NULL);
  }

  // HERE...

  HfstTransducer * SfstCompiler::insert_freely(HfstTransducer *t, Character input, Character output) {
    t->insert_freely(hfst::StringPair(TheAlphabet.code2symbol(input), TheAlphabet.code2symbol(output)));
    return t;
  }

  HfstTransducer * SfstCompiler::substitute(HfstTransducer *t, Character old_char, Character new_char) {
    t->substitute(std::string(TheAlphabet.code2symbol(old_char)), std::string(TheAlphabet.code2symbol(new_char)));
    return t;
  }

  HfstTransducer * SfstCompiler::substitute(HfstTransducer *t, Character old_char_in, Character old_char_out,
                        Character new_char_in, Character new_char_out) {
    t->substitute( hfst::StringPair(TheAlphabet.code2symbol(old_char_in),
                    TheAlphabet.code2symbol(old_char_out)),
           hfst::StringPair(TheAlphabet.code2symbol(new_char_in),
                    TheAlphabet.code2symbol(new_char_out)) );
    return t;
  }

  HfstTransducer * SfstCompiler::substitute(HfstTransducer *t, Character old_char_in, Character old_char_out,
                        HfstTransducer *tr) {
    t->substitute( hfst::StringPair(TheAlphabet.code2symbol(old_char_in),
                    TheAlphabet.code2symbol(old_char_out)), *tr );
    return t;
  }

  Contexts *SfstCompiler::make_context( HfstTransducer *l, HfstTransducer *r )
  {
    if (l != NULL && r != NULL) {
      if (l->get_type() != r->get_type()) {
    fprintf(stderr, "ERROR: in sfst-compiler.yy:"
        " context transducers do not have the same type.\n");
    HFST_THROW(HfstException);
      }
    }

    ImplementationType type;
    if (l != NULL)
      type = l->get_type();
    else
      type = r->get_type();

    if (l == NULL)
      l = new HfstTransducer(internal_epsilon,type);
    if (r == NULL)
      r = new HfstTransducer(internal_epsilon,type);

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

    return c;
  }

  Contexts *SfstCompiler::add_context( Contexts *nc, Contexts *c )
  {
    if (nc->left->get_type() != c->left->get_type() ||
    nc->right->get_type() != c->right->get_type() ) {
      fprintf(stderr, "ERROR: in sfst-compiler.yy:"
          " context transducers do not have the same type.\n");
      HFST_THROW(HfstException);
    }
    nc->next = c;
    return nc;
  }

  void SfstCompiler::warn(const char *msg) {
    std::cerr << "\nWarning: " << msg << "!\n";
  }

  HfstTransducer * SfstCompiler::negation( HfstTransducer *t )
  {
    if (RS.size() > 0 || RSS.size() > 0)
      warn("agreement operation inside of negation");
    if (!Alphabet_Defined)
      error("Negation requires the definition of an alphabet");

    // go through all symbol pairs in TheAlphabet and copy them to sps
    StringPairSet sps;
    for( SfstAlphabet::const_iterator it=TheAlphabet.begin(); it!=TheAlphabet.end(); it++ ) {
      SfstAlphabet::NumberPair l=*it;
      sps.insert(StringPair( TheAlphabet.code2symbol(l.first),
                 TheAlphabet.code2symbol(l.second)) );
    }
    // construct a universal language transducer
    HfstTransducer * pi_star = new HfstTransducer(sps, t->get_type());
    pi_star->repeat_star();
    pi_star->subtract(*t);
    delete t;
    return pi_star;
  }


  HfstTransducer * SfstCompiler::explode( HfstTransducer *t ) {

    //fprintf(stderr,"explode...\n");
    if (RS.size() == 0 && RSS.size() == 0) {
      //fprintf(stderr, "... no need to explode\n");
      return t;
    }

    t->minimize();

    // transducer agreement variable names
    std::vector<char*> name;
    for( RVarSet::iterator it=RS.begin(); it!=RS.end(); it++) {
      name.push_back(*it);
    }
    RS.clear();

    // Make a tokenizer that recognizes all multicharacter symbols in t.
    // It is needed when weighted paths are transformed into transducers.
    // HfstTokenizer TOK = t->create_tokenizer(); // no effect on performance

    // replace all agreement variables
    for( size_t i=0; i<name.size(); i++ ) {
      //fprintf(stderr, "substituting transducer agreement variable \"%s\"\n", name[i]);
      HfstTransducer *nt = new HfstTransducer(t->get_type()); // an initially empty transducer

      // enumerate all paths of the transducer
      HfstTransducer *vt=var_value(strdup(name[i])); // var_value frees its argument
      std::vector<HfstTransducer*> transducer_paths;

      if (t->type == SFST_TYPE) {
    //fprintf(stderr, "extracting paths in transducer: ...\n");
    //cerr << *vt;
    transducer_paths = vt->extract_path_transducers();
    //fprintf(stderr, "...extracted\n");
    delete vt;
      }
      else {
    HfstTwoLevelPaths paths;
    vt->extract_paths
      (paths, -1, -1);
    delete vt;

    // transform paths to a vector of transducers
    for (HfstTwoLevelPaths::const_iterator it = paths.begin(); it != paths.end(); it++) {
      HfstTransducer * path = new HfstTransducer(it->second, t->get_type());
      //HfstTransducer * path
      //  = new HfstTransducer(wp.istring, wp.ostring, TOK, t->get_type());
      path->set_final_weights(it->first);
      transducer_paths.push_back(path);
    }
      }


      // insert each path
      for( size_t j=0; j<transducer_paths.size(); j++ ) {
    //fprintf(stderr, "  substituting transducer agreement variable \"%s\" with transducer:\n", name[i]);
    //cerr << *(transducer_paths[j]);
    HfstTransducer ti(*t);
    //printf("in transducer:\n");
    //cerr << ti;
    ti.substitute(StringPair(std::string(name[i]), std::string(name[i])), *(transducer_paths[j]));
    //fprintf(stderr, "  ...substituted\n");
    delete transducer_paths[j];
    nt->disjunct(ti);
      }
      free(name[i]); // svar_value was given a copy of name[i], so the value is freed here

      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++ ) {
      //printf("substituting range agreement variable \"%s\"\n", name[i]);
      HfstTransducer *nt = new HfstTransducer(t->get_type());
      Range *r=svar_value(strdup(name[i]));  // svar_value frees its argument

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

    // insertion
    HfstTransducer ti(*t);
    // agreement variable marker should always appear on both sides of the tape..
    //printf("substituting agreement range variable %s with %s\n", name[i], TheAlphabet.code2symbol(r->character));
    ti.substitute(std::string(name[i]), TheAlphabet.code2symbol(r->character));
    nt->disjunct(ti);

    Range *next = r->next;
    delete r;
    r = next;
      }
      free(name[i]);  // svar_value was given a copy of name[i], so the value is freed here
      delete t;
      t = nt;
    }

    return t;
  }

  HfstTransducer * SfstCompiler::restriction( HfstTransducer * t, Twol_Type type, Contexts *c, int direction ) {

    StringPairSet sps;
    for( SfstAlphabet::const_iterator it=TheAlphabet.begin(); it!=TheAlphabet.end(); it++ ) {
      SfstAlphabet::NumberPair l=*it;
      sps.insert(StringPair( TheAlphabet.code2symbol(l.first),
                 TheAlphabet.code2symbol(l.second)) );
    }

    HfstTransducerPairVector contexts;
    Contexts *p = c;
    while (p != NULL)
      {
    HfstTransducerPair tr_pair(*(p->left),*(p->right));
    contexts.push_back(tr_pair);
    p = p->next;
      }

    return new HfstTransducer( hfst::rules::restriction(
         contexts, *t, sps,
         (hfst::rules::TwolType)type, direction ) );
  }

  HfstTransducer * SfstCompiler::make_rule( HfstTransducer * lc, Range * lower_range, Twol_Type type,
                        Range * upper_range, HfstTransducer * rc, ImplementationType implementation_type ) {

    if (RS.size() > 0 || RSS.size() > 0)
      std::cerr << "\nWarning: agreement operation inside of replacement rule!\n";

    if (!Alphabet_Defined) {
      fprintf(stderr, "\nERROR:"
          " Two level rules require the definition of an alphabet!\n");
      HFST_THROW(HfstException);
    }

    if (lc == NULL)
      lc = new HfstTransducer(internal_epsilon, implementation_type);
    if (rc == NULL)
      rc = new HfstTransducer(internal_epsilon, implementation_type);

    HfstTransducerPair tr_pair(*(lc), *(rc));

    StringPairSet sps;
    for( SfstAlphabet::const_iterator it=TheAlphabet.begin(); it!=TheAlphabet.end(); it++ ) {
      SfstAlphabet::NumberPair l=*it;
      sps.insert(StringPair( TheAlphabet.code2symbol(l.first),
                 TheAlphabet.code2symbol(l.second)) );
    }

    StringPairSet mappings;
    Range * r1 = lower_range;
    Range * r2 = upper_range;

    if (r1 == NULL || r2 == NULL) {
      if (!Alphabet_Defined) {
    fprintf(stderr, "ERROR: The wildcard symbol '.'"
        " requires the definition of an alphabet");
    HFST_THROW(HfstException);
      }

      // one of the ranges was '.'
      for(SfstAlphabet::const_iterator it=TheAlphabet.begin();
      it!=TheAlphabet.end(); it++) {
    if ((r1 == NULL || in_range(it->first, r1)) &&
        (r2 == NULL || in_range(it->second, r2))) {
      mappings.insert( StringPair(
                      TheAlphabet.code2symbol(it->first),
                      TheAlphabet.code2symbol(it->second) ) );
    }
      }
    }
    else {
      for (;;) {
    mappings.insert( StringPair(
                    TheAlphabet.code2symbol(r1->character),
                    TheAlphabet.code2symbol(r2->character) ) );
    if (!r1->next && !r2->next)
      break;
    if (r1->next)
      r1 = r1->next;
    if (r2->next)
      r2 = r2->next;
      }
    }

    switch(type)
      {
      case twol_left:
    return new HfstTransducer(rules::two_level_if(tr_pair, mappings, sps));
    break;
      case twol_right:
    return new HfstTransducer(rules::two_level_only_if(tr_pair, mappings, sps));
    break;
      case twol_both:
    return new HfstTransducer(rules::two_level_if_and_only_if(tr_pair, mappings, sps));
    break;
      }
    return NULL;

  }

  HfstTransducer * SfstCompiler::read_words(const char *folder, char *filename,
                        ImplementationType type) {

    std::string filestr("");
    if (NULL != folder && strcmp(folder, "") != 0) {
      filestr.append(folder);
      filestr.append("/"); // FIX: WINDOWS
    }
    filestr.append(filename);

    if (Verbose)
      fprintf(stderr,"\nreading words from %s...", filestr.c_str());
    std::ifstream is(filestr.c_str());
    if (!is.is_open()) {
      static char message[1000];
      sprintf(message,"Error: Cannot open file \"%s\"!", filestr.c_str());
      throw message;
    }
    free( filename );

    HfstTransducer * retval_hfst = NULL;
    hfst::implementations::HfstBasicTransducer retval_fsm;

    if (type != FOMA_TYPE &&
    type != TROPICAL_OPENFST_TYPE &&
    type != LOG_OPENFST_TYPE)
      retval_hfst = new HfstTransducer(type);

    int n=0;
    char buffer[10000];

    while (is.getline(buffer, 10000)) {
      if (Verbose && ++n % 10000 == 0) {
    if (n == 10000)
      cerr << "\n";
    cerr << "\r" << n << " words";
      }
      // delete final whitespace characters
      int l;
      for( l=(int)strlen(buffer)-1; l>=0; l-- )
    if ((buffer[l] != ' ' && buffer[l] != '\t' && buffer[l] != '\r') ||
        (l > 0 && buffer[l-1] == '\\'))
      break;
      buffer[l+1] = 0;

      StringPairVector spv;
      char *bufptr = buffer;

      std::pair<unsigned int, unsigned int> np =
    TheAlphabet.next_label(bufptr, true);
      while (np.first != 0 || np.second != 0) {
    spv.push_back(StringPair
              (std::string(TheAlphabet.code2symbol(np.first)),
               std::string(TheAlphabet.code2symbol(np.second)) ) );
    np = TheAlphabet.next_label(bufptr, true);
      }

      if (type != FOMA_TYPE &&
      type != TROPICAL_OPENFST_TYPE &&
      type != LOG_OPENFST_TYPE)
    retval_hfst->disjunct(spv);
      else
    retval_fsm.disjunct(spv,0);

    }
    if (Verbose && n >= 10000)
      cerr << "\n";

    is.close();
    if (Verbose)
      fprintf(stderr,"finished\n");

    if (type != FOMA_TYPE &&
    type != TROPICAL_OPENFST_TYPE &&
    type != LOG_OPENFST_TYPE)
      return retval_hfst;
    else {
      return new HfstTransducer(retval_fsm, type);
    }
  }

  HfstTransducer * SfstCompiler::read_transducer(const char *folder, char *filename, ImplementationType type) {

    std::string filestr("");
    if (NULL != folder) {
      filestr.append(folder);
      filestr.append("/"); // FIX: WINDOWS
    }
    filestr.append(filename);

    if (Verbose)
      fprintf(stderr,"\nreading transducer from %s...", filestr.c_str());
    HfstInputStream is(filestr.c_str());
    //is.open();
    HfstTransducer *t = new HfstTransducer(is);
    is.close();
    free(filename);
    if (Verbose)
      fprintf(stderr,"finished\n");
    t->convert(type);
    return t;
  }

  void SfstCompiler::write_to_file(HfstTransducer *t, const char* folder, char* filename) {

    std::string filestr("");
    if (NULL != folder) {
      filestr.append(folder);
      filestr.append("/"); // FIX: WINDOWS
    }
    filestr.append(filename);

    HfstOutputStream os(filestr, t->get_type());
    //os.open();
    os << *t;
    os.close();
    return;
  }

  HfstTransducer * SfstCompiler::replace_in_context(HfstTransducer * mapping, Repl_Type repl_type, Contexts *contexts, bool optional) {

    HfstTransducerPair tr_pair(*(contexts->left), *(contexts->right));
    StringPairSet sps;
    for( SfstAlphabet::const_iterator it=TheAlphabet.begin(); it!=TheAlphabet.end(); it++ ) {
      SfstAlphabet::NumberPair l=*it;
      sps.insert(StringPair( TheAlphabet.code2symbol(l.first),
                 TheAlphabet.code2symbol(l.second)) );
    }
    switch (repl_type)
      {
      case repl_up:
    return new HfstTransducer(rules::replace_up(tr_pair, *mapping, optional, sps));
    break;
      case repl_down:
    return new HfstTransducer(rules::replace_down(tr_pair, *mapping, optional, sps));
    break;
      case repl_left:
    return new HfstTransducer(rules::replace_left(tr_pair, *mapping, optional, sps));
    break;
      case repl_right:
    return new HfstTransducer(rules::replace_right(tr_pair, *mapping, optional, sps));
    break;
      }
    return NULL;
  }

  HfstTransducer * SfstCompiler::replace(HfstTransducer * mapping, Repl_Type repl_type, bool optional) {

    StringPairSet sps;
    for( SfstAlphabet::const_iterator it=TheAlphabet.begin(); it!=TheAlphabet.end(); it++ ) {
      SfstAlphabet::NumberPair l=*it;
      sps.insert(StringPair( TheAlphabet.code2symbol(l.first),
                 TheAlphabet.code2symbol(l.second)) );
    }
    switch (repl_type)
      {
      case repl_up:
    return new HfstTransducer(rules::replace_up(*mapping, optional, sps));
    break;
      case repl_down:
    return new HfstTransducer(rules::replace_down(*mapping, optional, sps));
    break;
      default:
    fprintf(stderr, "ERROR: invalid replace type requested\n");
    HFST_THROW(HfstException);
      }
    fprintf(stderr, "ERROR: in function SfstCompiler::replace\n");
    HFST_THROW(HfstException);
  }

  HfstTransducer * SfstCompiler::make_mapping( Ranges *list1, Ranges *list2, ImplementationType type ) {

    Ranges *l1=list1;
    Ranges *l2=list2;
    std::vector<StringPairSet> spsv;

    while (l1 && l2) {
      StringPairSet sps;
      for( Range *r1=l1->range; r1; r1=r1->next )
    for( Range *r2=l2->range; r2; r2=r2->next ) {
      sps.insert(StringPair(TheAlphabet.code2symbol(r1->character),
                TheAlphabet.code2symbol(r2->character)) );
    }
      spsv.push_back(sps);
      l1 = l1->next;
      l2 = l2->next;
    }
    while (l1) {
      StringPairSet sps;
      for( Range *r1=l1->range; r1; r1=r1->next ) {
    sps.insert(StringPair(TheAlphabet.code2symbol(r1->character),
                  TheAlphabet.code2symbol(0)) );
      }
      spsv.push_back(sps);
      l1 = l1->next;
    }
    while (l2) {
      StringPairSet sps;
      for( Range *r2=l2->range; r2; r2=r2->next ) {
    sps.insert(StringPair(TheAlphabet.code2symbol(0),
                  TheAlphabet.code2symbol(r2->character)) );
      }
      spsv.push_back(sps);
      l2 = l2->next;
    }

    free_values(list1);
    free_values(list2);

    return new HfstTransducer(spsv, type);

  }


  HfstTransducer * SfstCompiler::result( HfstTransducer *t, bool switch_flag) {

    t = explode(t);

    // delete the variable values
    //std::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->invert();
    //add_alphabet(t);
    t->minimize();
    return t;
  }

  void SfstCompiler::def_alphabet( HfstTransducer *tr )
  {
    tr = explode(tr);
    tr->minimize();

    TheAlphabet.clear_pairs();
    TheAlphabet.add(internal_unknown.c_str(), 1);
    TheAlphabet.add(internal_identity.c_str(), 2);

    // no effect on performance in OMorFi, but in Morphisto?
    if (false || tr->type == SFST_TYPE)
      {
    StringPairSet sps = tr->get_symbol_pairs();
    for (StringPairSet::const_iterator it = sps.begin(); it != sps.end(); it ++)
      {
        unsigned int inumber, onumber;

        if (it->first.compare("<>") == 0)
          inumber=0;
        else
          inumber = TheAlphabet.symbol2code(it->first.c_str());
        if (it->second.compare("<>") == 0)
          onumber=0;
        else
          onumber = TheAlphabet.symbol2code(it->second.c_str());

        TheAlphabet.insert(SfstAlphabet::NumberPair(inumber,onumber));
      }
    Alphabet_Defined = 1;
      }

    else {

      HfstBasicTransducer t(*tr);

      for (HfstBasicTransducer::const_iterator it = t.begin();
       it != t.end(); it++)
    {
      for (hfst::implementations::HfstBasicTransitions::const_iterator tr_it
         = it->begin();
           tr_it != it->end(); tr_it++)
        {
          TheAlphabet.insert(SfstAlphabet::NumberPair
                  (
                   TheAlphabet.symbol2code
                   (tr_it->get_input_symbol().c_str())
                   ,
                   TheAlphabet.symbol2code
                   (tr_it->get_output_symbol().c_str())
                   )
                 );
        }
    }
      Alphabet_Defined = 1;
    }
  }

}
