static char rcsid[] = "$Id: 5083399dfe38058bb73e62d0396ab8eb64c9c8e9 $";
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifndef HAVE_MEMCPY
#define memcpy(d,s,n) bcopy((s),(d),(n))
#endif

#include "pathpair-eval.h"
#include "path-eval.h"		/* For Path_mark_alignment */

#include "path-solve.h"
#include "path-fusion.h"
#include "path-trim.h"

#include <stdio.h>
#include <math.h>		/* For rint */
#include "fastlog.h"		/* For fasterexp */

#include "assert.h"
#include "list.h"
#include "genomebits_count.h"
#include "junction.h"
#include "mapq.h"
#include "outputtype.h"

static Transcriptome_T transcriptome;

static bool *circularp;
static bool *chrsubsetp;
static bool *altlocp;

static Outputtype_T output_type;
static bool splicingp;
static bool resolve_inner_p;
static bool want_random_p;


#define OUTERLENGTH_FACTOR 1.5


/* Pathpair_eval_and_sort */
#ifdef DEBUG8
#define debug8(x) x
#else
#define debug8(x)
#endif

#define T Path_T

/* For DNA-seq reads, do not want to use insertlength or outerlength */
/* Duplicates with respect to method have already been taken care of */
/* Ignore sensedir, so we keep both sensedirs if they have equivalent matches */
static int
Pathpair_method_cmp (const void *x, const void *y) {
  Pathpair_T a = * (Pathpair_T *) x;
  Pathpair_T b = * (Pathpair_T *) y;

  int method_a, method_b;

  method_a = (int) a->pathL->method + (int) a->pathH->method;
  method_b = (int) b->pathL->method + (int) b->pathH->method;

  if (method_a > method_b) {
    return -1;
  } else if (method_b > method_a) {
    return +1;
  } else {
    return 0;
  }
}


static int
Pathpair_local_cmp (const void *x, const void *y) {
  Pathpair_T a = * (Pathpair_T *) x;
  Pathpair_T b = * (Pathpair_T *) y;

  int nmatches_a, nmatches_b;
#if 0
  /* We want to keep all possible transcript results at a locus */
  double junction_splice_prob_a, junction_splice_prob_b,
    total_splice_prob_a, total_splice_prob_b;;
#endif
  bool insertlength_knownp_x, insertlength_knownp_y,
    outerlength_knownp_x, outerlength_knownp_y;

  nmatches_a = a->pathL->nmatches + a->pathH->nmatches;
  nmatches_b = b->pathL->nmatches + b->pathH->nmatches;
#if 0
  junction_splice_prob_a = a->pathL->junction_splice_prob + a->pathH->junction_splice_prob;
  junction_splice_prob_b = b->pathL->junction_splice_prob + b->pathH->junction_splice_prob;
  total_splice_prob_a = a->pathL->total_splice_prob + a->pathH->total_splice_prob;
  total_splice_prob_b = b->pathL->total_splice_prob + b->pathH->total_splice_prob;
#endif
    
  if (a->transcript_concordant_p > b->transcript_concordant_p) {
    return -1;
  } else if (b->transcript_concordant_p > a->transcript_concordant_p) {
    return +1;
  } else if (nmatches_a > nmatches_b) {
    return -1;
  } else if (nmatches_b > nmatches_a) {
    return +1;

#if 0
  /* We want to keep all possible transcript results at a locus */
  } else if (junction_splice_prob_a > junction_splice_prob_b) {
    return -1;
  } else if (junction_splice_prob_b > junction_splice_prob_a) {
    return +1;
  } else if (total_splice_prob_a > total_splice_prob_b) {
    return -1;
  } else if (total_splice_prob_b > total_splice_prob_a) {
    return +1;
#endif

  } else {
    insertlength_knownp_x = Pathpair_insertlength_knownp(a);
    insertlength_knownp_y = Pathpair_insertlength_knownp(b);

    if (insertlength_knownp_x == false) {
      /* Fall through */
    } else if (insertlength_knownp_y == false) {
      /* Fall through */
    } else if (a->insertlength < b->insertlength) {
      return -1;
    } else if (b->insertlength < a->insertlength) {
      return +1;
    }

    outerlength_knownp_x = Pathpair_outerlength_knownp(a);
    outerlength_knownp_y = Pathpair_outerlength_knownp(b);
    if (outerlength_knownp_x == false) {
      /* Fall through */
    } else if (outerlength_knownp_y == false) {
      /* Fall through */
    } else if (a->outerlength * OUTERLENGTH_FACTOR < b->outerlength) {
      return -1;
    } else if (b->outerlength * OUTERLENGTH_FACTOR < a->outerlength) {
      return +1;
    }

    return 0;
  }
}


