/*******************************************************************************
*
*       This file is part of the General Hidden Markov Model Library,
*       GHMM version __VERSION__, see http://ghmm.org
*
*       Filename: ghmm/ghmm/model.c
*       Authors:  Benhard Knab, Bernd Wichern, Benjamin Georgi, Alexander Schliep
*
*       Copyright (C) 1998-2004 Alexander Schliep
*       Copyright (C) 1998-2001 ZAIK/ZPR, Universitaet zu Koeln
*       Copyright (C) 2002-2004 Max-Planck-Institut fuer Molekulare Genetik,
*                               Berlin
*
*       Contact: schliep@ghmm.org
*
*       This library is free software; you can redistribute it and/or
*       modify it under the terms of the GNU Library General Public
*       License as published by the Free Software Foundation; either
*       version 2 of the License, or (at your option) any later version.
*
*       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
*       Library General Public License for more details.
*
*       You should have received a copy of the GNU Library General Public
*       License along with this library; if not, write to the Free
*       Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*
*       This file is version $Revision: 2304 $
*                       from $Date: 2013-05-31 13:48:13 -0400 (Fri, 31 May 2013) $
*             last change by $Author: ejb177 $.
*
*******************************************************************************/

#ifdef HAVE_CONFIG_H
#  include "../config.h"
#endif

#include <float.h>
#include <math.h>
#include <assert.h>

#include "ghmmconfig.h"

#include "model.h"
#include "matrix.h"
#include "sequence.h"
#include "rng.h"
#include "foba.h"
#include "mes.h"
#include "mprintf.h"
#include "string.h"
#include "vector.h"
#include "ghmm_internals.h"
#include "xmlreader.h"
#include "xmlwriter.h"

#include "obsolete.h"

#define  __EPS 10e-6

/* Using floating point exceptions */
#ifdef DEBUG_FPE
#include <fenv.h>
static void __attribute__ ((constructor)) trapfpe (void)
{
  /* Enable some exceptions. At startup all exceptions are masked. */
  feenableexcept (FE_INVALID | FE_DIVBYZERO | FE_OVERFLOW);
}
#endif

typedef enum DFSFLAG { DONE, NOTVISITED, VISITED } DFSFLAG;


/*----------------------------------------------------------------------------*/
int ghmm_ipow(const ghmm_dmodel * mo, int x, unsigned int n) {
#define CUR_PROC "ghmm_ipow"
  int result=1;  

  if ((mo->M == x) && (n <= mo->maxorder + 1)) {
    if (mo->pow_lookup)
      result = mo->pow_lookup[n];
  } else {
    while (n != 0) {
      if (n & 1)
        result *= x;
      x *= x;
      n >>= 1;
    }
  }

  return result;
#undef CUR_PROC
}

/*----------------------------------------------------------------------------*/
static int model_state_alloc (ghmm_dstate * s, int M, int in_states,
                              int out_states)
{
# define CUR_PROC "model_state_alloc"
  int res = -1;
  ARRAY_CALLOC (s->b, M);
  if (out_states > 0) {
    ARRAY_CALLOC (s->out_id, out_states);
    ARRAY_CALLOC (s->out_a, out_states);
  }
  if (in_states > 0) {
    ARRAY_CALLOC (s->in_id, in_states);
    ARRAY_CALLOC (s->in_a, in_states);
  }
  res = 0;
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  return (res);
# undef CUR_PROC
}                               /* model_state_alloc */


/*----------------------------------------------------------------------------*/
ghmm_dmodel * ghmm_dmodel_calloc(int M, int N, int modeltype, int * inDegVec,
				int * outDegVec) {
#define CUR_PROC "ghmm_dmodel_calloc"
  int i;
  ghmm_dmodel * mo;

  assert(modeltype & GHMM_kDiscreteHMM);

  ARRAY_CALLOC(mo, 1);

  mo->M = M;
  mo->N = N;
  mo->model_type = modeltype;

  ARRAY_CALLOC(mo->s, N);
  for (i=0; i<N; i++) {
    if (model_state_alloc(&(mo->s[i]), M, inDegVec[i], outDegVec[i]))
      goto STOP;
  }

  if (mo->model_type & GHMM_kSilentStates)
    ARRAY_CALLOC(mo->silent, N);

  if (mo->model_type & GHMM_kTiedEmissions) {
    ARRAY_CALLOC(mo->tied_to, N);
    for (i=0; i<N; ++i)
      mo->tied_to[i] = GHMM_kUntied;
  }
   
  if (mo->model_type & GHMM_kBackgroundDistributions) {
    ARRAY_MALLOC(mo->background_id, N);
    for (i=0; i<N; ++i)
      mo->background_id[i] = GHMM_kNoBackgroundDistribution;
  }

  if (mo->model_type & GHMM_kHigherOrderEmissions)
    ARRAY_CALLOC(mo->order, N);
   
  if (mo->model_type & GHMM_kLabeledStates)
    ARRAY_CALLOC(mo->label, N);

  return mo;
STOP:
  ghmm_dmodel_free(&mo);
  return NULL;
#undef CUR_PROC
}


/*----------------------------------------------------------------------------*/
#ifdef GHMM_OBSOLETE
static int model_copy_vectors (ghmm_dmodel * mo, int index, double **a_matrix,
                               double **b_matrix, double *pi, int *fix)
{
#define CUR_PROC "model_copy_vectors"
  int i, cnt_out = 0, cnt_in = 0;
  mo->s[index].pi = pi[index];
  mo->s[index].fix = fix[index];
  for (i = 0; i < mo->M; i++)
    mo->s[index].b[i] = b_matrix[index][i];
  for (i = 0; i < mo->N; i++) {
    if (a_matrix[index][i]) {   /* Transitions to a following state possible */
      if (cnt_out >= mo->s[index].out_states) {
        GHMM_LOG_QUEUED(LCONVERTED);
        return (-1);
      }
      mo->s[index].out_id[cnt_out] = i;
      mo->s[index].out_a[cnt_out] = a_matrix[index][i];
      cnt_out++;
    }
    if (a_matrix[i][index]) {   /* Transitions to a previous state possible */
      if (cnt_in >= mo->s[index].in_states) {
        GHMM_LOG_QUEUED(LCONVERTED);
        return (-1);
      }
      mo->s[index].in_id[cnt_in] = i;
      mo->s[index].in_a[cnt_in] = a_matrix[i][index];
      cnt_in++;
    }
  }
  return (0);
#undef CUR_PROC
}                               /* model_copy_vectors */


/*============================================================================*/

/* Old prototype:

ghmm_dmodel **ghmm_dmodel_read(char *filename, int *mo_number, int **seq,
			 const int *seq_len, int seq_number) { */

ghmm_dmodel **ghmm_dmodel_read (char *filename, int *mo_number)
{
#define CUR_PROC "ghmm_dmodel_read"
  int j;
  long new_models = 0;
  scanner_t *s = NULL;
  ghmm_dmodel **mo = NULL;
  *mo_number = 0;
  s = ighmm_scanner_alloc (filename);
  if (!s) {
    GHMM_LOG_QUEUED(LCONVERTED);
    goto STOP;
  }
  while (!s->err && !s->eof) {
    ighmm_scanner_get_name (s);
    ighmm_scanner_consume (s, '=');
    if (s->err)
      goto STOP;
    if (!strcmp (s->id, "HMM") || !strcmp (s->id, "hmm")) {
      (*mo_number)++;
      /* more mem */
      ARRAY_REALLOC (mo, *mo_number);
      mo[*mo_number - 1] = ghmm_dmodel_direct_read (s, (int *) &new_models);
      if (!mo[*mo_number - 1]) {
        GHMM_LOG_QUEUED(LCONVERTED);
        goto STOP;
      }
      /* Copies the model, that has already been read. */
      if (new_models > 1) {
        /* "-1" because mo_number++ has already been done. */
        ARRAY_REALLOC (mo, *mo_number - 1 + new_models);
        for (j = 1; j < new_models; j++) {
          mo[*mo_number] = ghmm_dmodel_copy (mo[*mo_number - 1]);
          if (!mo[*mo_number]) {
            GHMM_LOG_QUEUED(LCONVERTED);
            goto STOP;
          }
          (*mo_number)++;
        }
      }
    }
    else if (!strcmp (s->id, "HMM_SEQ")) {
      ghmm_dmodel **tmp_mo = NULL;
      tmp_mo = ghmm_dmodel_from_sequence_ascii (s, &new_models);
      ARRAY_REALLOC (mo, (*mo_number + new_models));
      for (j = 0; j < new_models; j++) {
        if (!tmp_mo[j]) {
          GHMM_LOG_QUEUED(LCONVERTED);
          goto STOP;
        }
        mo[*mo_number] = tmp_mo[j];
        (*mo_number)++;
      }
    }
    else {
      ighmm_scanner_error (s, "unknown identifier");
      goto STOP;
    }
    ighmm_scanner_consume (s, ';');
    if (s->err)
      goto STOP;
  }                             /* while(!s->err && !s->eof) */
  
  ighmm_scanner_free(&s);
  return mo;
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  ighmm_scanner_free(&s);
  return NULL;
#undef CUR_PROC
}                               /* ghmm_dmodel_read */