static int
Pathpair_global_cmp (const void *x, const void *y) {
  Pathpair_T a = * (Pathpair_T *) x;
  Pathpair_T b = * (Pathpair_T *) y;

  int nmatches_x = a->pathL->nmatches + a->pathH->nmatches;
  int nmatches_y = b->pathL->nmatches + b->pathH->nmatches;
  bool fusionp_x = false, fusionp_y = false;
  /* bool insertlength_knownp_x, insertlength_knownp_y; */

  if (a->pathL->fusion_querystart_junction != NULL ||
      a->pathL->fusion_queryend_junction != NULL ||
      a->pathH->fusion_querystart_junction != NULL ||
      a->pathH->fusion_queryend_junction != NULL) {
    fusionp_x = true;
  }
  if (b->pathL->fusion_querystart_junction != NULL ||
      b->pathL->fusion_queryend_junction != NULL ||
      b->pathH->fusion_querystart_junction != NULL ||
      b->pathH->fusion_queryend_junction != NULL) {
    fusionp_y = true;
  }

  if (fusionp_x == true && fusionp_y == false) {
    return -1;
  } else if (fusionp_y == true && fusionp_x == false) {
    return +1;

  } else if (a->transcript_concordant_p > b->transcript_concordant_p) {
    return -1;
  } else if (b->transcript_concordant_p > a->transcript_concordant_p) {
    return +1;
  } else if (nmatches_x > nmatches_y) {
    return -1;
  } else if (nmatches_y > nmatches_x) {
    return +1;
  } else if (splicingp == false) {
    return 0;

  } else {
#if 0
    /* We want to keep all possible transcript results across loci */
    insertlength_knownp_x = Pathpair_insertlength_knownp(a);
    insertlength_knownp_y = Pathpair_insertlength_knownp(b);
    if (insertlength_knownp_x == false) {
      return 0;
    } else if (insertlength_knownp_y == false) {
      return 0;
    } else if (a->insertlength < b->insertlength) {
      return -1;
    } else if (b->insertlength < a->insertlength) {
      return +1;
    } else {
      return 0;
    }
#else
    return 0;
#endif
  }
}