/*============================================================================*/
ghmm_dmodel *ghmm_dmodel_direct_read (scanner_t * s, int *multip)
{
#define CUR_PROC "ghmm_dmodel_direct_read"
  int i, m_read, n_read, a_read, b_read, pi_read, len, prior_read, fix_read;
  int mt_read=0;
  ghmm_dmodel *mo = NULL;
  double **a_matrix = NULL, **b_matrix = NULL;
  double *pi_vector = NULL;
  int *fix_vector = NULL;
  m_read = n_read = a_read = b_read = pi_read = prior_read = fix_read = 0;
  *multip = 1;                  /* default */
  ARRAY_CALLOC (mo, 1);

  ighmm_scanner_consume (s, '{');
  if (s->err)
    goto STOP;
  while (!s->err && !s->eof && s->c - '}') {
    ighmm_scanner_get_name(s);
    if (strcmp(s->id, "M") && strcmp(s->id, "N") && strcmp(s->id, "Pi")
        && strcmp(s->id, "A") && strcmp(s->id, "B") && strcmp(s->id, "multip")
	&& strcmp(s->id, "prior") && strcmp(s->id, "fix_state") && strcmp(s->id, "ModelType")) {
      ighmm_scanner_error(s, "unknown identifier");
      goto STOP;
    }
    ighmm_scanner_consume (s, '=');
    if (s->err)
      goto STOP;
    if (!strcmp (s->id, "multip")) {
      *multip = ighmm_scanner_get_int (s);
      if (*multip < 1) {        /* Doesn't make any sense! */
        *multip = 1;
        GHMM_LOG(LCONVERTED, "Multip < 1 ignored\n");
      }
    }
    else if (!strcmp (s->id, "M")) {    /*Number of output values */
      if (m_read) {
        ighmm_scanner_error (s, "identifier M twice");
        goto STOP;
      }
      mo->M = ighmm_scanner_get_int (s);
      m_read = 1;
    }
    else if (!strcmp (s->id, "N")) {    /*Number of states */
      if (n_read) {
        ighmm_scanner_error (s, "identifier N twice");
        goto STOP;
      }
      mo->N = ighmm_scanner_get_int (s);
      ARRAY_CALLOC (mo->s, mo->N);
      n_read = 1;
    }
    else if (!strcmp (s->id, "A")) {    /*Transition probability */
      if (!n_read) {
        ighmm_scanner_error (s, "need N as a range for A");
        goto STOP;
      }
      if (a_read) {
        ighmm_scanner_error (s, "identifier A twice");
        goto STOP;
      }
      ARRAY_CALLOC (a_matrix, mo->N);
      ighmm_scanner_get_name (s);
      if (!strcmp (s->id, "matrix")) {
        if (ighmm_cmatrix_read (s, a_matrix, mo->N, mo->N)) {
          ighmm_scanner_error (s, "unable to read matrix A");
          goto STOP;
        }
        a_read = 1;
      }
      else {
        ighmm_scanner_error (s, "unknown identifier");
        goto STOP;
      }
    }
    else if (!strcmp (s->id, "B")) {    /*Output probability */
      if ((!n_read) || (!m_read)) {
        ighmm_scanner_error (s, "need M and N as a range for B");
        goto STOP;
      }
      if (b_read) {
        ighmm_scanner_error (s, "identifier B twice");
        goto STOP;
      }
      ARRAY_CALLOC (b_matrix, mo->N);
      ighmm_scanner_get_name (s);
      if (!strcmp (s->id, "matrix")) {
        if (ighmm_cmatrix_read (s, b_matrix, mo->N, mo->M)) {
          ighmm_scanner_error (s, "unable to read matrix B");
          goto STOP;
        }
        b_read = 1;
      }
      else {
        ighmm_scanner_error (s, "unknown identifier");
        goto STOP;
      }
    }
    else if (!strcmp (s->id, "prior")) {        /*A prior model */
      if (prior_read) {
        ighmm_scanner_error (s, "identifier prior twice");
        goto STOP;
      }
      mo->prior = ighmm_scanner_get_edouble (s);
      if ((mo->prior < 0 || mo->prior > 1) && mo->prior != -1) {
        ighmm_scanner_error (s, "invalid model prior");
        goto STOP;
      }
      prior_read = 1;
    }
    else if (!strcmp (s->id, "ModelType")) {    /* Model type*/
      if (mt_read) {
        ighmm_scanner_error(s, "identifier ModelType twice");
        goto STOP;
      }
      mo->model_type = ighmm_scanner_get_int(s);
      if (mo->model_type & (GHMM_kLabeledStates + GHMM_kHigherOrderEmissions)) {
	ighmm_scanner_error(s, "unsupported Model Type");
	goto STOP;
      }
      mt_read = 1;
    }
    else if (!strcmp (s->id, "Pi")) {   /*Initial state probabilty */
      if (!n_read) {
        ighmm_scanner_error (s, "need N as a range for Pi");
        goto STOP;
      }
      if (pi_read) {
        ighmm_scanner_error (s, "identifier Pi twice");
        goto STOP;
      }
      ighmm_scanner_get_name (s);
      if (!strcmp (s->id, "vector")) {
        ighmm_scanner_consume (s, '{');
        if (s->err)
          goto STOP;
        pi_vector = scanner_get_double_earray (s, &len);
        if (len != mo->N) {
          ighmm_scanner_error (s, "wrong number of elements in PI");
          goto STOP;
        }
        ighmm_scanner_consume (s, ';');
        if (s->err)
          goto STOP;
        ighmm_scanner_consume (s, '}');
        if (s->err)
          goto STOP;
        pi_read = 1;
      }
      else {
        ighmm_scanner_error (s, "unknown identifier");
        goto STOP;
      }
    }
    else if (!strcmp (s->id, "fix_state")) {
      if (!n_read) {
        ighmm_scanner_error (s, "need N as a range for fix_state");
        goto STOP;
      }
      if (fix_read) {
        ighmm_scanner_error (s, "identifier fix_state twice");
        goto STOP;
      }
      ighmm_scanner_get_name (s);
      if (!strcmp (s->id, "vector")) {
        ighmm_scanner_consume (s, '{');
        if (s->err)
          goto STOP;
        fix_vector = scanner_get_int_array (s, &len);
        if (len != mo->N) {
          ighmm_scanner_error (s, "wrong number of elements in fix_state");
          goto STOP;
        }
        ighmm_scanner_consume (s, ';');
        if (s->err)
          goto STOP;
        ighmm_scanner_consume (s, '}');
        if (s->err)
          goto STOP;
        fix_read = 1;
      }
      else {
        ighmm_scanner_error (s, "unknown identifier");
        goto STOP;
      }
    }
    else {
      ighmm_scanner_error (s, "unknown identifier");
      goto STOP;
    }
    ighmm_scanner_consume (s, ';');
    if (s->err)
      goto STOP;
  }                             /* while(..s->c-'}') */
  ighmm_scanner_consume (s, '}');
  if (s->err)
    goto STOP;

  /* No prior read --> give it the value -1 */
  if (prior_read == 0)
    mo->prior = -1;
  /* Allocate memory for the model */
  for (i = 0; i < mo->N; i++) {
    mo->s[i].out_states = ighmm_cmatrix_notzero_columns (a_matrix, i, mo->N);
    mo->s[i].in_states = ighmm_cmatrix_notzero_rows (a_matrix, i, mo->N);
    if (model_state_alloc (mo->s + i, mo->M, mo->s[i].in_states,
                           mo->s[i].out_states)) {
      GHMM_LOG_QUEUED(LCONVERTED);
      goto STOP;
    }

    /* Assign the parameters to the model */
    if (!a_matrix) {
      fprintf (stderr, "no A matrix specified in file!\n");
      exit (1);
    }
    if (!b_matrix) {
      fprintf (stderr, "no B matrix specified in file!\n");
      exit (1);
    }
    if (!fix_vector) {
      fprintf (stderr, "no fix_state vector specified in file!\n");
      exit (1);
    }
    if (!pi_vector) {
      fprintf (stderr, "no Pi vector specified in file!\n");
      exit (1);
    }

    if (model_copy_vectors (mo, i, a_matrix, b_matrix, pi_vector, fix_vector)) {
      GHMM_LOG_QUEUED(LCONVERTED);
      goto STOP;
    }
  }
  ighmm_cmatrix_free (&a_matrix, mo->N);
  ighmm_cmatrix_free (&b_matrix, mo->N);
  m_free (pi_vector);
  return (mo);
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  ighmm_cmatrix_free (&a_matrix, mo->N);
  ighmm_cmatrix_free (&b_matrix, mo->N);
  m_free (pi_vector);
  ghmm_dmodel_free(&mo);
  return NULL;
#undef CUR_PROC
}                               /* ghmm_dmodel_direct_read */
#endif /* GHMM_OBSOLETE */

/*============================================================================*/
/* Produces models from given sequences */
ghmm_dmodel **ghmm_dmodel_from_sequence (ghmm_dseq * sq, long *mo_number)
{
#define CUR_PROC "ghmm_dmodel_from_sequence"
  long i;
  int max_symb;
  ghmm_dmodel **mo = NULL;
  ARRAY_CALLOC (mo, sq->seq_number);
  max_symb = ghmm_dseq_max_symbol (sq);
  for (i = 0; i < sq->seq_number; i++)
    mo[i] = ghmm_dmodel_generate_from_sequence (sq->seq[i], sq->seq_len[i],
                                          max_symb + 1);
  *mo_number = sq->seq_number;
  return mo;
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  for (i = 0; i < *mo_number; i++)
    ghmm_dmodel_free(&(mo[i]));
  return NULL;
#undef CUR_PROC
}                               /* ghmm_dmodel_from_sequence */

#ifdef GHMM_OBSOLETE
/*============================================================================*/
/* Produces models form given sequences */
ghmm_dmodel **ghmm_dmodel_from_sequence_ascii (scanner_t * s, long *mo_number)
{
#define CUR_PROC "ghmm_dmodel_from_sequence_ascii"
  long i;
  int max_symb;
  ghmm_dmodel **mo = NULL;
  ghmm_dseq *sq = NULL;

  ighmm_scanner_consume (s, '{');
  if (s->err)
    goto STOP;
  while (!s->err && !s->eof && s->c - '}') {
    ighmm_scanner_get_name (s);
    ighmm_scanner_consume (s, '=');
    if (s->err)
      goto STOP;
    /* Reads sequences on normal format */
    if (!strcmp (s->id, "SEQ")) {
      sq = ghmm_dseq_read_alloc (s);
      if (!sq) {
        GHMM_LOG_QUEUED(LCONVERTED);
        goto STOP;
      }
    }
    else {
      ighmm_scanner_error (s, "unknown identifier");
      goto STOP;
    }
    ighmm_scanner_consume (s, ';');
    if (s->err)
      goto STOP;
  }                             /* while(..s->c-'}') */
  ighmm_scanner_consume (s, '}');
  if (s->err)
    goto STOP;

  ARRAY_CALLOC (mo, sq->seq_number);
  /* The biggest symbol that occurs */
  max_symb = ghmm_dseq_max_symbol (sq);
  for (i = 0; i < sq->seq_number; i++)
    mo[i] = ghmm_dmodel_generate_from_sequence (sq->seq[i], sq->seq_len[i],
                                          max_symb + 1);

  *mo_number = sq->seq_number;
  ghmm_dseq_free (&sq);
  return mo;
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  ghmm_dseq_free (&sq);
  for (i = 0; i < *mo_number; i++)
    ghmm_dmodel_free(&(mo[i]));
  return NULL;
#undef CUR_PROC
}                               /* ghmm_dmodel_from_sequence_ascii */
#endif /* GHMM_OBSOLETE */

/*============================================================================*/
static void ghmm_alphabet_free(ghmm_alphabet *a) {
#define CUR_PROC "ghmm_alphabet_free"
    int i;
    if(a && a->symbols){
      for (i=0; i<a->size; i++)
          m_free(a->symbols[i]);
      m_free(a->symbols);
      m_free(a);
    }
#undef CUR_PROC
}

/*============================================================================*/
int ghmm_dmodel_free(ghmm_dmodel ** mo) {
#define CUR_PROC "ghmm_dmodel_free"
  int i;
  ghmm_dmodel *m = NULL;
  mes_check_ptr (mo, return (-1));
  m = *mo;
  mes_check_ptr (m, return (-1));

  for (i=0; i < m->N && m->s; i++)
    ghmm_dstate_clean(&m->s[i]);

  if (m->s)
    m_free(m->s);
  if (m->alphabet)
    ghmm_alphabet_free(m->alphabet);
  if (m->name)
    m_free(m->name);
  if (m->model_type & GHMM_kSilentStates) {
    if (m->topo_order)
      m_free(m->topo_order);
    m_free(m->silent);
  }
  if (m->model_type & GHMM_kTiedEmissions)
    m_free(m->tied_to);
  if (m->pow_lookup)
    m_free(m->pow_lookup);
  if (m->model_type & GHMM_kBackgroundDistributions) {
    m_free(m->background_id);
    ghmm_dbackground_free(m->bp);
  }
  if (m->model_type & GHMM_kHigherOrderEmissions)
    m_free(m->order);
  if (m->model_type & GHMM_kLabeledStates)
    m_free(m->label);
 
  m_free(m);
  return (0);
#undef CUR_PROC
}  /* ghmm_dmodel_free */


/*============================================================================*/
int ghmm_dbackground_free(ghmm_dbackground *bg) {
#define CUR_PROC "ghmm_dbackground_free"
  int i;
  if (bg->order)
    m_free(bg->order);
  if (bg->b)
    ighmm_cmatrix_free(&(bg->b), bg->n);
  if (bg->name) {
    for (i=0; i<bg->n; i++)
      free(bg->name[i]);
    m_free(bg->name);
  }
  m_free(bg);
  return 0;
#undef CUR_PROC
}

/*============================================================================*/
ghmm_dmodel *ghmm_dmodel_copy (const ghmm_dmodel * mo)
{
# define CUR_PROC "ghmm_dmodel_copy"
  int i, j, nachf, vorg, m, size;
  ghmm_dmodel *m2 = NULL;
  ARRAY_CALLOC (m2, 1);
  ARRAY_CALLOC (m2->s, mo->N);

  if (mo->model_type & GHMM_kSilentStates)
    ARRAY_CALLOC (m2->silent, mo->N);
  if (mo->model_type & GHMM_kTiedEmissions)
    ARRAY_CALLOC (m2->tied_to, mo->N);
  if (mo->model_type & GHMM_kBackgroundDistributions) {
    ARRAY_CALLOC (m2->background_id, mo->N);
    m2->bp = mo->bp;
  }
  if (mo->model_type & GHMM_kHigherOrderEmissions)
    ARRAY_CALLOC (m2->order, mo->N);
  if (mo->model_type & GHMM_kLabeledStates)
    ARRAY_CALLOC (m2->label, mo->N);

  ARRAY_MALLOC(m2->pow_lookup, mo->maxorder+2);
  
  for (i = 0; i < mo->N; i++) {
    if (mo->model_type & GHMM_kHigherOrderEmissions)
      size = ghmm_ipow(mo, mo->M, mo->order[i]+1);
    else
      size = mo->M;

    nachf = mo->s[i].out_states;
    vorg = mo->s[i].in_states;
    
    ARRAY_CALLOC (m2->s[i].out_id, nachf);
    ARRAY_CALLOC (m2->s[i].out_a, nachf);
    ARRAY_CALLOC (m2->s[i].in_id, vorg);
    ARRAY_CALLOC (m2->s[i].in_a, vorg);
    ARRAY_CALLOC (m2->s[i].b, size);

    /* copy the values */
    for (j = 0; j < nachf; j++) {
      m2->s[i].out_a[j] = mo->s[i].out_a[j];
      m2->s[i].out_id[j] = mo->s[i].out_id[j];
    }
    for (j = 0; j < vorg; j++) {
      m2->s[i].in_a[j] = mo->s[i].in_a[j];
      m2->s[i].in_id[j] = mo->s[i].in_id[j];
    }
    /* copy all b values for higher order states */
    for (m = 0; m < size; m++)
      m2->s[i].b[m] = mo->s[i].b[m];

    m2->s[i].pi = mo->s[i].pi;
    m2->s[i].fix = mo->s[i].fix;
    if (mo->model_type & GHMM_kSilentStates)
      m2->silent[i] = mo->silent[i];
    if (mo->model_type & GHMM_kTiedEmissions)
      m2->tied_to[i] = mo->tied_to[i];
    if (mo->model_type & GHMM_kLabeledStates)
      m2->label[i] = mo->label[i];
    if (mo->model_type & GHMM_kHigherOrderEmissions)
      m2->order[i] = mo->order[i];
    if (mo->model_type & GHMM_kBackgroundDistributions)
      m2->background_id[i] = mo->background_id[i];
    m2->s[i].out_states = nachf;
    m2->s[i].in_states = vorg;
  }

  m2->N = mo->N;
  m2->M = mo->M;
  m2->prior = mo->prior;
  if (mo->model_type & GHMM_kHigherOrderEmissions) {
    m2->maxorder = mo->maxorder;
    for (i=0; i < mo->maxorder+2; i++)
      m2->pow_lookup[i] = mo->pow_lookup[i];
  }  

  m2->model_type = mo->model_type;
  /* not necessary but the history is at least initialised */
  m2->emission_history = mo->emission_history;
  return (m2);
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  ghmm_dmodel_free (&m2);
  return (NULL);
# undef CUR_PROC
}                               /* ghmm_dmodel_copy */


/*============================================================================*/
int ghmm_dmodel_check(const ghmm_dmodel * mo) {
# define CUR_PROC "ghmm_dmodel_check"
  int res=0;
  double sum;
  int i, j, imag=0;

  /* The sum of the Pi[i]'s is 1 */
  sum = 0.0;
  for (i = 0; i < mo->N; i++)
    sum += mo->s[i].pi;

  if (fabs(sum-1.0) >= GHMM_EPS_PREC) {
    GHMM_LOG(LERROR, "sum Pi[i] != 1.0");
    res--;
  }

  /* check each state */
  for (i=0; i<mo->N; i++) {
    sum = 0.0;
    /* Sum the a[i][j]'s : normalized out transitions */
    for (j=0; j < mo->s[i].out_states; j++) {
      sum += mo->s[i].out_a[j];
    }
    if (j==0) {
      GHMM_LOG_PRINTF(LDEBUG, LOC, "out_states = 0 (state %d -> final state!)", i);
    }
    else if (sum == 0.0) {
      GHMM_LOG_PRINTF(LWARN, LOC, "sum of s[%d].out_a[*] = 0.0 (assumed final "
                      "state but %d transitions)", i, mo->s[i].out_states);
    }
    else if (fabs(sum-1.0) >= GHMM_EPS_PREC) {
      GHMM_LOG_PRINTF(LERROR, LOC, "sum of s[%d].out_a[*] = %f != 1.0", i, sum);
      res--;
    }
    /* Sum the a[i][j]'s : normalized in transitions */
    sum = mo->s[i].pi;
    for (j=0; j<mo->s[i].in_states; j++)
      sum += mo->s[i].in_a[j];

    if (fabs(sum) <= GHMM_EPS_PREC) {
      imag = 1;
      GHMM_LOG_PRINTF(LINFO, LOC, "state %d can't be reached", i);
    }

    /* Sum the b[j]'s: normalized emission probs */
    sum = 0.0;
    for (j=0; j<mo->M; j++)
      sum += mo->s[i].b[j];

    if (imag) {
      /* not reachable states */
      if ((fabs(sum + mo->M ) >= GHMM_EPS_PREC)) {
        GHMM_LOG_PRINTF(LERROR, LOC, "state %d can't be reached but is not set"
                        " as non-reachale state", i);
        res--;
      }
    } else if ((mo->model_type & GHMM_kSilentStates) && mo->silent[i]) {
      /* silent states */
      if (sum != 0.0) {
        GHMM_LOG_PRINTF(LERROR, LOC, "state %d is silent but has a non-zero"
                        " emission probability", i);
        res--;
      }
    } 
    else {
      /* normal states */
      if (fabs(sum-1.0) >= GHMM_EPS_PREC) {
        GHMM_LOG_PRINTF(LERROR, LOC, "sum s[%d].b[*] = %f != 1.0", i, sum);
        res--;
      }
    }
  }

  return (res);
# undef CUR_PROC
}                               /* ghmm_dmodel_check */

/*============================================================================*/
int ghmm_dmodel_check_compatibility (ghmm_dmodel ** mo, int model_number)
{
#define CUR_PROC "ghmm_dmodel_check_compatibility"
  int i;
  for (i = 1; i < model_number; i++)
    if (-1 == ghmm_dmodel_check_compatibel_models (mo[0], mo[i]))
      return -1;

  return 0;
#undef CUR_PROC
}

/*============================================================================*/
int ghmm_dmodel_check_compatibel_models (const ghmm_dmodel * mo, const ghmm_dmodel * m2)
{
#define CUR_PROC "ghmm_dmodel_check_compatibel_models"
  int i, j;

  if (mo->N != m2->N) {
    GHMM_LOG_PRINTF(LINFO, LOC, "different number of states (%d != %d)\n",
                   mo->N, m2->N);
    return -1;
  }
  if (mo->M != m2->M) {
    GHMM_LOG_PRINTF(LINFO, LOC, "different number of possible outputs (%d != %d)\n",
                   mo->M, m2->M);
    return -1;
  }
  for (i=0; i<mo->N; ++i) {
    if (mo->s[i].out_states != m2->s[i].out_states) {
      GHMM_LOG_PRINTF(LINFO, LOC, "different number of outstates (%d != %d) in state %d.\n",
                   mo->s[i].out_states, m2->s[i].out_states, i);
      return -1;
    }
    for (j=0; j<mo->s[i].out_states; ++j) {
      if (mo->s[i].out_id[j] != m2->s[i].out_id[j]) {
	GHMM_LOG_PRINTF(LINFO, LOC, "different out_ids (%d != %d) in entry %d of state %d.\n",
		      mo->s[i].out_id[j], m2->s[i].out_id[j], j, i);
	return -1;
      }
    }
  }

  return 0;
#undef CUR_PROC
}                               /* ghmm_dmodel_check_compatibility */