Pathpair_T *
Pathpair_eval_and_sort (int *found_score_5, int *found_score_3,
			int *npaths_primary, int *npaths_altloc, int *first_absmq, int *second_absmq,
			Pathpair_T *pathpairarray, int npaths, Stage1_T stage1_5, Stage1_T stage1_3,
			Compress_T query5_compress_fwd, Compress_T query5_compress_rev,
			Compress_T query3_compress_fwd, Compress_T query3_compress_rev,
			Shortread_T queryseq5, Shortread_T queryseq3,
			char *queryuc_ptr_5, char *queryrc5, char *queryuc_ptr_3, char *queryrc3, 
			char *quality_string_5, char *quality_string_3,
			Univcoord_T *novel_diagonals_alloc, unsigned short *localdb_alloc,
			Knownsplicing_T knownsplicing,

			int nmismatches_allowed_5, int nmismatches_allowed_3,
			int nmismatches_filter_5, int nmismatches_filter_3,
			int mincoverage_filter_5, int mincoverage_filter_3,
			int max_insertionlen_5, int max_insertionlen_3,
			int max_deletionlen_5, int max_deletionlen_3,
			int querylength5, int querylength3,
			Univdiagpool_T univdiagpool, Intlistpool_T intlistpool,
			Uintlistpool_T uintlistpool, Univcoordlistpool_T univcoordlistpool,
			Listpool_T listpool, Pathpool_T pathpool, Transcriptpool_T transcriptpool,
			Vectorpool_T vectorpool, Hitlistpool_T hitlistpool) {
  int pathi;
  float maxlik, loglik;

  float total, qual;
  int mapq_score;

  int randomi, i, j, k, l;
  Pathpair_T temp, pathpair;
  Path_T path5, path3;
  bool possible_fusion5_p = false, possible_fusion3_p = false;

  List_T paths, fusion_paths_5, fusion_paths_3, p, q;
  

  if (npaths == 0) {
    /* Skip */
    *npaths_primary = *npaths_altloc = 0;
    *first_absmq = 0;
    *second_absmq = 0;

  } else {
    /* Have already called Path_extend */

    /* 0.  Sort by structure to remove duplicates */
    if (npaths > 1) {
      qsort(pathpairarray,npaths,sizeof(T),Pathpair_structure_cmp);
    
      k = 0;
      i = 0;
      while (i < npaths) {
	j = i + 1;
	while (j < npaths && Pathpair_structure_cmp(&(pathpairarray[j]),&(pathpairarray[i])) == 0) {
	  j++;
	}
	debug8(printf("Found an identical group by structure (except sensedir) of %d paths => Re-sorting by method_cmp\n",j - i));

	qsort(&(pathpairarray[i]),j - i,sizeof(Pathpair_T),Pathpair_method_cmp);
	debug8(printf("(0) Keeping by method_cmp\n")); debug8(Path_print(pathpairarray[i]->pathL)); debug8(Path_print(pathpairarray[i]->pathH));
	pathpairarray[k++] = pathpairarray[i];

	for (l = i + 1; l < j; l++) {
	  debug8(printf("(0) Eliminating by method_cmp\n")); debug8(Path_print(pathpairarray[l]->pathL)); debug8(Path_print(pathpairarray[l]->pathH));
	  pathpair = pathpairarray[l];
	  Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool);
	}

	i = j;
      }
      npaths = k;
    }
    debug8(printf("After removing duplicates (except for sensedir), have %d paths\n",npaths));


    /* 1.  Unalias circular alignments */
    for (i = 0; i < npaths; i++) {
      pathpair = pathpairarray[i];
      path5 = pathpair->path5;
      path3 = pathpair->path3;
      if (circularp[pathpair->pathL->chrnum] == true) {
	Path_trim_circular_unalias_pair(pathpair->pathL,pathpair->pathH);
      }
    }


    /* 2.  Sort by intervals to resolve inner splices */
    qsort(pathpairarray,npaths,sizeof(Pathpair_T),Pathpair_interval_cmp);

    if (resolve_inner_p == true) {
      k = 0;
      i = 0;
      while (i < npaths) {
	j = i + 1;
	while (j < npaths && Pathpair_overlap_p(pathpairarray[j],pathpairarray[i]) == true) {
	  j++;
	}
	debug8(printf("Found an overlapping group of %d => re-sorting by Pathpair_local_cmp\n",j - i));
	
	/* Resolve inner parts */
	for (l = i; l < j; l++) {
	  pathpair = pathpairarray[l];
	  path5 = pathpair->path5;
	  path3 = pathpair->path3;
	  
	  if (pathpair->plusp == true) {
	    if (Path_resolved_qend_p(path5) == true && Path_resolved_qstart_p(path3) == true) {
	      debug8(printf("Have a resolved inner: ")); debug8(Path_print(path5)); debug8(Path_print(path3));
	    } else {
	      Pathpair_resolve(&(*found_score_5),&(*found_score_3),
			       pathpair,pathpair->plusp,/*genestrand*/0,
			       query5_compress_fwd,query5_compress_fwd,query5_compress_rev,
			       query3_compress_fwd,query3_compress_fwd,query3_compress_rev,
			       /*queryseqL*/queryseq5,/*queryseqH*/queryseq3,queryuc_ptr_5,queryuc_ptr_3,
			       novel_diagonals_alloc,localdb_alloc,stage1_5,stage1_3,knownsplicing,
			       nmismatches_allowed_5,nmismatches_allowed_3,
			       max_insertionlen_5,max_insertionlen_3,max_deletionlen_5,max_deletionlen_3,
			       intlistpool,univcoordlistpool,listpool,pathpool);
	    }
	    
	  } else {
	    if (Path_resolved_qend_p(path3) == true && Path_resolved_qstart_p(path5) == true) {
	      debug8(printf("Have a resolved inner: ")); debug8(Path_print(path5)); debug8(Path_print(path3));
	    } else {
	      Pathpair_resolve(&(*found_score_5),&(*found_score_3),
			       pathpair,pathpair->plusp,/*genestrand*/0,
			       query3_compress_rev,query3_compress_fwd,query3_compress_rev,
			       query5_compress_rev,query5_compress_fwd,query5_compress_rev,
			       /*queryseqL*/queryseq3,/*queryseqH*/queryseq5,queryrc3,queryrc5,
			       novel_diagonals_alloc,localdb_alloc,stage1_3,stage1_5,knownsplicing,
			       nmismatches_allowed_3,nmismatches_allowed_5,
			       max_insertionlen_3,max_insertionlen_5,max_deletionlen_3,max_deletionlen_5,
			       intlistpool,univcoordlistpool,listpool,pathpool);
	    }
	  }
	}
	
	i = j;
      }
    }


    /* Note: Don't maximize on nmatches until we find fusions, because a fusion splice may need to truncate nmatches */

    /* 3.  Check all solutions for a possible outer fusion */
    if (splicingp == true) {
      /* 3a.  Look for possibilities */
      for (i = 0; i < npaths; i++) {
	pathpair = pathpairarray[i];
	if (pathpair->plusp == true) {
	  if (Path_unextended_qstart_p(pathpair->path5,/*endtrim_allowed*/25,/*allow_ambig_p*/false) == true) {
	    possible_fusion5_p = true;
	  }
	  if (Path_unextended_qend_p(pathpair->path3,/*endtrim_allowed*/25,/*allow_ambig_p*/false) == true) {
	    possible_fusion3_p = true;
	  }
	} else {
	  if (Path_unextended_qend_p(pathpair->path5,/*endtrim_allowed*/25,/*allow_ambig_p*/false) == true) {
	    possible_fusion5_p = true;
	  }
	  if (Path_unextended_qstart_p(pathpair->path3,/*endtrim_allowed*/25,/*allow_ambig_p*/false) == true) {
	    possible_fusion3_p = true;
	  }
	}
	i++;
      }
    }

    debug8(printf("possible_fusion5_p %d, possible_fusion3_p %d\n",
		  possible_fusion5_p,possible_fusion3_p));

    if (possible_fusion5_p == true || possible_fusion3_p == true) {
      /* 3b.  Look for outer fusions */

      /* Clean up unextended paths to reduce complexity of finding
	 fusions, and to allow for single results in Path_fusion
	 procedures */
      if (possible_fusion5_p == true) {
	stage1_5->sense_paths_gplus = Path_filter(stage1_5->sense_paths_gplus,
						  intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_5->sense_paths_gminus = Path_filter(stage1_5->sense_paths_gminus,
						   intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_5->antisense_paths_gplus = Path_filter(stage1_5->antisense_paths_gplus,
						      intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_5->antisense_paths_gminus = Path_filter(stage1_5->antisense_paths_gminus,
						       intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_5->unextended_sense_paths_gplus = Path_filter(stage1_5->unextended_sense_paths_gplus,
							     intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_5->unextended_sense_paths_gminus = Path_filter(stage1_5->unextended_sense_paths_gminus,
							      intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_5->unextended_antisense_paths_gplus = Path_filter(stage1_5->unextended_antisense_paths_gplus,
								 intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_5->unextended_antisense_paths_gminus = Path_filter(stage1_5->unextended_antisense_paths_gminus,
								  intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
      }
      if (possible_fusion3_p == true) {
	stage1_3->sense_paths_gplus = Path_filter(stage1_3->sense_paths_gplus,
						  intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_3->sense_paths_gminus = Path_filter(stage1_3->sense_paths_gminus,
						   intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_3->antisense_paths_gplus = Path_filter(stage1_3->antisense_paths_gplus,
						      intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_3->antisense_paths_gminus = Path_filter(stage1_3->antisense_paths_gminus,
						       intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_3->unextended_sense_paths_gplus = Path_filter(stage1_3->unextended_sense_paths_gplus,
							     intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_3->unextended_sense_paths_gminus = Path_filter(stage1_3->unextended_sense_paths_gminus,
							      intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_3->unextended_antisense_paths_gplus = Path_filter(stage1_3->unextended_antisense_paths_gplus,
								 intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	stage1_3->unextended_antisense_paths_gminus = Path_filter(stage1_3->unextended_antisense_paths_gminus,
								  intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
      }
      
      paths = (List_T) NULL;
      for (i = 0; i < npaths; i++) {
	pathpair = pathpairarray[i];

	/* Push original pathpair onto paths */
	paths = Hitlist_push(paths,hitlistpool, (void *) pathpair
			     hitlistpool_trace(__FILE__,__LINE__));

	/* Look for fusion paths */
	if (pathpair->plusp == true) {
	  /* plus */
	  if (Path_unextended_qstart_p(pathpair->path5,/*endtrim_allowed*/25,/*allow_ambig_p*/false) == false) {
	    fusion_paths_5 = (List_T) NULL;
	  } else {
	    fusion_paths_5 = Path_fusion_outer_querystart_plus(&(*found_score_5),/*main*/pathpair->path5,stage1_5,
							       query5_compress_fwd,query5_compress_rev,
							       queryseq5,querylength5,knownsplicing,pathpair->path5->genestrand,
							       nmismatches_allowed_5,max_insertionlen_5,max_deletionlen_5,
							       intlistpool,uintlistpool,univcoordlistpool,
							       listpool,univdiagpool,pathpool,vectorpool,
							       hitlistpool,transcriptpool,/*endtrim_allowed*/8);
	  }

	  if (Path_unextended_qend_p(pathpair->path3,/*endtrim_allowed*/25,/*allow_ambig_p*/false) == false) {
	    fusion_paths_3 = (List_T) NULL;
	  } else {
	    fusion_paths_3 = Path_fusion_outer_queryend_plus(&(*found_score_3),/*main*/pathpair->path3,stage1_3,
							     query3_compress_fwd,query3_compress_rev,
							     queryseq3,querylength3,knownsplicing,pathpair->path3->genestrand,
							     nmismatches_allowed_3,max_insertionlen_3,max_deletionlen_3,
							     intlistpool,uintlistpool,univcoordlistpool,
							     listpool,univdiagpool,pathpool,vectorpool,
							     hitlistpool,transcriptpool,/*endtrim_allowed*/8);
	  }

	  if (fusion_paths_5 == NULL && fusion_paths_3 == NULL) {
	    /* Skip */

	  } else if (fusion_paths_5 != NULL && fusion_paths_3 == NULL) {
	    for (p = fusion_paths_5; p != NULL; p = List_next(p)) {
	      paths = Hitlist_push(paths,hitlistpool,
				   (void *) Pathpair_new_concordant(/*pathL*/(Path_T) List_head(p),/*pathH*/pathpair->path3,
								    /*queryseqL*/queryseq5,/*queryseqH*/queryseq3,/*plusp*/true,
								    intlistpool,univcoordlistpool,listpool,pathpool,vectorpool,
								    transcriptpool,/*copyLp (was false)*/true,/*copyHp*/true)
				   hitlistpool_trace(__FILE__,__LINE__));
	    }
	    /* Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool); */
	    Path_gc(&fusion_paths_5,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);

	  } else if (fusion_paths_5 == NULL && fusion_paths_3 != NULL) {
	    for (p = fusion_paths_3; p != NULL; p = List_next(p)) {
	      paths = Hitlist_push(paths,hitlistpool,
				   (void *) Pathpair_new_concordant(/*pathL*/pathpair->path5,/*pathH*/(Path_T) List_head(p),
								    /*queryseqL*/queryseq5,/*queryseqH*/queryseq3,/*plusp*/true,
								    intlistpool,univcoordlistpool,listpool,pathpool,vectorpool,
								    transcriptpool,/*copyLp*/true,/*copyHp (was false)*/true)
				   hitlistpool_trace(__FILE__,__LINE__));
	    }
	    /* Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool); */
	    Path_gc(&fusion_paths_3,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);

	  } else {
	    for (p = fusion_paths_5; p != NULL; p = List_next(p)) {
	      for (q = fusion_paths_3; q != NULL; q = List_next(q)) {
		paths = Hitlist_push(paths,hitlistpool,
				     (void *) Pathpair_new_concordant(/*pathL*/(Path_T) List_head(p),/*pathH*/(Path_T) List_head(q),
								      /*queryseqL*/queryseq5,/*queryseqH*/queryseq3,/*plusp*/true,
								      intlistpool,univcoordlistpool,listpool,pathpool,vectorpool,
								      transcriptpool,/*copyLp (was false)*/true,/*copyHp (was false)*/true)
				     hitlistpool_trace(__FILE__,__LINE__));
	      }
	    }
	    /* Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool); */
	    Path_gc(&fusion_paths_5,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	    Path_gc(&fusion_paths_3,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	  }

	} else {
	  /* minus */
	  if (Path_unextended_qstart_p(pathpair->path3,/*endtrim_allowed*/25,/*allow_ambig_p*/false) == false) {
	    fusion_paths_3 = (List_T) NULL;
	  } else {
	    fusion_paths_3 = Path_fusion_outer_querystart_minus(&(*found_score_3),/*main*/pathpair->path3,stage1_3,
								query3_compress_fwd,query3_compress_rev,
								queryseq3,querylength3,knownsplicing,pathpair->path3->genestrand,
								nmismatches_allowed_3,max_insertionlen_3,max_deletionlen_3,
								intlistpool,uintlistpool,univcoordlistpool,
								listpool,univdiagpool,pathpool,vectorpool,
								hitlistpool,transcriptpool,/*endtrim_allowed*/8);
	  }

	  if (Path_unextended_qend_p(pathpair->path5,/*endtrim_allowed*/25,/*allow_ambig_p*/false) == false) {
	    fusion_paths_5 = (List_T) NULL;
	  } else {
	    fusion_paths_5 = Path_fusion_outer_queryend_minus(&(*found_score_5),/*main*/pathpair->path5,stage1_5,
							      query5_compress_fwd,query5_compress_rev,
							      queryseq5,querylength5,knownsplicing,pathpair->path5->genestrand,
							      nmismatches_allowed_5,max_insertionlen_5,max_deletionlen_5,
							      intlistpool,uintlistpool,univcoordlistpool,
							      listpool,univdiagpool,pathpool,vectorpool,
							      hitlistpool,transcriptpool,/*endtrim_allowed*/8);
	  }

	  if (fusion_paths_5 == NULL && fusion_paths_3 == NULL) {
	    /* Skip */

	  } else if (fusion_paths_5 != NULL && fusion_paths_3 == NULL) {
	    for (p = fusion_paths_5; p != NULL; p = List_next(p)) {
	      paths = Hitlist_push(paths,hitlistpool,
				   (void *) Pathpair_new_concordant(/*pathL*/pathpair->path3,/*pathH*/(Path_T) List_head(p),
								    /*queryseqL*/queryseq3,/*queryseqH*/queryseq5,/*plusp*/false,
								    intlistpool,univcoordlistpool,listpool,pathpool,vectorpool,
								    transcriptpool,/*copyLp*/true,/*copyHp (was false)*/true)
				   hitlistpool_trace(__FILE__,__LINE__));
	    }
	    /* Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool); */
	    Path_gc(&fusion_paths_5,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);

	  } else if (fusion_paths_5 == NULL && fusion_paths_3 != NULL) {
	    for (p = fusion_paths_3; p != NULL; p = List_next(p)) {
	      paths = Hitlist_push(paths,hitlistpool,
				   (void *) Pathpair_new_concordant(/*pathL*/(Path_T) List_head(p),/*pathH*/pathpair->path5,
								    /*queryseqL*/queryseq3,/*queryseqH*/queryseq5,/*plusp*/false,
								    intlistpool,univcoordlistpool,listpool,pathpool,vectorpool,
								    transcriptpool,/*copyLp (was false)*/true,/*copyHp*/true)
				   hitlistpool_trace(__FILE__,__LINE__));
	    }
	    /* Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool); */
	    Path_gc(&fusion_paths_3,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);

	  } else {
	    for (p = fusion_paths_5; p != NULL; p = List_next(p)) {
	      for (q = fusion_paths_3; q != NULL; q = List_next(q)) {
		paths = Hitlist_push(paths,hitlistpool,
				     (void *) Pathpair_new_concordant(/*pathL*/(Path_T) List_head(q),/*pathH*/(Path_T) List_head(p),
								      /*queryseqL*/queryseq3,/*queryseqH*/queryseq5,/*plusp*/false,
								      intlistpool,univcoordlistpool,listpool,pathpool,vectorpool,
								      transcriptpool,/*copyLp (was false)*/true,/*copyHp (was false)*/true)
				     hitlistpool_trace(__FILE__,__LINE__));
	      }
	    }
	    /* Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool); */
	    Path_gc(&fusion_paths_5,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	    Path_gc(&fusion_paths_3,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool,hitlistpool);
	  }
	}
      }

      /* Use new pathpairarray that integrates original paths and fusions */
      FREE_OUT(pathpairarray);
      pathpairarray = (Pathpair_T *) List_to_array_out_n(&npaths,paths);
      Hitlistpool_free_list(&paths,hitlistpool
			    hitlistpool_trace(__FILE__,__LINE__));
      debug8(printf("HAVE %d PATHS\n",npaths));
    }


    /* 4.  Find best paths in each local region */
    qsort(pathpairarray,npaths,sizeof(Pathpair_T),Pathpair_interval_cmp);

    k = 0;
    i = 0;
    while (i < npaths) {
      j = i + 1;
      while (j < npaths && Pathpair_overlap_p(pathpairarray[j],pathpairarray[i]) == true) {
	j++;
      }
      debug8(printf("Found an overlapping group of %d => re-sorting by Pathpair_local_cmp\n",j - i));

      /* Keep the best one in the overlapping group */
      qsort(&(pathpairarray[i]),j - i,sizeof(Pathpair_T),Pathpair_local_cmp);
#ifdef DEBUG8
      printf("(4) Keeping by local_cmp ");
      printf(" %p transcript_concordant_p: %d, nmatches %d+%d, insertlength %u, outerlength %u\n",
	     pathpairarray[i],pathpairarray[i]->transcript_concordant_p,
	     pathpairarray[i]->pathL->nmatches,pathpairarray[i]->pathH->nmatches,
	     pathpairarray[i]->insertlength,pathpairarray[i]->outerlength);
      Path_print(pathpairarray[i]->pathL);
      Path_print(pathpairarray[i]->pathH);
#endif
      pathpairarray[k++] = pathpairarray[i];

      for (l = i + 1; l < j; l++) {
#ifdef DEBUG8
	printf("(4) Eliminating by local_cmp ");
	printf(" %p transcript_concordant_p: %d, nmatches %d+%d, insertlength %u, outerlength %u\n",
	       pathpairarray[l],pathpairarray[l]->transcript_concordant_p,
	       pathpairarray[l]->pathL->nmatches,pathpairarray[l]->pathH->nmatches,
	       pathpairarray[l]->insertlength,pathpairarray[l]->outerlength);
	Path_print(pathpairarray[l]->pathL);
	Path_print(pathpairarray[l]->pathH);
#endif
	pathpair = pathpairarray[l];
	Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool);
      }

      i = j;
    }
    npaths = k;


    /* 5.  Find best solution globally */
    /* TODO: Can make this O(n) rather than O(n*log n) */
    if (npaths > 1) {
      qsort(pathpairarray,npaths,sizeof(T),Pathpair_global_cmp);
    }
    debug8(printf("Found the global best solution with %d + %d nmatches\n",
		  pathpairarray[0]->path5->nmatches,pathpairarray[0]->path3->nmatches));


    /* 6.  Check if we should be filtering the result */
    pathpair = pathpairarray[0];
    if (pathpair->path5->score_within_trims > nmismatches_filter_5 || pathpair->path3->score_within_trims > nmismatches_filter_3) {
      debug8(printf("Best solution has too many %d + %d nmismatches, so eliminating all\n",
		    pathpair->path5->score_within_trims,pathpair->path3->score_within_trims));
      for (k = 0; k < npaths; k++) {
	debug8(printf("(5) Eliminating ")); debug8(Path_print(pathpairarray[k]->pathL)); debug8(Path_print(pathpairarray[k]->pathH));
	pathpair = pathpairarray[k];
	Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool);
      }
      FREE_OUT(pathpairarray);
      *npaths_primary = *npaths_altloc = 0;
      return (Pathpair_T *) NULL;

    } else if (Path_coverage(pathpair->path5) < mincoverage_filter_5 ||
	       Path_coverage(pathpair->path3) < mincoverage_filter_3) {
      debug8(printf("Best solution has too little %d and %d coverage, so eliminating all\n",
		    Path_coverage(pathpair->path5),Path_coverage(pathpair->path3)));
      for (k = 0; k < npaths; k++) {
	debug8(printf("(6a) Eliminating ")); debug8(Path_print(pathpairarray[k]->pathL)); debug8(Path_print(pathpairarray[k]->pathH));
	pathpair = pathpairarray[k];
	Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool);
      }
      FREE_OUT(pathpairarray);
      *npaths_primary = *npaths_altloc = 0;
      return (Pathpair_T *) NULL;

    } else {
      /* Otherwise, keep all paths equivalent to the best path */
      debug8(printf("(6a) Keeping best by global_cmp\n")); debug8(Path_print(pathpairarray[0]->pathL)); debug8(Path_print(pathpairarray[0]->pathH));
      i = 1;			/* Skip the best path */
      
      while (i < npaths && Pathpair_global_cmp(&(pathpairarray[i]),&(pathpairarray[0])) == 0) {
	debug8(printf("(6b) Keeping by global_cmp\n")); debug8(Path_print(pathpairarray[i]->pathL)); debug8(Path_print(pathpairarray[i]->pathH));
	i++;
      }

      /* Remove non-optimal paths */
      for (k = i; k < npaths; k++) {
	debug8(printf("This solution has only %d + %d nmatches\n",
		      pathpairarray[k]->path5->nmatches,pathpairarray[k]->path3->nmatches));
	debug8(printf("(6b) Eliminating by global_cmp\n")); debug8(Path_print(pathpairarray[k]->pathL)); debug8(Path_print(pathpairarray[k]->pathH));
	pathpair = pathpairarray[k];
	Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool);
      }
      npaths = i;			
    }


    if (want_random_p && npaths > 1) {
      /* Randomize among best alignments */
      /* randomi = (int) ((double) i * rand()/((double) RAND_MAX + 1.0)); */
      randomi = (int) (rand() / (((double) RAND_MAX + 1.0) / (double) npaths));
      /* fprintf(stderr,"%d dups => random %d\n",i,randomi); */
      debug8(printf("Swapping random path (%d out of %d) %p with best path %p\n",
		    randomi,npaths,pathpairarray[randomi],pathpairarray[0]));
      temp = pathpairarray[0];
      pathpairarray[0] = pathpairarray[randomi];
      pathpairarray[randomi] = temp;
    }


    /* Trim alignments at chromosomal bounds */
    for (i = 0; i < npaths; i++) {
      pathpair = pathpairarray[i];
      if (circularp[pathpair->pathL->chrnum] == false) {
	Path_trim_qstart_chrbounds(pathpair->pathL,intlistpool,univcoordlistpool,listpool,pathpool);
      } else if (output_type == STD_OUTPUT || output_type == M8_OUTPUT) {
	/* If output type is alignment or m8, then don't want to split up the parts */
      } else if (pathpair->pathL->plusp == true) {
	Path_trim_circular(pathpair->pathL,query5_compress_fwd,intlistpool,univcoordlistpool,listpool);
      } else {
	Path_trim_circular(pathpair->pathL,query3_compress_rev,intlistpool,univcoordlistpool,listpool);
      }

      if (circularp[pathpair->pathH->chrnum] == false) {
	Path_trim_qend_chrbounds(pathpair->pathH,intlistpool,univcoordlistpool,listpool,pathpool);
      } else if (output_type == STD_OUTPUT || output_type == M8_OUTPUT) {
	/* If output type is alignment or m8, then don't want to split up the parts */
      } else if (pathpair->pathH->plusp == true) {
	Path_trim_circular(pathpair->pathH,query3_compress_fwd,intlistpool,univcoordlistpool,listpool);
      } else {
	Path_trim_circular(pathpair->pathH,query5_compress_rev,intlistpool,univcoordlistpool,listpool);
      }
    }


    /* Compute mapq_loglik */
    if (npaths == 1) {
      pathpair = pathpairarray[0];

      Path_mark_alignment(pathpair->path5,query5_compress_fwd,queryuc_ptr_5,query5_compress_rev,queryrc5,
			  pathpool);
      Path_mark_alignment(pathpair->path3,query3_compress_fwd,queryuc_ptr_3,query3_compress_rev,queryrc3,
			  pathpool);

      pathpair->mapq_loglik = MAPQ_MAXIMUM_SCORE;
      pathpair->mapq_score = MAPQ_max_quality_score(quality_string_5,querylength5);
      if ((mapq_score = MAPQ_max_quality_score(quality_string_3,querylength3)) > pathpairarray[0]->mapq_score) {
	pathpair->mapq_score = mapq_score;
      }
      pathpair->absmq_score = MAPQ_MAXIMUM_SCORE;

      *first_absmq = pathpair->absmq_score;
      *second_absmq = 0;

    } else {
      for (i = 0; i < npaths; i++) {
	pathpair = pathpairarray[i];
	path5 = pathpair->path5;
	path3 = pathpair->path3;
	Path_mark_alignment(path5,query5_compress_fwd,queryuc_ptr_5,query5_compress_rev,queryrc5,
			    pathpool);
	Path_mark_alignment(path3,query3_compress_fwd,queryuc_ptr_3,query3_compress_rev,queryrc3,
			    pathpool);
	pathpair->mapq_loglik =
	  MAPQ_loglik_string(path5->genomic_diff,quality_string_5,querylength5,path5->plusp);
	pathpair->mapq_loglik +=
	  MAPQ_loglik_string(path3->genomic_diff,quality_string_3,querylength3,path3->plusp);
      }


      /* Enforce monotonicity */
      for (i = npaths - 1; i > 0; i--) {
	if (pathpairarray[i-1]->mapq_loglik < pathpairarray[i]->mapq_loglik) {
	  pathpairarray[i-1]->mapq_loglik = pathpairarray[i]->mapq_loglik;
	}
      }
      maxlik = pathpairarray[0]->mapq_loglik;

      /* Subtract maxlik to avoid underflow */
      for (i = 0; i < npaths; i++) {
	pathpairarray[i]->mapq_loglik -= maxlik;
      }

      /* Compute absolute mapq */
      for (i = 0; i < npaths; i++) {
	loglik = pathpairarray[i]->mapq_loglik + MAPQ_MAXIMUM_SCORE;
	if (loglik < 0.0) {
	  loglik = 0.0;
	}
	pathpairarray[i]->absmq_score = rint(loglik);
      }
      *first_absmq = pathpairarray[0]->absmq_score;
      if (npaths == 1) {
	*second_absmq = 0;
      } else {
	*second_absmq = pathpairarray[1]->absmq_score;
      }

      /* Compute Bayesian mapq */
      total = 0.0;
      for (i = 0; i < npaths; i++) {
	total += (pathpairarray[i]->mapq_loglik = fasterexp(pathpairarray[i]->mapq_loglik));
      }

      /* Obtain posterior probabilities of being true */
      for (i = 0; i < npaths; i++) {
	pathpairarray[i]->mapq_loglik /= total;
      }

      /* Convert to Phred scores */
      for (i = 0; i < npaths; i++) {
	if ((qual = 1.0 - pathpairarray[i]->mapq_loglik) < 2.5e-10 /* 10^-9.6 */) {
	  pathpairarray[i]->mapq_score = 40;
	} else {
	  pathpairarray[i]->mapq_score = rint(-10.0 * log10(qual));
	}
      }
    }
  }

  /* Filter for chrsubset */
  /* Want to allow other alignments to be found before filtering */
  *npaths_primary = *npaths_altloc = 0;
  if (chrsubsetp == NULL) {
    k = 0;
    for (pathi = 0; pathi < npaths; pathi++) {
      pathpair = pathpairarray[pathi];
      if (altlocp[pathpair->path5->chrnum] == true ||
	  altlocp[pathpair->path3->chrnum] == true) {
	(*npaths_altloc) += 1;
      } else {
	(*npaths_primary) += 1;
      }
    }

    return pathpairarray;

  } else {
    k = 0;
    for (pathi = 0; pathi < npaths; pathi++) {
      pathpair = pathpairarray[pathi];
      if (chrsubsetp[pathpair->path5->chrnum] == false ||
	  chrsubsetp[pathpair->path3->chrnum] == false) {
	/* Do not save this pathpair */
	Pathpair_free(&pathpair,intlistpool,univcoordlistpool,listpool,pathpool,transcriptpool);
	
      } else {
	/* Save this pathpair.  Re-use existing array */
	if (altlocp[pathpair->path5->chrnum] == true ||
	    altlocp[pathpair->path3->chrnum] == true) {
	  (*npaths_altloc) += 1;
	} else {
	  (*npaths_primary) += 1;
	}
	pathpairarray[k++] = pathpair;
      }
    }

    if ((*npaths_primary) + (*npaths_altloc) == 0) {
      FREE_OUT(pathpairarray);
      return (Pathpair_T *) NULL;
    } else {
      return pathpairarray;
    }
  }
}


void
Pathpair_eval_setup (Transcriptome_T transcriptome_in,
		     bool *circularp_in, bool *chrsubsetp_in, bool *altlocp_in,
		     Outputtype_T output_type_in,
		     bool splicingp_in, bool resolve_inner_p_in, bool want_random_p_in) {

  transcriptome = transcriptome_in;

  circularp = circularp_in;
  chrsubsetp = chrsubsetp_in;
  altlocp = altlocp_in;

  output_type = output_type_in;
  splicingp = splicingp_in;
  resolve_inner_p = resolve_inner_p_in;
  want_random_p = want_random_p_in;

  return;
}