/*============================================================================*/
ghmm_dmodel *ghmm_dmodel_generate_from_sequence (const int *seq, int seq_len,
                                     int anz_symb)
{
#define CUR_PROC "ghmm_dmodel_generate_from_sequence"
  int i;
  ghmm_dmodel *mo = NULL;
  ghmm_dstate *s = NULL;
  ARRAY_CALLOC (mo, 1);
  mo->N = seq_len;
  mo->M = anz_symb;
  /* All models generated from sequences have to be LeftRight-models */
  mo->model_type = GHMM_kLeftRight;

  /* Allocate memory for all vectors */
  ARRAY_CALLOC (mo->s, mo->N);
  for (i = 0; i < mo->N; i++) {
    if (i == 0) {               /* Initial state */
      if (model_state_alloc (mo->s, mo->M, 0, 1)) {
        GHMM_LOG_QUEUED(LCONVERTED);
        goto STOP;
      }
    }
    else if (i == mo->N - 1) {  /* End state */
      if (model_state_alloc (mo->s + i, mo->M, 1, 0)) {
        GHMM_LOG_QUEUED(LCONVERTED);
        goto STOP;
      }
    }
    else {                      /* others */
      if (model_state_alloc (mo->s + i, mo->M, 1, 1)) {
        GHMM_LOG_QUEUED(LCONVERTED);
        goto STOP;
      }
    }
  }

  /* Allocate states with the right values, the initial state and the end 
     state extra */
  for (i = 1; i < mo->N - 1; i++) {
    s = mo->s + i;
    s->pi = 0.0;
    s->out_states = 1;
    s->in_states = 1;
    s->b[seq[i]] = 1.0;         /* others stay 0 */
    *(s->out_id) = i + 1;
    *(s->in_id) = i - 1;
    *(s->out_a) = *(s->in_a) = 1.0;
  }

  /* Initial state */
  s = mo->s;
  s->pi = 1.0;
  s->out_states = 1;
  s->in_states = 0;
  s->b[seq[0]] = 1.0;
  *(s->out_id) = 1;
  *(s->out_a) = 1.0;
  /* No in_id and in_a */

  /* End state */
  s = mo->s + mo->N - 1;
  s->pi = 0.0;
  s->out_states = 0;
  s->in_states = 1;
  s->b[seq[mo->N - 1]] = 1.0;   /* All other b's stay zero */
  *(s->in_id) = mo->N - 2;
  *(s->in_a) = 1.0;
  /* No out_id and out_a */

  if (ghmm_dmodel_check (mo)) {
    GHMM_LOG_QUEUED(LCONVERTED);
    goto STOP;
  }
  return mo;
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  ghmm_dmodel_free (&mo);
  return NULL;
#undef CUR_PROC
}                               /* ghmm_dmodel_generate_from_sequence */


/*===========================================================================*/

 static int get_random_output (ghmm_dmodel * mo, int i, int position)
{
#define CUR_PROC "get_random_output"
  int m, e_index;
  double p, sum=0.0;

  p = GHMM_RNG_UNIFORM (RNG);

  for (m = 0; m < mo->M; m++) {
    /* get the right index for higher order emission models */
    e_index = get_emission_index(mo, i, m, position);

    /* get the probability, exit, if the index is -1 */
    if (-1 != e_index) {
      sum += mo->s[i].b[e_index];
      if (sum >= p)
        break;
    }
    else {
      fprintf (stderr,
               "ERROR: State has order %d, but in the history are only %d emissions.\n",
               mo->order[i], position);
      return -1;
    }
  }

  if (mo->M == m) {
    fprintf (stderr,
             "ERROR: no valid output choosen. Are the Probabilities correct? sum: %g, p: %g\n",
             sum, p);
    return -1;
  }

  return (m);
#undef CUR_PROC
}                               /* get_random_output */

/*============================================================================*/

ghmm_dseq *ghmm_dmodel_generate_sequences(ghmm_dmodel* mo, int seed, int global_len,
                                          long seq_number, int Tmax)
{
#define CUR_PROC "ghmm_dmodel_generate_sequences"

  ghmm_dseq *sq = NULL;
  int state;
  int j, m, j_id;
  double p, sum, max_sum;
  int len = global_len;
  int n = 0;
  int pos, label_pos;

  sq = ghmm_dseq_calloc(seq_number);
  if (!sq) {
    GHMM_LOG_QUEUED(LCONVERTED);
    goto STOP;
  }

  /* allocating additional fields for the state sequence in the ghmm_dseq struct */
  ARRAY_CALLOC(sq->states, seq_number);
  ARRAY_CALLOC(sq->states_len, seq_number);

  /* A specific length of the sequences isn't given. As a model should have
     an end state, the konstant MAX_SEQ_LEN is used. */
  if (len <= 0)
    len = (int)GHMM_MAX_SEQ_LEN;

  if (seed > 0) {
    GHMM_RNG_SET(RNG, seed);
  }

  /* initialize the emission history */
  mo->emission_history = 0;

  while (n < seq_number) {
    ARRAY_CALLOC(sq->seq[n], len);
    
    /* for silent models we have to allocate for the maximal possible number
       of lables and states */
    if (mo->model_type & GHMM_kSilentStates) {
      ARRAY_CALLOC(sq->states[n], len * mo->N);
    }
    else {
      ARRAY_CALLOC(sq->states[n], len);
    }

    pos = label_pos = 0;
    
    /* Get a random initial state i */
    p = GHMM_RNG_UNIFORM(RNG);
    sum = 0.0;
    for (state=0; state < mo->N; state++) {
      sum += mo->s[state].pi;
      if (sum >= p)
        break;
    }

    while (pos < len) {
      /* save the state path and label */
      sq->states[n][label_pos] = state;
      label_pos++;

      /* Get a random output m if the state is not a silent state */
      if (!(mo->model_type & GHMM_kSilentStates) || !(mo->silent[state])) {
        m = get_random_output(mo, state, pos);
        update_emission_history(mo, m);
        sq->seq[n][pos] = m;
        pos++;
      }

      /* get next state */
      p = GHMM_RNG_UNIFORM(RNG);
      if (pos < mo->maxorder) {
        max_sum = 0.0;
        for (j = 0; j < mo->s[state].out_states; j++) {
          j_id = mo->s[state].out_id[j];
          if (!(mo->model_type & GHMM_kHigherOrderEmissions) || mo->order[j_id] <= pos)
            max_sum += mo->s[state].out_a[j];
        }
        if (j && fabs(max_sum) < GHMM_EPS_PREC) {
          GHMM_LOG_PRINTF(LERROR, LOC, "No possible transition from state %d "
                          "since all successor states require more history "
                          "than seen up to position: %d.",
                          state, pos);
          break;
        }
        if (j)
          p *= max_sum;
      }

      sum = 0.0;
      for (j = 0; j < mo->s[state].out_states; j++) {
        j_id = mo->s[state].out_id[j];
        if (!(mo->model_type & GHMM_kHigherOrderEmissions) || mo->order[j_id] <= pos) {
          sum += mo->s[state].out_a[j];
          if (sum >= p)
            break;
        }
      }

      if (sum == 0.0) {
        GHMM_LOG_PRINTF(LINFO, LOC, "final state (%d) reached at position %d "
                        "of sequence %d", state, pos, n);
	break;
      }

      state = j_id;
    }                           /* while (pos < len) */

    /* realocate state path and label sequence to actual size */ 
    if (mo->model_type & GHMM_kSilentStates) {
      ARRAY_REALLOC(sq->states[n], label_pos);
    }

    sq->seq_len[n] = pos;
    sq->states_len[n] = label_pos;
    n++;
  }                             /* while( n < seq_number ) */

  return (sq);
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  ghmm_dseq_free(&sq);
  return NULL;
#undef CUR_PROC
}

/*============================================================================*/

double ghmm_dmodel_likelihood (ghmm_dmodel * mo, ghmm_dseq * sq)
{
# define CUR_PROC "ghmm_dmodel_likelihood"
  double log_p_i, log_p;
  int found, i;

  /* printf("***  model_likelihood:\n"); */

  found = 0;
  log_p = 0.0;
  for (i = 0; i < sq->seq_number; i++) {

/* 	printf("sequence:\n"); */
/* 	for (j=0;j < sq->seq_len[i];j++) {  */
/* 		printf("%d, ",sq->seq[i][j]); */
/* 	} */
/* 	printf("\n"); */


    if (ghmm_dmodel_logp (mo, sq->seq[i], sq->seq_len[i], &log_p_i) == -1) {
      GHMM_LOG_QUEUED(LCONVERTED);
      goto STOP;
    }

/* 	printf("\nlog_p_i = %f\n", log_p_i); */

    if (log_p_i != +1) {
      log_p += log_p_i;
      found = 1;
    }
    else {
      GHMM_LOG_PRINTF(LWARN, LOC, "sequence[%d] can't be build.", i);
    }
  }
  if (!found)
    log_p = +1.0;
  return (log_p);
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  return -1;
# undef CUR_PROC
}                               /* ghmm_dmodel_likelihood */

double ghmm_dmodel_get_transition(ghmm_dmodel* mo, int i, int j)
{
# define CUR_PROC "ghmm_dmodel_get_transition"
  int out;

  if (mo->s && mo->s[i].out_a && mo->s[j].in_a) {
    for (out=0; out < mo->s[i].out_states; out++) {
      if (mo->s[i].out_id[out] == j)
        return mo->s[i].out_a[out];
    }
  }
  return 0.0;
# undef CUR_PROC
}   /* ghmm_dmodel_get_transition */

int ghmm_dmodel_check_transition(ghmm_dmodel* mo, int i, int j)
{
# define CUR_PROC "ghmm_dmodel_check_transition"
  int out;

  if (mo->s && mo->s[i].out_a && mo->s[j].in_a) {
    for (out=0; out < mo->s[i].out_states; out++) {
      if (mo->s[i].out_id[out] == j)
        return 1;
    }
  }
  return 0;
# undef CUR_PROC
}   /* ghmm_dmodel_check_transition */

void ghmm_dmodel_set_transition (ghmm_dmodel * mo, int i, int j, double prob)
{
# define CUR_PROC "ghmm_dmodel_set_transition"
  int in, out;

  if (mo->s && mo->s[i].out_a && mo->s[j].in_a) {
    for (out = 0; out < mo->s[i].out_states; out++) {
      if (mo->s[i].out_id[out] == j) {
        mo->s[i].out_a[out] = prob;
        /* fprintf(stderr, CUR_PROC": State %d, %d, = %f\n", i, j,
                prob); */
        break;
      }
    }

    for (in = 0; in < mo->s[j].in_states; in++) {
      if (mo->s[j].in_id[in] == i) {
        mo->s[j].in_a[in] = prob;
        break;
      }
    }
  }
# undef CUR_PROC
}   /* ghmm_dmodel_set_transition */




/*============================================================================*/
/* Some outputs */
/*============================================================================*/

void ghmm_dmodel_states_print (FILE * file, ghmm_dmodel * mo)
{
  int i, j;
  fprintf (file, "Modelparameters: \n M = %d \t N = %d\n", mo->M, mo->N);
  for (i = 0; i < mo->N; i++) {
    fprintf (file,
             "\nState %d \n PI = %.3f \n out_states = %d \n in_states = %d \n",
             i, mo->s[i].pi, mo->s[i].out_states, mo->s[i].in_states);
    fprintf (file, " Output probability:\t");
    for (j = 0; j < mo->M; j++)
      fprintf (file, "%.3f \t", mo->s[i].b[j]);
    fprintf (file, "\n Transition probability \n");
    fprintf (file, "  Out states (Id, a):\t");
    for (j = 0; j < mo->s[i].out_states; j++)
      fprintf (file, "(%d, %.3f) \t", mo->s[i].out_id[j], mo->s[i].out_a[j]);
    fprintf (file, "\n");
    fprintf (file, "  In states (Id, a):\t");
    for (j = 0; j < mo->s[i].in_states; j++)
      fprintf (file, "(%d, %.3f) \t", mo->s[i].in_id[j], mo->s[i].in_a[j]);
    fprintf (file, "\n");
  }
}                               /* ghmm_dmodel_states_print */

/*============================================================================*/

void ghmm_dmodel_A_print (FILE * file, ghmm_dmodel * mo, char *tab, char *separator,
                    char *ending)
{
  int i, j, out_state;
  for (i = 0; i < mo->N; i++) {
    out_state = 0;
    fprintf (file, "%s", tab);
    if (mo->s[i].out_states > 0 && mo->s[i].out_id[out_state] == 0) {
      fprintf (file, "%.2f", mo->s[i].out_a[out_state]);
      out_state++;
    }
    else
      fprintf (file, "0.00");
    for (j = 1; j < mo->N; j++) {
      if (mo->s[i].out_states > out_state && mo->s[i].out_id[out_state] == j) {
        fprintf (file, "%s %.2f", separator, mo->s[i].out_a[out_state]);
        out_state++;
      }
      else
        fprintf (file, "%s 0.00", separator);
    }
    fprintf (file, "%s\n", ending);
  }
}                               /* ghmm_dmodel_A_print */

/*============================================================================*/

void ghmm_dmodel_B_print (FILE * file, ghmm_dmodel * mo, char *tab, char *separator,
                    char *ending)
{
  int i, j, size;

  for (i = 0; i < mo->N; i++) {
    fprintf (file, "%s", tab);
    fprintf (file, "%.2f", mo->s[i].b[0]);
    if (!(mo->model_type & GHMM_kHigherOrderEmissions)) {
      for (j = 1; j < mo->M; j++)
        fprintf (file, "%s %.2f", separator, mo->s[i].b[j]);
      fprintf (file, "%s\n", ending);
    }
    else {
      size = ghmm_ipow (mo, mo->M, mo->order[i] + 1);
      for (j = 1; j < size; j++)
        fprintf (file, "%s %.2f", separator, mo->s[i].b[j]);
      fprintf (file, "%s\n", ending);
    }
  }
}                               /* ghmm_dmodel_B_print */

/*============================================================================*/

void ghmm_dmodel_Pi_print (FILE * file, ghmm_dmodel * mo, char *tab, char *separator,
                     char *ending)
{
  int i;
  fprintf (file, "%s%.2f", tab, mo->s[0].pi);
  for (i = 1; i < mo->N; i++)
    fprintf (file, "%s %.2f", separator, mo->s[i].pi);
  fprintf (file, "%s\n", ending);
}                               /* ghmm_dmodel_Pi_print */

void ghmm_dmodel_fix_print (FILE * file, ghmm_dmodel * mo, char *tab, char *separator,
                      char *ending)
{
  int i;
  fprintf (file, "%s%d", tab, mo->s[0].fix);
  for (i = 1; i < mo->N; i++)
    fprintf (file, "%s %d", separator, mo->s[i].fix);
  fprintf (file, "%s\n", ending);
}                               /* ghmm_dmodel_Pi_print */

/*============================================================================*/

void ghmm_dmodel_A_print_transp (FILE * file, ghmm_dmodel * mo, char *tab,
                           char *separator, char *ending)
{
# define CUR_PROC "ghmm_dmodel_A_print_transp"
  int i, j;
  int *out_state;

  ARRAY_CALLOC (out_state, mo->N);
  for (i = 0; i < mo->N; i++)
    out_state[i] = 0;

  for (j = 0; j < mo->N; j++) {
    fprintf (file, "%s", tab);
    if (mo->s[0].out_states != 0 && mo->s[0].out_id[out_state[0]] == j) {
      fprintf (file, "%.2f", mo->s[0].out_a[out_state[0]]);
      (out_state[0])++;
    }
    else
      fprintf (file, "0.00");
    for (i = 1; i < mo->N; i++) {
      if (mo->s[i].out_states != 0 && mo->s[i].out_id[out_state[i]] == j) {
        fprintf (file, "%s %.2f", separator, mo->s[i].out_a[out_state[i]]);
        (out_state[i])++;
      }
      else
        fprintf (file, "%s 0.00", separator);
    }
    fprintf (file, "%s\n", ending);
  }
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  m_free (out_state);
  return;
# undef CUR_PROC
}                               /* ghmm_dmodel_A_print_transp */

/*============================================================================*/

void ghmm_dmodel_B_print_transp (FILE * file, ghmm_dmodel * mo, char *tab,
                           char *separator, char *ending)
{
  int i, j;
  for (j = 0; j < mo->M; j++) {
    fprintf (file, "%s", tab);
    fprintf (file, "%.2f", mo->s[0].b[j]);
    for (i = 1; i < mo->N; i++)
      fprintf (file, "%s %.2f", separator, mo->s[i].b[j]);
    fprintf (file, "%s\n", ending);
  }
}                               /* ghmm_dmodel_B_print_transp */

/*============================================================================*/

void ghmm_dmodel_Pi_print_transp (FILE * file, ghmm_dmodel * mo, char *tab, char *ending)
{
  int i;
  for (i = 0; i < mo->N; i++)
    fprintf (file, "%s%.2f%s\n", tab, mo->s[i].pi, ending);
}                               /* ghmm_dmodel_Pi_print_transp */

/*============================================================================*/

void ghmm_dl_print (FILE * file, ghmm_dmodel * mo, char *tab, char *separator,
                        char *ending)
{
  int i;
  fprintf (file, "%s%d", tab, mo->label[0]);
  for (i = 1; i < mo->N; i++)
    fprintf (file, "%s %d", separator, mo->label[i]);
  fprintf (file, "%s\n", ending);
}                               /* ghmm_dl_print */

/*============================================================================*/
void ghmm_dmodel_print (FILE * file, ghmm_dmodel * mo)
{
  fprintf (file, "HMM = {\n\tM = %d;\n\tN = %d;\n", mo->M, mo->N);
  fprintf (file, "\tprior = %.3f;\n", mo->prior);
  fprintf (file, "\tModelType = %d;\n", mo->model_type);
  fprintf (file, "\tA = matrix {\n");
  ghmm_dmodel_A_print (file, mo, "\t", ",", ";");
  fprintf (file, "\t};\n\tB = matrix {\n");
  ghmm_dmodel_B_print (file, mo, "\t", ",", ";");
  fprintf (file, "\t};\n\tPi = vector {\n");
  ghmm_dmodel_Pi_print (file, mo, "\t", ",", ";");
  fprintf (file, "\t};\n\tfix_state = vector {\n");
  ghmm_dmodel_fix_print (file, mo, "\t", ",", ";");
  if (mo->model_type & GHMM_kLabeledStates) {
    fprintf (file, "\t};\n\tlabel_state = vector {\n");
    ghmm_dl_print (file, mo, "\t", ",", ";");
  }
  fprintf (file, "\t};\n};\n\n");
}                               /* ghmm_dmodel_print */

/*============================================================================*/

#ifdef GHMM_OBSOLETE
void ghmm_dmodel_direct_print (FILE * file, model_direct * mo_d, int multip)
{
  int i, j;
  for (i = 0; i < multip; i++) {
    fprintf (file, "HMM = {\n\tM = %d;\n\tN = %d;\n", mo_d->M, mo_d->N);
    fprintf (file, "\tprior = %.3f;\n", mo_d->prior);
    fprintf (file, "\tA = matrix {\n");
    ighmm_cmatrix_print (file, mo_d->A, mo_d->N, mo_d->N, "\t", ",", ";");
    fprintf (file, "\t};\n\tB = matrix {\n");
    ighmm_cmatrix_print (file, mo_d->B, mo_d->N, mo_d->M, "\t", ",", ";");
    fprintf (file, "\t};\n\tPi = vector {\n");
    fprintf (file, "\t%.4f", mo_d->Pi[0]);
    for (j = 1; j < mo_d->N; j++)
      fprintf (file, ", %.4f", mo_d->Pi[j]);
    fprintf (file, ";\n\t};\n");
    fprintf (file, "\tfix_state = vector {\n");
    fprintf (file, "\t%d", mo_d->fix_state[0]);
    for (j = 1; j < mo_d->N; j++)
      fprintf (file, ", %d", mo_d->fix_state[j]);
    fprintf (file, ";\n\t};\n");
    fprintf (file, "};\n\n");
  }
}                               /* ghmm_dmodel_direct_print */

/*============================================================================*/

void ghmm_dmodel_direct_clean (model_direct * mo_d, hmm_check_t * check)
{
#define CUR_PROC "ghmm_dmodel_direct_clean"
  int i;
  if (!mo_d)
    return;
  mo_d->M = mo_d->N = 0;
  mo_d->prior = -1;
  if (mo_d->A) {
    for (i = 0; i < check->r_a; i++)
      m_free (mo_d->A[i]);
    m_free (mo_d->A);
  }
  if (mo_d->B) {
    for (i = 0; i < check->r_b; i++)
      m_free (mo_d->B[i]);
    m_free (mo_d->B);
  }
  if (mo_d->Pi){
    m_free (mo_d->Pi);
  }
  if (mo_d->fix_state){
    m_free (mo_d->fix_state);
  }
  
  mo_d->A = mo_d->B = NULL;
  mo_d->Pi = NULL;
  mo_d->fix_state = NULL;
#undef CUR_PROC
}                               /* ghmm_dmodel_direct_clean */

/*============================================================================*/

int ghmm_dmodel_direct_check_data (model_direct * mo_d, hmm_check_t * check)
{
#define CUR_PROC "ghmm_dmodel_direct_check_data"
  char *str;
  if (check->r_a != mo_d->N || check->c_a != mo_d->N) {
    str = ighmm_mprintf (NULL, 0, "Incompatible dim. A (%d X %d) and N (%d)\n",
                   check->r_a, check->c_a, mo_d->N);
    GHMM_LOG(LCONVERTED, str);
    m_free (str);
    return (-1);
  }
  if (check->r_b != mo_d->N || check->c_b != mo_d->M) {
    str = ighmm_mprintf (NULL, 0,
            "Incompatible dim. B (%d X %d) and N X M (%d X %d)\n",
            check->r_b, check->c_b, mo_d->N, mo_d->M);
    GHMM_LOG(LCONVERTED, str);
    m_free (str);
    return (-1);
  }
  if (check->len_pi != mo_d->N) {
    str = ighmm_mprintf (NULL, 0, "Incompatible dim. Pi (%d) and N (%d)\n",
                   check->len_pi, mo_d->N);
    GHMM_LOG(LCONVERTED, str);
    m_free (str);
    return (-1);
  }
  if (check->len_fix != mo_d->N) {
    str = ighmm_mprintf (NULL, 0, "Incompatible dim. fix_state (%d) and N (%d)\n",
                   check->len_fix, mo_d->N);
    GHMM_LOG(LCONVERTED, str);
    m_free (str);
    return (-1);
  }

  return 0;
#undef CUR_PROC
}                               /* ghmm_dmodel_direct_check_data */
#endif /* GHMM_OBSOLETE */


/*============================================================================*/
/* XXX symmetric not implemented yet */
double ghmm_dmodel_prob_distance (ghmm_dmodel * m0, ghmm_dmodel * m, int maxT, int symmetric,
                            int verbose)
{
#define CUR_PROC "ghmm_dmodel_prob_distance"

#define STEPS 40

  double p0, p;
  double d = 0.0;
  double *d1;
  ghmm_dseq *seq0 = NULL;
  ghmm_dseq *tmp = NULL;
  ghmm_dmodel *mo1, *mo2;
  int i, t, a, k;
  int true_len;
  int true_number;
  int left_to_right = 0;
  long total, index;
  int step_width = 0;
  int steps = 1;

  /* printf("***  model_prob_distance:\n"); */

  if (verbose) {                /* If we are doing it verbosely we want to have 40 steps */
    step_width = maxT / 40;
    steps = STEPS;
  }
  else                          /* else just one */
    step_width = maxT;

  ARRAY_CALLOC (d1, steps);

  mo1 = m0;
  mo2 = m;

  for (k = 0; k < 2; k++) {     /* Two passes for the symmetric case */

    /* seed = 0 -> no reseeding. Call  ghmm_rng_timeseed(RNG) externally */
    seq0 = ghmm_dmodel_generate_sequences (mo1, 0, maxT + 1, 1, maxT + 1);



    if (seq0 == NULL) {
      GHMM_LOG(LCONVERTED, " generate_sequences failed !");
      goto STOP;
    }

    if (seq0->seq_len[0] < maxT) {      /* There is an absorbing state */

      /* NOTA BENE: Assumpting the model delivers an explicit end state, 
         the condition of a fix initial state is removed. */

      /* For now check that Pi puts all weight on state */
      /*
         t = 0;
         for (i = 0; i < mo1->N; i++) {
         if (mo1->s[i].pi > 0.001)
         t++;
         }    
         if (t > 1) {
         GHMM_LOG(LCONVERTED, "ERROR: No proper left-to-right model. Multiple start states");
         goto STOP;
         } */

      left_to_right = 1;
      total = seq0->seq_len[0];

      while (total <= maxT) {

        /* create a additional sequences at once */
        a = (maxT - total) / (total / seq0->seq_number) + 1;
        /* printf("total=%d generating %d", total, a); */
        tmp = ghmm_dmodel_generate_sequences (mo1, 0, 0, a, a);
        if (tmp == NULL) {
          GHMM_LOG(LCONVERTED, " generate_sequences failed !");
          goto STOP;
        }
        ghmm_dseq_free (&tmp);
        ghmm_dseq_add (seq0, tmp);

        total = 0;
        for (i = 0; i < seq0->seq_number; i++)
          total += seq0->seq_len[i];
      }
    }

    if (left_to_right) {

      for (t = step_width, i = 0; t <= maxT; t += step_width, i++) {

        index = 0;
        total = seq0->seq_len[0];

        /* Determine how many sequences we need to get a total of t
           and adjust length of last sequence to obtain total of 
           exactly t */

        while (total < t) {
          index++;
          total += seq0->seq_len[index];
        }

        true_len = seq0->seq_len[index];
        true_number = seq0->seq_number;

        if ((total - t) > 0)
          seq0->seq_len[index] = total - t;
        seq0->seq_number = index;

        p0 = ghmm_dmodel_likelihood (mo1, seq0);
        if (p0 == +1 || p0 == -1) {     /* error! */
          GHMM_LOG(LCONVERTED, "problem: ghmm_dmodel_likelihood failed !");
          goto STOP;
        }
        p = ghmm_dmodel_likelihood (mo2, seq0);
        if (p == +1 || p == -1) {       /* what shall we do now? */
          GHMM_LOG(LCONVERTED, "problem: ghmm_dmodel_likelihood failed !");
          goto STOP;
        }

        d = 1.0 / t * (p0 - p);

        if (symmetric) {
          if (k == 0)
            /* save d */
            d1[i] = d;
          else {
            /* calculate d */
            d = 0.5 * (d1[i] + d);
          }
        }

        if (verbose && (!symmetric || k == 1))
          printf ("%d\t%f\t%f\t%f\n", t, p0, p, d);

        seq0->seq_len[index] = true_len;
        seq0->seq_number = true_number;
      }
    }

    else {

      true_len = seq0->seq_len[0];

      for (t = step_width, i = 0; t <= maxT; t += step_width, i++) {
        seq0->seq_len[0] = t;

        p0 = ghmm_dmodel_likelihood (mo1, seq0);
        /* printf("   P(O|m1) = %f\n",p0); */
        if (p0 == +1) {         /* error! */
          GHMM_LOG(LCONVERTED, "seq0 can't be build from mo1!");
          goto STOP;
        }
        p = ghmm_dmodel_likelihood (mo2, seq0);
        /* printf("   P(O|m2) = %f\n",p); */
        if (p == +1) {          /* what shall we do now? */
          GHMM_LOG(LCONVERTED, "problem: seq0 can't be build from mo2!");
          goto STOP;
        }

        d = (1.0 / t) * (p0 - p);

        if (symmetric) {
          if (k == 0)
            /* save d */
            d1[i] = d;
          else {
            /* calculate d */
            d = 0.5 * (d1[i] + d);
          }
        }

        if (verbose && (!symmetric || k == 1))
          printf ("%d\t%f\t%f\t%f\n", t, p0, p, d);

      }
      seq0->seq_len[0] = true_len;
    }

    if (symmetric) {
      ghmm_dseq_free (&seq0);
      mo1 = m;
      mo2 = m0;
    }
    else
      break;

  }                             /* k = 1,2 */

  ghmm_dseq_free (&seq0);
  m_free (d1);
  return d;

STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  ghmm_dseq_free (&seq0);
  m_free (d1);
  return (0.0);
#undef CUR_PROC
}


/*============================================================================*/

void ghmm_dstate_clean (ghmm_dstate * my_state)
{
#define CUR_PROC "ghmm_dstate_clean"

  mes_check_ptr (my_state, return;);

  if (my_state->b){
    m_free (my_state->b);
  }
  if (my_state->out_id){
    m_free (my_state->out_id);
  }
  if (my_state->in_id){
    m_free (my_state->in_id);
  }
  if (my_state->out_a){
    m_free (my_state->out_a);
  }
  if (my_state->in_a){
    m_free (my_state->in_a);
  }
  my_state->pi = 0;
  my_state->b = NULL;
  my_state->out_id = NULL;
  my_state->in_id = NULL;
  my_state->out_a = NULL;
  my_state->in_a = NULL;
  my_state->out_states = 0;
  my_state->in_states = 0;
  my_state->fix = 0;

#undef CUR_PROC
}                               /* ghmm_dstate_clean */



/*==========================  Labeled HMMs  ================================*/

ghmm_dseq *ghmm_dmodel_label_generate_sequences(
    ghmm_dmodel * mo, int seed, int global_len, long seq_number, int Tmax)
{
#define CUR_PROC "ghmm_dmodel_label_generate_sequences"

  ghmm_dseq *sq = NULL;
  int state;
  int j, m, j_id;
  double p, sum, max_sum;
  int len = global_len;
  int n = 0;
  int pos, label_pos;

  sq = ghmm_dseq_calloc(seq_number);
  if (!sq) {
    GHMM_LOG_QUEUED(LCONVERTED);
    goto STOP;
  }

  /* allocating additional fields for the state sequence in the ghmm_dseq struct */
  ARRAY_CALLOC(sq->states, seq_number);
  ARRAY_CALLOC(sq->states_len, seq_number);

  /* allocating additional fields for the labels in the ghmm_dseq struct */
  ARRAY_CALLOC(sq->state_labels, seq_number);
  ARRAY_CALLOC(sq->state_labels_len, seq_number);

  /* A specific length of the sequences isn't given. As a model should have
     an end state, the konstant MAX_SEQ_LEN is used. */
  if (len <= 0)
    len = (int)GHMM_MAX_SEQ_LEN;

  if (seed > 0) {
    GHMM_RNG_SET(RNG, seed);
  }

  /* initialize the emission history */
  mo->emission_history = 0;

  while (n < seq_number) {
    ARRAY_CALLOC(sq->seq[n], len);
    
    /* for silent models we have to allocate for the maximal possible number
       of lables and states */
    if (mo->model_type & GHMM_kSilentStates) {
      ARRAY_CALLOC(sq->states[n], len * mo->N);
      ARRAY_CALLOC(sq->state_labels[n], len * mo->N);
    }
     else {
      ARRAY_CALLOC(sq->states[n], len);
      ARRAY_CALLOC(sq->state_labels[n], len);
    }

    pos = label_pos = 0;
    
    /* Get a random initial state i */
    p = GHMM_RNG_UNIFORM(RNG);
    sum = 0.0;
    for (state=0; state < mo->N; state++) {
      sum += mo->s[state].pi;
      if (sum >= p)
        break;
    }

    while (pos < len) {
      /* save the state path and label */
      sq->states[n][label_pos] = state;
      sq->state_labels[n][label_pos] = mo->label[state];
      label_pos++;

      /* Get a random output m if the state is not a silent state */
      if (!(mo->model_type & GHMM_kSilentStates) || !(mo->silent[state])) {
        m = get_random_output(mo, state, pos);
        update_emission_history(mo, m);
        sq->seq[n][pos] = m;
        pos++;
      }

      /* get next state */
      p = GHMM_RNG_UNIFORM(RNG);
      if (pos < mo->maxorder) {
        max_sum = 0.0;
        for (j = 0; j < mo->s[state].out_states; j++) {
          j_id = mo->s[state].out_id[j];
          if (!(mo->model_type & GHMM_kHigherOrderEmissions) || mo->order[j_id] < pos)
            max_sum += mo->s[state].out_a[j];
        }
        if (j && fabs(max_sum) < GHMM_EPS_PREC) {
          GHMM_LOG_PRINTF(LERROR, LOC, "No possible transition from state %d "
                          "since all successor states require more history "
                          "than seen up to position: %d.",
                          state, pos);
          break;
        }
        if (j)
          p *= max_sum;
      }

      sum = 0.0;
      for (j = 0; j < mo->s[state].out_states; j++) {
        j_id = mo->s[state].out_id[j];
        if (!(mo->model_type & GHMM_kHigherOrderEmissions) || mo->order[j_id] < pos) {
          sum += mo->s[state].out_a[j];
          if (sum >= p)
            break;
        }
      }

      if (sum == 0.0) {
        GHMM_LOG_PRINTF(LINFO, LOC, "final state (%d) reached at position %d "
                        "of sequence %d", state, pos, n);
	break;
      }

      state = j_id;
    }                           /* while (pos < len) */

    /* realocate state path and label sequence to actual size */ 
    if (mo->model_type & GHMM_kSilentStates) {
      ARRAY_REALLOC(sq->states[n], label_pos);
      ARRAY_REALLOC(sq->state_labels[n], label_pos);
    }

    sq->seq_len[n] = pos;
    sq->states_len[n] = label_pos;
    sq->state_labels_len[n] = label_pos;
    n++;
  }                             /* while( n < seq_number ) */

  return (sq);
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  ghmm_dseq_free(&sq);
  return NULL;
#undef CUR_PROC
}


/*----------------------------------------------------------------------------*/
/* Scales the output and transitions probs of all states in a given model */
int ghmm_dmodel_normalize (ghmm_dmodel * mo)
{
#define CUR_PROC "ghmm_dmodel_normalize"
  double pi_sum=0.0;
  int i, j, m, j_id, i_id=0, res=0;
  int size = 1;

  for (i = 0; i < mo->N; i++) {
    if (mo->s[i].pi >= 0.0)
      pi_sum += mo->s[i].pi;
    else
      mo->s[i].pi = 0.0;

    /* check model_type before using state order */
    if (mo->model_type & GHMM_kHigherOrderEmissions)
      size = ghmm_ipow (mo, mo->M, mo->order[i]);

    /* normalize transition probabilities */
    if (ighmm_cvector_normalize (mo->s[i].out_a, mo->s[i].out_states) == -1) {
      res = -1;
    }
    /* for every outgoing probability update the corrosponding incoming probability */
    for (j = 0; j < mo->s[i].out_states; j++) {
      j_id = mo->s[i].out_id[j];
      for (m = 0; m < mo->s[j_id].in_states; m++) {
        if (i == mo->s[j_id].in_id[m]) {
          i_id = m;
          break;
        }
      }
      if (i_id == mo->s[j_id].in_states) {
        GHMM_LOG_PRINTF(LERROR, LOC, "Outgoing transition from state %d to \
           state %d has no corresponding incoming transition.", i, j_id);
        return -1;
      }
      mo->s[j_id].in_a[i_id] = mo->s[i].out_a[j];
    }
    /* normalize emission probabilities, but not for silent states */
    if (!((mo->model_type & GHMM_kSilentStates) && mo->silent[i])) {
      for (m = 0; m < size; m++) {
        if (ighmm_cvector_normalize (&(mo->s[i].b[m * mo->M]), mo->M) == -1)
          res = -1;
      }
    }
  }
  for (i = 0; i < mo->N; i++)
    mo->s[i].pi /= pi_sum;

  return res;
#undef CUR_PROC
}                               /* ghmm_dmodel_normalize */


/*----------------------------------------------------------------------------*/
int ghmm_dmodel_add_noise (ghmm_dmodel * mo, double level, int seed)
{
#define CUR_PROC "model_add_noise_A"

  int h, i, j, hist;
  int size = 1;

  if (level > 1.0)
    level = 1.0;

  for (i = 0; i < mo->N; i++) {
    for (j = 0; j < mo->s[i].out_states; j++)
      /* add noise only to out_a, in_a is updated on normalisation */
      mo->s[i].out_a[j] *= (1 - level) + (GHMM_RNG_UNIFORM (RNG) * 2 * level);

    if (mo->model_type & GHMM_kHigherOrderEmissions)
      size = ghmm_ipow (mo, mo->M, mo->order[i]);
    for (hist = 0; hist < size; hist++)
      for (h = hist * mo->M; h < hist * mo->M + mo->M; h++)
        mo->s[i].b[h] *= (1 - level) + (GHMM_RNG_UNIFORM (RNG) * 2 * level);
  }

  return ghmm_dmodel_normalize (mo);

#undef CUR_PROC
}

/*----------------------------------------------------------------------------*/
static int ghmm_dstate_transition_add(ghmm_dstate * s, int start, int dest, double prob)
{
#define CUR_PROC "ghmm_dmodel_transition_add"

  int i;

  /* resize the arrays */
  ARRAY_REALLOC (s[dest].in_id, s[dest].in_states + 1);
  ARRAY_REALLOC (s[dest].in_a, s[dest].in_states + 1);
  ARRAY_REALLOC (s[start].out_id, s[start].out_states + 1);
  ARRAY_REALLOC (s[start].out_a, s[start].out_states + 1);

  s[dest].in_states += 1;
  s[start].out_states += 1;

  /* search the right place to insert while moving greater entrys one field back */
  for (i = s[start].out_states - 1; i >= 0; i--) {
    if (i == 0 || dest > s[start].out_id[i - 1]) {
      s[start].out_id[i] = dest;
      s[start].out_a[i] = prob;
      break;
    }
    else {
      s[start].out_id[i] = s[start].out_id[i - 1];
      s[start].out_a[i] = s[start].out_a[i - 1];
    }
  }

  /* search the right place to insert while moving greater entrys one field back */
  for (i = s[dest].in_states - 1; i >= 0; i--)
    if (i == 0 || start > s[dest].in_id[i - 1]) {
      s[dest].in_id[i] = start;
      s[dest].in_a[i] = prob;
      break;
    }
    else {
      s[dest].in_id[i] = s[dest].in_id[i - 1];
      s[dest].in_a[i] = s[dest].in_a[i - 1];
    }

  return 0;
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  return -1;
#undef CUR_PROC
}

/*----------------------------------------------------------------------------*/
static int ghmm_dstate_transition_del(ghmm_dstate * s, int start, int dest)
{
#define CUR_PROC "ghmm_dmodel_transition_del"

  int i, j;

  /* search ... */
  for (j = 0; dest != s[start].out_id[j]; j++)
    if (j == s[start].out_states) {
      GHMM_LOG(LCONVERTED, "No such transition");
      return -1;
    }
  /* ... and replace outgoing */
  for (i = j + 1; i < s[start].out_states; i++) {
    s[start].out_id[i - 1] = s[start].out_id[i];
    s[start].out_a[i - 1] = s[start].out_a[i];
  }

  /* search ... */
  for (j = 0; start != s[dest].in_id[j]; j++)
    if (j == s[dest].in_states) {
      GHMM_LOG(LCONVERTED, "No such transition");
      return -1;
    }
  /* ... and replace incoming */
  for (i = j + 1; i < s[dest].in_states; i++) {
    s[dest].in_id[i - 1] = s[dest].in_id[i];
    s[dest].in_a[i - 1] = s[dest].in_a[i];
  }

  /* reset number */
  s[dest].in_states -= 1;
  s[start].out_states -= 1;

  /* free memory */
  ARRAY_REALLOC (s[dest].in_id, s[dest].in_states);
  ARRAY_REALLOC (s[dest].in_a, s[dest].in_states);
  ARRAY_REALLOC (s[start].out_id, s[start].out_states);
  ARRAY_REALLOC (s[start].out_a, s[start].out_states);

  return 0;
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  return -1;
#undef CUR_PROC
}

/*----------------------------------------------------------------------------*/
/** 
   Allocates a new ghmm_dbackground struct and assigs the arguments to
   the respective fields. Note: The arguments need allocation outside of this
   function.
   
   @return     :               0 on success, -1 on error
   @param mo   :               one model
   @param cur  :               a id of a state
   @param times:               number of times the state cur is at least evaluated
*/
int ghmm_dmodel_duration_apply (ghmm_dmodel * mo, int cur, int times)
{
#define CUR_PROC "ghmm_dmodel_duration_apply"

  int i, j, last, size, failed=0;

  if (mo->model_type & GHMM_kSilentStates) {
    GHMM_LOG(LCONVERTED, "Sorry, apply_duration doesn't support silent states yet\n");
    return -1;
  }

  last = mo->N;
  mo->N += times - 1;

  ARRAY_REALLOC (mo->s, mo->N);
  if (mo->model_type & GHMM_kSilentStates) {
    ARRAY_REALLOC (mo->silent, mo->N);
    ARRAY_REALLOC (mo->topo_order, mo->N);
  }
  if (mo->model_type & GHMM_kTiedEmissions)
    ARRAY_REALLOC (mo->tied_to, mo->N);
  if (mo->model_type & GHMM_kBackgroundDistributions)
    ARRAY_REALLOC (mo->background_id, mo->N);

  size = ghmm_ipow (mo, mo->M, mo->order[cur] + 1);
  for (i = last; i < mo->N; i++) {
    /* set the new state */
    mo->s[i].pi = 0.0;
    mo->order[i] = mo->order[cur];
    mo->s[i].fix = mo->s[cur].fix;
    mo->label[i] = mo->label[cur];
    mo->s[i].in_a = NULL;
    mo->s[i].in_id = NULL;
    mo->s[i].in_states = 0;
    mo->s[i].out_a = NULL;
    mo->s[i].out_id = NULL;
    mo->s[i].out_states = 0;

    ARRAY_MALLOC (mo->s[i].b, size);
    for (j = 0; j < size; j++)
      mo->s[i].b[j] = mo->s[cur].b[j];

    if (mo->model_type & GHMM_kSilentStates) {
      mo->silent[i] = mo->silent[cur];
      /* XXX what to do with topo_order
         mo->topo_order[i] = ????????????; */
    }
    if (mo->model_type & GHMM_kTiedEmissions)
      /* XXX is there a clean solution for tied states?
         what if the current state is a tie group leader?
         the last added state should probably become
         the new tie group leader */
      mo->tied_to[i] = GHMM_kUntied;
    if (mo->model_type & GHMM_kBackgroundDistributions)
      mo->background_id[i] = mo->background_id[cur];
  }

  /* move the outgoing transitions to the last state */
  while (mo->s[cur].out_states > 0) {
    if (mo->s[cur].out_id[0] == cur) {
      ghmm_dstate_transition_add (mo->s, mo->N - 1, mo->N - 1, mo->s[cur].out_a[0]);
      ghmm_dstate_transition_del (mo->s, cur, mo->s[cur].out_id[0]);
    }
    else {
      ghmm_dstate_transition_add (mo->s, mo->N - 1, mo->s[cur].out_id[0],
                            mo->s[cur].out_a[0]);
      ghmm_dstate_transition_del (mo->s, cur, mo->s[cur].out_id[0]);
    }
  }

  /* set the linear transitions through all added states */
  ghmm_dstate_transition_add (mo->s, cur, last, 1.0);
  for (i = last + 1; i < mo->N; i++) {
    ghmm_dstate_transition_add (mo->s, i - 1, i, 1.0);
  }

  if (ghmm_dmodel_normalize (mo))
    goto STOP;

  return 0;
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  /* Fail hard if these realloc fail. They shouldn't because we have the memory
     and try only to clean up! */
  if (failed++)
    exit (1);
  
  ARRAY_REALLOC (mo->s, last);
  ARRAY_REALLOC (mo->tied_to, last);
  ARRAY_REALLOC (mo->background_id, last);

  mo->N = last;
  return -1;
#undef CUR_PROC
}

/*----------------------------------------------------------------------------*/
ghmm_dbackground *ghmm_dbackground_alloc (int n, int m, int *orders, double **B) {
#define CUR_PROC "ghmm_dbackground_alloc"
  ghmm_dbackground *ptbackground;

  ARRAY_CALLOC(ptbackground, 1);

  //initialize name
  ARRAY_CALLOC(ptbackground->name, n);
  int i;
  for(i = 0; i < n; i++){
    ptbackground->name[i] = NULL;
  }

  ptbackground->n = n;
  ptbackground->m = m;
  if (orders)
    ptbackground->order = orders;
  if (B)
    ptbackground->b = B;

  return ptbackground;
STOP:     /* Label STOP from ARRAY_[CM]ALLOC */
  return NULL;
#undef CUR_PROC
}

/*----------------------------------------------------------------------------*/
ghmm_dbackground *ghmm_dbackground_copy(ghmm_dbackground * bg)
{
#define CUR_PROC "ghmm_dbackground_copy"
  int i, j, b_i_len;
  int *new_order;
  double **new_b;

  ARRAY_MALLOC (new_order, bg->n);
  ARRAY_CALLOC (new_b, bg->n);

  for (i = 0; i < bg->n; i++) {
    new_order[i] = bg->order[i];
    b_i_len = pow (bg->m, bg->order[i] + 1);
    ARRAY_CALLOC (new_b[i], b_i_len);
    for (j = 0; j < b_i_len; j++) {
      new_b[i][j] = bg->b[i][j];
    }
  }
  
  ghmm_dbackground *tmp = ghmm_dbackground_alloc (bg->n, bg->m, new_order,
                                               new_b);
  
  for(i = 0; i < bg->n; i++){
    if(bg->name[i]) strcpy(tmp->name[i], bg->name[i]);
  }
  return tmp;

STOP:     /* Label STOP from ARRAY_[CM]ALLOC */

  return NULL;

#undef CUR_PROC
}


/*----------------------------------------------------------------------------*/
int ghmm_dmodel_background_apply (ghmm_dmodel * mo, double *background_weight)
{
# define CUR_PROC "ghmm_dmodel_apply_background"

  int i, j, size;

  if (!(mo->model_type & GHMM_kBackgroundDistributions)) {
    GHMM_LOG(LCONVERTED, "Error: No background distributions");
    return -1;
  }

  for (i=0; i<mo->N; i++) {
    if (mo->background_id[i] != GHMM_kNoBackgroundDistribution) {
      if (mo->model_type & GHMM_kHigherOrderEmissions) {
	if (mo->order[i] != mo->bp->order[mo->background_id[i]]) {
	  GHMM_LOG_PRINTF(LERROR, LOC, "State (%d) and background order (%d) "
			       "do not match in state %d. Background_id = %d",
			       mo->order[i],
			       mo->bp->order[mo->background_id[i]], i,
			       mo->background_id[i]);
	  return -1;
	}
	/* XXX Cache in ghmm_dbackground */
	size = ghmm_ipow(mo, mo->M, mo->order[i]+1);
	for (j=0; j<size; j++)
	  mo->s[i].b[j] = (1.0 - background_weight[i]) * mo->s[i].b[j]
	    + background_weight[i] * mo->bp->b[mo->background_id[i]][j];
      } else {
	if (mo->bp->order[mo->background_id[i]] != 0) {
	  GHMM_LOG(LCONVERTED, "Error: State and background order do not match\n");
	  return -1;
	}
	for (j=0; j<mo->M; j++)
	  mo->s[i].b[j] = (1.0 - background_weight[i]) * mo->s[i].b[j]
	    + background_weight[i] * mo->bp->b[mo->background_id[i]][j];
      }
    }
  }

  return 0;
#undef CUR_PROC
}                               /* ghmm_dmodel_apply_background */


/*----------------------------------------------------------------------------*/
int ghmm_dmodel_get_uniform_background (ghmm_dmodel * mo, ghmm_dseq * sq)
{
# define CUR_PROC "get_background"

  int h, i, j, m, t, n=0;
  int e_index, size;
  double sum=0.0;

  if (!(mo->model_type & GHMM_kBackgroundDistributions)) {
    GHMM_LOG(LCONVERTED, "Error: Model has no background distribution");
    return -1;
  }

  mo->bp = NULL;
  ARRAY_MALLOC (mo->background_id, mo->N);

  /* create a background distribution for each state */
  for (i = 0; i < mo->N; i++) {
    mo->background_id[i] = mo->order[i];
  }

  /* allocate */
  ARRAY_CALLOC (mo->bp, 1);
  ARRAY_CALLOC (mo->bp->order, mo->maxorder);

  /* set number of distributions */
  mo->bp->n = mo->maxorder;

  /* set br->order */
  for (i = 0; i < mo->N; i++)
    if (mo->background_id[i] != GHMM_kNoBackgroundDistribution)
      mo->bp->order[mo->background_id[i]] = mo->order[i];

  /* allocate and initialize br->b with zeros */
  ARRAY_CALLOC (mo->bp->b, mo->bp->n);

  for (i = 0; i < mo->bp->n; i++)
    ARRAY_MALLOC (mo->bp->b[i], ghmm_ipow (mo, mo->M, mo->bp->order[i] + 1));

  for (i = 0; i < mo->bp->n; i++) {

    /* find a state with the current order */
    for (j = 0; j < mo->N; j++)
      if (mo->bp->order[i] == mo->order[j])
        break;

    /* initialize with ones as psoudocounts */
    size = ghmm_ipow (mo, mo->M, mo->bp->order[n] + 1);
    for (m = 0; m < size; m++)
      mo->bp->b[i][m] = 1.0;

    for (n = 0; n < sq->seq_number; n++) {

      for (t = 0; t < mo->bp->order[i]; t++)
        update_emission_history (mo, sq->seq[n][t]);

      for (t = mo->bp->order[i]; t < sq->seq_len[n]; t++) {

        e_index = get_emission_index (mo, j, sq->seq[n][t], t);
        if (-1 != e_index)
          mo->bp->b[i][e_index]++;
      }
    }

    /* normalise */
    size = ghmm_ipow (mo, mo->M, mo->bp->order[n]);
    for (h = 0; h < size; h += mo->M) {
      for (m = h; m < h + mo->M; m++)
        sum += mo->bp->b[i][m];
      for (m = h; m < h + mo->M; m++)
        mo->bp->b[i][m] /= sum;
    }

  }

  return 0;

STOP:     /* Label STOP from ARRAY_[CM]ALLOC */


  return -1;
# undef CUR_PROC
}                               /* end get_background */


/*============================================================================*/
double ghmm_dmodel_distance(const ghmm_dmodel * mo, const ghmm_dmodel * m2) {
#define CUR_PROC "model_distances"

  int i, j, number=0;
  double tmp, distance=0.0;

/*   if (!ghmm_dmodel_check_compatibility(mo, m2)) */
/*     exit(1); */
/*   if (!ghmm_dmodel_check(mo)) */
/*     exit(1); */
/*   if (!ghmm_dmodel_check(m2)) */
/*     exit(1); */


  /* PI */
  for (i=0; i<mo->N; ++i) {
    tmp = mo->s[i].pi - m2->s[i].pi;
    distance += tmp*tmp;
    ++number;
  }
  for (i=0; i<mo->N; ++i) {
    /* A */
    for (j=0; j<mo->s[i].out_states; ++j) {
      tmp = mo->s[i].out_a[j] - m2->s[i].out_a[j];
      distance += tmp*tmp;
      ++number;
    }
    /* B */
    for (j=0; j<ghmm_ipow(mo, mo->M, mo->order[i]+1); ++j) {
      tmp = mo->s[i].b[j] - m2->s[i].b[j];
      distance += tmp*tmp;
      ++number;
    } 
  }

  return (distance/number);
#undef CUR_PROC
}

/*============================================================================*/
ghmm_dmodel** ghmm_dmodel_xml_read(const char* filename, int* mo_number) {
#define CUR_PROC "ghmm_dmodel_xml_read"
  ghmm_xmlfile* f;
  ghmm_dmodel** mo;

  f = ghmm_xmlfile_parse(filename);
  if (!f && (f->modelType & GHMM_kDiscreteHMM)
      && !(f->modelType & (GHMM_kPairHMM+GHMM_kTransitionClasses))) {
    GHMM_LOG(LERROR, "wrong model type, model in file is not a plain discrete model");
    goto STOP;
  }
  *mo_number = f->noModels;
  mo = f->model.d;

  free(f); /* XXX - by now, we free f */  
  return mo;
STOP:  
  return NULL;
#undef CUR_PROC
}


/*============================================================================*/
int ghmm_dmodel_xml_write(ghmm_dmodel** mo, const char *file, int mo_number) {
#define CUR_PROC "ghmm_dmodel_xml_write"

  ghmm_xmlfile* f;

  ARRAY_MALLOC(f,1);
  f->noModels = mo_number;
  f->modelType = mo[0]->model_type;
  f->model.d = mo;
  ghmm_xmlfile_write(f, file);
  free(f);  
  return 0;
STOP:  
  return -1;
#undef CUR_PROC
}


/*===================== E n d   o f  f i l e  "model.c"       ===============*/
