/***************************************************************

   The Subread software package is free software package:
   you can redistribute it and/or modify it under the terms
   of the GNU General Public License as published by the
   Free Software Foundation, either version 3 of the License,
   or (at your option) any later version.

   Subread is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty
   of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

   See the GNU General Public License for more details.

   Authors: Drs Yang Liao and Wei Shi

  ***************************************************************/

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include <pthread.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sys/timeb.h>
#include <sys/stat.h>
#include <locale.h>
#include <ctype.h>
#include <unistd.h>
#include <getopt.h>

#include "../subread.h"
#include "LRMconfig.h"
#include "LRMhelper.h"
#include "LRMsorted-hashtable.h"
#include "LRMbase-index.h"
#include "LRMchro-event.h"
#include "LRMfile-io.h"


int main(int argc, char ** argv){
	int retv=0;
	
	LRMcontext_t *context = NULL;
	retv = retv || LRMvalidate_and_init_context(&context, argc, argv);
	retv = retv || LRMshow_conf(context);
	retv = retv || LRMrun_task(context);
	retv = retv || LRMfinalise(context);
	retv = retv || LRMprint_mapping_summary(context);
	retv = retv || LRMdestroy_context(context);
	context = NULL;
	
	return retv;
}

int LRMprint_mapping_summary(LRMcontext_t * context){
	LRMprintf("\n\nAll finished.\n\nTotal processed reads : %d\n", context -> all_processed_reads);
	LRMprintf("Mapped reads: %u (%.1f%%)\n", context->mapped_reads, context->mapped_reads*100./context->all_processed_reads);
	LRMprintf("Time: %.1f minutes\n\n" , (LRMmiltime() - context->start_running_time)/60);
	return 0;
}

void LRMprint_version(){
	LRMprintf("\nVersion %s\n", SUBREAD_VERSION);
	LRMputs("");
}
void LRMprint_usage(){
	LRMprint_version();
	LRMputs("Usage:");
	LRMputs("");
	LRMputs("./sublong [options] -i <index_name> -r <input> -o <output>");
	LRMputs("");
	LRMputs("## Mandatory arguments:");
	LRMputs("");
	LRMputs(" -i <string>   Base name of the index. The index must be built as a full index.");
	LRMputs("");
	LRMputs(" -r <string>   Name of an input read file. Acceptable formats include gzipped");
    LRMputs("               FASTQ, FASTQ and FASTA (automatically identified). The quality");
	LRMputs("               scores should be in the Phred 33 format for the FASTQ or gzipped");
	LRMputs("               FASTQ formats.");
	LRMputs("");
	LRMputs(" -o <string>   Name of an output file. By default, the output is in BAM format.");
	LRMputs("");
	LRMputs("## Optional arguments:");
	LRMputs("# input reads and output");
	LRMputs("");
	LRMputs(" --SAMoutput   Save mapping results in SAM format.");
	LRMputs("");
	LRMputs("# thresholds for mapping");
	LRMputs("");
	LRMputs(" -n <int>      Number of selected subreads in a readlet, 85 by default.");
	LRMputs("");
	LRMputs(" -m <int>      Consensus threshold for mapping a readlet. 1 by default.");
	LRMputs("");
	LRMputs(" -X <int>      Maximum number of mis-matched bases allowed in each subread.");
	LRMputs("               0 by default.");
	LRMputs("");
	LRMputs("# number of CPU threads");
	LRMputs("");
	LRMputs(" -T <int>      Number of CPU threads used. 1 by default.");
	LRMputs("");
	LRMputs("# others");
	LRMputs("");
	LRMputs(" -v            Output version of the program.");
	LRMputs("");
	LRMputs("Refer to Users Manual for detailed description to the arguments.");
	LRMputs("");
}


static struct option long_options[] ={
	{"SAMoutput", no_argument, 0, 0},
	{0, 0, 0, 0}
};

int LRMvalidate_and_init_context(LRMcontext_t ** context, int argc, char ** argv){
	int c;
	
	(*context) = malloc(sizeof(LRMcontext_t));
	memset((*context), 0, sizeof(LRMcontext_t));
	LRMset_default_values_context(*context);

	(*context) -> input_file_name[0] = 0;
	(*context) -> output_file_name[0] = 0;	
	(*context) -> index_prefix[0] = 0;
	
	optind = 0;
	opterr = 1;
	optopt = 63;
	int option_index = 0;	
	while ((c = getopt_long (argc, argv, "B:Mr:i:o:T:P:m:O:X:n:jqv", long_options, &option_index))!=-1){
		switch(c){
			case 'M':
				(*context) -> unique_only = 0;
				break;
			case 'B':
				(*context) -> max_best_alignments = atoi(optarg);
				(*context) -> max_best_alignments = min((*context) -> max_best_alignments, LRMMAX_MULTI_BEST );
				(*context) -> unique_only = 0;
				break;
			case 'P': // not useable now!
				(*context) -> is_Phred_64=(optarg[0]=='6');
				break;
			case 'j': // not useable now.
				(*context) -> do_junction_detection = 1;
				(*context) -> result_merge_tolerance = 500000;
				break;
			case 'q': // not for end users.
				(*context) -> show_read_validation = 1;
				break;
			case 'r':
				strcpy((*context) -> input_file_name, optarg);
				break;
			case 'i':
				strcpy((*context) -> index_prefix, optarg);
				break;
			case 'o':
				strcpy((*context) -> output_file_name, optarg);
				break;
			case 'T':
				(*context) -> threads = min(max(1,atoi(optarg)),LRMMAX_THREADS);
				break;
			case 'n':
				(*context) -> max_subreads_per_segment = atoi(optarg);
				assert( (*context) -> max_subreads_per_segment< LRMMAX_SUBREAD_PER_SEGMENT_HARDLIMIT );
				break;
			case 'X':
				(*context) -> max_mismatched_bases_in_subread = atoi(optarg);
				assert((*context) -> max_mismatched_bases_in_subread <3);
				break;
			case 'O': // not useable now.
				(*context) -> segment_overlapping = atoi(optarg);
				break;
			case 'm':
				(*context) -> min_voting_number = atoi(optarg);
				break;
			case 'v':
				LRMprint_version();
				return 1;
			case 0:
				if(strcmp("SAMoutput", long_options[option_index].name)==0) {
					(*context) -> is_SAM_output = 1;
				}
				break;
			case '?':
			default:
				LRMprint_usage();
				return 1;
		}
	}

	if((*context) -> input_file_name[0] ==0 || (*context) -> output_file_name[0]==0 || (*context) -> index_prefix[0]==0){
		LRMprint_usage();
		if(!((*context) -> input_file_name[0] ==0 && (*context) -> output_file_name[0]==0 && (*context) -> index_prefix[0]==0)){
			LRMprintf("Please specify the input, output files and the index.\n");//(*context) -> input_file_name[0] ,  (*context) -> output_file_name[0],  (*context) -> index_prefix[0]);
		}
		return 1;
	}

	int index_gap = 99999;
	int retv = LRMgehash_load_option((*context) -> index_prefix, LRMSUBREAD_INDEX_OPTION_INDEX_GAP , &index_gap);
	if(retv<0){
		LRMprintf("\nUnable to find the index.\n\n");
		return 1;
	}else retv = 0;
	if(index_gap != 1){
		LRMprintf("\nPlease build the index as a full index.\n\n");
		return 1;
	}

	(*context) -> user_command_line[0]=0;
	for(c = 0; c<argc;c++)
		sprintf((*context) -> user_command_line+strlen( (*context) -> user_command_line), "\"%s\" ", argv[c]);
	

	(*context) -> max_cigar_opts_in_read = (*context) -> is_SAM_output? LRMMAX_CIGAR_OPTS_IN_SAM_READ : LRMMAX_CIGAR_OPTS_IN_BAM_READ;
	LRMthread_lockinit(&(*context) -> input_lock);
	LRMthread_lockinit(&(*context) -> sam_bam_file_lock);
	
	(*context)-> sam_bam_chromosome_table = HashTableCreate(199);
	HashTableSetKeyComparisonFunction((*context)-> sam_bam_chromosome_table, LRMhash_strcmp);
	HashTableSetHashFunction((*context)-> sam_bam_chromosome_table, LRMhash_strhash);
	HashTableSetDeallocationFunctions((*context)-> sam_bam_chromosome_table, NULL, NULL);
	

	(*context)-> chromosome_size_list = ArrayListCreate(29);

	(*context)-> chromosome_size_table = HashTableCreate(199);
	HashTableSetKeyComparisonFunction((*context)-> chromosome_size_table, LRMhash_strcmp);
	HashTableSetHashFunction((*context)-> chromosome_size_table, LRMhash_strhash);
	HashTableSetDeallocationFunctions((*context)-> chromosome_size_table, free, NULL);

	(*context) -> sam_bam_chromosome_list = ArrayListCreate(29);
	
	LRMload_offsets(*context);

	retv = LRMgeinput_open((*context)->input_file_name,&(*context) -> input_file);
	if(retv)  LRMprintf("\nUnable to open the input file.\n");

	(*context)->sam_bam_file = fopen( (*context) -> output_file_name, "w");
	if(NULL == (*context)->sam_bam_file) retv = 1;
	
	(*context)->event_space = malloc(sizeof(LRMevent_t)*20000);
	(*context)->event_space_size = 20000;
	LRMthread_lockinit(&(*context) -> event_space_lock);
	(*context)->events_realignment  = HashTableCreate(320000);

	assert(LRMMAX_SUBREAD_PER_SEGMENT_HARDLIMIT >= (*context)->max_subreads_per_segment );

	return retv;
}


double LRMmiltime(){
	double ret;
	#ifdef FREEBSD
	struct timeval tp;
	struct timezone tz;
	tz.tz_minuteswest=0;
	tz.tz_dsttime=0;

	gettimeofday(&tp,&tz);

	ret = tp.tv_sec+ 0.001*0.001* tp.tv_usec; 

	#else

	struct timeb trp;
	ftime(&trp);
	ret = trp.time*1.0+(trp.millitm*1.0/1000.0);
	#endif

	return ret;
}


void LRMset_default_values_context(LRMcontext_t * context){
	context->threads = 1;
	context->start_running_time = LRMmiltime();
	context->multi_best_read_alignments = 1;
	context->max_dynamic_indel_length = 5000;
	
	context->max_read_indel_length = 0;
	context->max_junction_distance = 100000;
	context->max_mismatched_bases_in_subread = 0;
	context->max_subreads_per_segment = 85;
	context->min_voting_number = 1;
	context->min_matched_bases = 40;
	context->segment_overlapping = 20;
	context->result_merge_tolerance = 15000;
	context->unique_only = 1;
	context->max_best_alignments = 1;


	context->dynamic_programming_score_match = 6;
	context->dynamic_programming_score_mismatch = 0;
	context->dynamic_programming_score_create_gap = -4;
	context->dynamic_programming_score_extend_gap = -1;
}

int LRMshow_conf(LRMcontext_t * context){
	LRMprintf("\n ====== Subread long read mapping ======\n\n");
	LRMprintf("Threads: %d\n" , context->threads);
	LRMprintf("Input file: %s\n" , context->input_file_name);
	LRMprintf("Output file: %s (%s)\n" , context->output_file_name,  context->is_SAM_output?"SAM":"BAM");
	LRMprintf("Index: %s\n\n" , context->index_prefix);
	
	return 0;
}

int LRMinit_chunk(LRMcontext_t * context){
	if(context->all_processed_reads) memset(context ->  read_mapping_results, 0, sizeof(LRMread_mapping_result_t)*LRMREADS_PER_CHUNK);
	return 0;
}

int LRMrun_task(LRMcontext_t * context){
	int retv = 0;
	retv = LRMload_index( context );
	if(!retv) LRMprintf("Index was loaded; the gap bewteen subreads is %d bases\n", context -> current_index.index_gap );
	while(!(retv ||LRMinput_has_finished( context ))){
		retv=retv || LRMinit_chunk(context);
		retv=retv || LRMsave_input_pos(context);
		retv=retv || LRMiterate_reads( context , LRMRUNNING_STEP_VOTING);
		retv=retv || LRMrewind_input_pos(context);
		retv=retv || LRMiterate_reads( context , LRMRUNNING_STEP_REALIGN);
		retv=retv || LRMfinalise_chunk_reads(context);
	}
	return retv;
}

int LRMfinalise(LRMcontext_t * context){
	return 0;
}

int LRMdestroy_context(LRMcontext_t * context){

	LRMgehash_destory(&(context -> current_index));
	LRMgvindex_destory(&(context -> current_base_index));
	
	HashTableDestroy(context -> chromosome_size_table);
	ArrayListDestroy(context -> chromosome_size_list);

	HashTableDestroy(context -> sam_bam_chromosome_table);
	ArrayListDestroy(context -> sam_bam_chromosome_list);
	
	HashTableSetDeallocationFunctions(context -> events_realignment, NULL, free);
	HashTableDestroy(context -> events_realignment);

	free(context -> event_space);

	int readno;
	for(readno = 0; readno < LRMREADS_PER_CHUNK; readno++){
		if(context -> read_mapping_results[readno].segment_results != NULL)
			free(context -> read_mapping_results[readno].segment_results);
	}

	if(!context -> is_SAM_output){
		fwrite(context -> bam_file_tail_binary,1, context -> bam_file_tail_length, context->sam_bam_file);
	}

	LRMgeinput_close(&context->input_file);
	fclose(context->sam_bam_file);
	//free(context->user_command_line);
	free(context);
	return 0;
}


int LRMinput_has_finished( LRMcontext_t * context ){
	return context -> input_exhausted ;
}

int LRMload_index(LRMcontext_t * context){
	int retv = 0;
	char indextab_fname[LRMMAX_FILENAME_LENGTH];

	sprintf(indextab_fname, "%s.00.b.tab", context -> index_prefix);
	retv = retv || LRMgehash_load(&(context -> current_index), indextab_fname);

	sprintf(indextab_fname, "%s.00.b.array", context -> index_prefix);
	retv = retv || LRMgvindex_load(&(context -> current_base_index), indextab_fname);
	
	return retv;
} 


int LRMiterate_reads( LRMcontext_t * context, int task ){
	int retv = 0;
	retv = retv || LRMstart_thread( context , task );
	retv = retv || LRMwait_threads( context );
	retv = retv || LRMmerge_threads( context, task );
	return retv;
}

void * LRM_thread_runner (void * args){
	void ** argv = args;
	LRMcontext_t * context = argv[0];
	int thid = argv[1]-NULL;
	int task = argv[2]-NULL;	
	free(args);

	LRMchunk_read_iteration(context, thid, task);	
	
	return NULL;
}

int LRMstart_thread_init_context(LRMcontext_t * context, int thread_id, int step){
	LRMthread_context_t * thread_context = context -> thread_contexts+thread_id;
	memset(thread_context, 0, sizeof(LRMthread_context_t));
	thread_context->thread_id = thread_id;
	
	if(step == LRMRUNNING_STEP_VOTING){
		if( thread_context -> thread_id == 0 )LRMsambam_write_header(context, thread_context);
		thread_context -> dynamic_programming_movement_buffer = malloc(( 2* LRMINDEL_DYNAMIC_CHANNEL_TOLERANCE + 1) * LRMDYNAMIC_MAXIMUM_GAP_LENGTH);
		thread_context -> dynamic_programming_score_buffer = malloc(sizeof(int) * ( 2 * LRMINDEL_DYNAMIC_CHANNEL_TOLERANCE + 1) *( LRMDYNAMIC_MAXIMUM_GAP_LENGTH+1));
		thread_context -> dynamic_programming_indel_movement_buf = malloc( max( LRMDYNAMIC_MAXIMUM_GAP_LENGTH * 15, 300 ) +  context -> max_dynamic_indel_length + 1 );
	}else if(step == LRMRUNNING_STEP_REALIGN){
		thread_context -> mapped_reads = 0;
		thread_context -> dynamic_programming_movement_buffer = malloc(( 2* LRMINDEL_DYNAMIC_CHANNEL_TOLERANCE + 1) * LRMDYNAMIC_MAXIMUM_GAP_LENGTH);
		thread_context -> dynamic_programming_score_buffer = malloc(sizeof(int) * ( 2 * LRMINDEL_DYNAMIC_CHANNEL_TOLERANCE + 1) *( LRMDYNAMIC_MAXIMUM_GAP_LENGTH+1));
		thread_context -> dynamic_programming_indel_movement_buf = malloc( max( LRMDYNAMIC_MAXIMUM_GAP_LENGTH * 15, 300 ) +  context -> max_dynamic_indel_length + 1 );

		thread_context -> out_SAMBAM_buffer = malloc(2400000);
		if(thread_context -> out_SAMBAM_buffer == NULL) return 1;
		
		thread_context -> out_buff_used = 0;
		thread_context -> out_buff_capacity = 2400000;
	}
	return 0;
}

int LRMstart_thread(LRMcontext_t * context, int task ){
	int th_id, retv=0;
		
	for(th_id=0; th_id<context -> threads; th_id++){
		
		retv = retv || LRMstart_thread_init_context(context,th_id,task);
		if(retv)
			break;
		else {
			void ** th_args=malloc(sizeof(void *)*3); // to be freed in the thread.
			th_args[0] = context;
			th_args[1] = NULL + th_id;
			th_args[2] = NULL + task;
			LRMpthread_create(context -> running_threads+th_id, NULL, LRM_thread_runner, th_args);
		}
	}
	
	return retv;
}

int LRMwait_threads( LRMcontext_t * context ){
	int th_id;
	for(th_id=0; th_id<context -> threads; th_id++)
		LRMpthread_join(context -> running_threads[th_id], NULL);
	return 0;
}

void LRMmerge_threads_destroy_context(LRMcontext_t * context, LRMthread_context_t * thread_context, int task){
	if(task == LRMRUNNING_STEP_VOTING){
		free(thread_context -> dynamic_programming_movement_buffer);
		free(thread_context -> dynamic_programming_score_buffer);
		free(thread_context -> dynamic_programming_indel_movement_buf);
	}else if(task == LRMRUNNING_STEP_REALIGN){
		free(thread_context -> dynamic_programming_movement_buffer);
		free(thread_context -> dynamic_programming_score_buffer);
		free(thread_context -> dynamic_programming_indel_movement_buf);
		free(thread_context -> out_SAMBAM_buffer);
	}
}

int LRMmerge_threads( LRMcontext_t * context , int step){
	int retv = 0;
	int th_id;

	for(th_id=0; th_id<context -> threads; th_id++){
		
		if(step == LRMRUNNING_STEP_VOTING){
			retv = retv || LRMevents_reorder(context);
			retv = retv || LRMevents_build_entries(context);
		}else if(step == LRMRUNNING_STEP_REALIGN){
			LRMwrite_chunk_check_buffer_write(context,  context -> thread_contexts+th_id, 1);
			if(th_id == context -> threads-1)LRMbam_generate_tail_binary(context,  context -> thread_contexts+th_id);
			context -> mapped_reads += context -> thread_contexts[th_id].mapped_reads;
		}else assert(0);		
		LRMmerge_threads_destroy_context(context,  context -> thread_contexts+th_id, step);
	}
	
	return retv;
}

int LRMrewind_input_pos(LRMcontext_t * context){
	context -> processed_reads_in_chunk = 0;
	if(context->input_file.file_type == LRMGENE_INPUT_GZIP_FASTQ)
		seekgz_seek(context->input_file.input_fp, &context->last_saved_zlib_pos);
	else
		fseeko(context->input_file.input_fp, context->last_saved_raw_pos, SEEK_SET);
	return 0;
}

int LRMsave_input_pos( LRMcontext_t * context){
	context -> processed_reads_in_chunk = 0;
	if(context->input_file.file_type == LRMGENE_INPUT_GZIP_FASTQ)
		seekgz_tell(context->input_file.input_fp, &context->last_saved_zlib_pos);
	else
		context -> last_saved_raw_pos = ftello(context->input_file.input_fp);
		
	return 0;
}

int LRMsplit_read_to_segments(LRMcontext_t * context, LRMread_iteration_context_t* iteration_context){
	int seg_curs = 0;
	iteration_context->total_segments = 0;
	if(iteration_context->read_length<=16) return 1;

	int increment_step = LRMSEGMENT_MIN_LENGTH - context -> segment_overlapping; //;LRMSEGMENT_MIN_LENGTH - LRMSEGMENT_OVERLAPPING;
	
	while(1){
		int seg_end = seg_curs + increment_step;
		if(seg_end + LRMSEGMENT_MIN_LENGTH > iteration_context->read_length) seg_end = iteration_context->read_length;
		assert(seg_curs >= 0);

		iteration_context->segment_texts[iteration_context->total_segments] = iteration_context->read_text + seg_curs;
		iteration_context->segment_quals[iteration_context->total_segments] = iteration_context->qual_text + seg_curs;
		iteration_context->segment_lengths[iteration_context->total_segments] =(seg_end == iteration_context->read_length ? iteration_context->read_length - seg_curs : LRMSEGMENT_MIN_LENGTH);
		//LRMprintf("SPLIT: [%d] len=%d\n", iteration_context->total_segments, iteration_context->segment_lengths[iteration_context->total_segments]);
		iteration_context->total_segments  ++;
		seg_curs = seg_end;
		if(seg_curs >= iteration_context->read_length) break;
	}

	//assert(iteration_context->total_segments <= LRMMAX_READ_LENGTH / LRMSEGMENT_MIN_LENGTH);
	return 0;
}

void LRMreverse_read_and_qual(LRMcontext_t * context, LRMthread_context_t * thread_context, LRMread_iteration_context_t * iteration_context){
	LRMreverse_read(iteration_context -> read_text, iteration_context -> read_length);
	LRMreverse_quality(iteration_context -> qual_text, iteration_context -> read_length);
	
	int segi;
	for(segi = 0; segi < iteration_context->total_segments / 2; segi++){
		//#warning " positions of the subreads should be moved after reversing -- for the best sequencing quality "
		unsigned int tmp_offsets[LRMMAX_SUBREAD_PER_SEGMENT_HARDLIMIT];
		memcpy(tmp_offsets, iteration_context->subread_offsets[segi], sizeof(int)* LRMMAX_SUBREAD_PER_SEGMENT_HARDLIMIT);
		memcpy(iteration_context->subread_offsets[segi], iteration_context->subread_offsets[ iteration_context->total_segments-segi-1 ],sizeof(int)* LRMMAX_SUBREAD_PER_SEGMENT_HARDLIMIT);
		memcpy(iteration_context->subread_offsets[iteration_context->total_segments-segi-1], tmp_offsets, sizeof(int)* LRMMAX_SUBREAD_PER_SEGMENT_HARDLIMIT);
	}

	for(segi = 0; segi < iteration_context->total_segments / 2; segi++){
		int old_left_length = iteration_context->segment_lengths[segi];
		int old_right_length = iteration_context->segment_lengths[iteration_context->total_segments - segi -1];

		int old_left_start = iteration_context -> segment_texts[segi] - iteration_context->read_text;
		int old_right_start = iteration_context -> segment_texts[ iteration_context->total_segments - segi -1 ] - iteration_context->read_text;
		int old_left_new_start = iteration_context -> read_length - (old_left_start + old_left_length);
		int old_right_new_start = iteration_context -> read_length - (old_right_start + old_right_length);

		iteration_context -> segment_texts[segi] = iteration_context->read_text + old_right_new_start;
		iteration_context -> segment_quals[segi] = iteration_context->qual_text + old_right_new_start;
		iteration_context -> segment_texts[iteration_context->total_segments -  segi -1] = iteration_context->read_text + old_left_new_start;
		iteration_context -> segment_quals[iteration_context->total_segments -  segi -1] = iteration_context->qual_text + old_left_new_start;

		int tmpi = iteration_context -> segment_lengths[ iteration_context->total_segments -  segi -1];
		iteration_context -> segment_lengths[ iteration_context->total_segments -  segi -1] = iteration_context -> segment_lengths[segi];
		iteration_context -> segment_lengths[segi] = tmpi;
	}

	if(iteration_context->total_segments % 2){
		int old_len = iteration_context -> segment_lengths[segi];
		int segi = iteration_context->total_segments / 2;
		int old_start = iteration_context -> segment_texts[segi] - iteration_context->read_text;
		int new_start = iteration_context->read_length - (old_start + old_len);

		iteration_context -> segment_texts[segi] = iteration_context->read_text + new_start;
		iteration_context -> segment_quals[segi] = iteration_context->qual_text + new_start;
	}
}

void LRMdo_one_voting_read_process_setres(LRMcontext_t * context, LRMread_iteration_context_t * iteration_context, LRMsegment_mapping_result_t * seg_result, int replace_index, LRMgene_vote_t *vote_table, int iii, int jjj, int this_seg_id){
	int x1;
	for(x1 = LRMSEGMENT_MAX_CANDIDATES-2; x1 >= replace_index; x1--)
		memcpy(seg_result->candidates + x1 + 1, seg_result->candidates + x1, sizeof(LRMsegment_mapping_candidate_t));
	
	seg_result->candidates[replace_index].first_base_position = vote_table -> pos[iii][jjj];
	seg_result->candidates[replace_index].indel_length_inside = vote_table -> current_indel_cursor[iii][jjj];
	seg_result->candidates[replace_index].confident_coverage_start = vote_table -> coverage_start[iii][jjj];
	seg_result->candidates[replace_index].confident_coverage_end = vote_table -> coverage_end[iii][jjj];
	seg_result->candidates[replace_index].votes = vote_table -> votes[iii][jjj];
	seg_result->candidates[replace_index].masks = vote_table -> masks[iii][jjj];

	if(0 && seg_result->candidates[replace_index].votes>=2){
		char postxt[100];
		LRMpos2txt(context, seg_result->candidates[replace_index].first_base_position , postxt);
		LRMprintf("REPLACE CANDIDATE %d : to %s (%s), V=%d   SEG=%d\n", replace_index, postxt, (seg_result->candidates[replace_index].masks & LRMIS_NEGATIVE_STRAND)?"NEG":"POS", seg_result->candidates[replace_index].votes, this_seg_id);
	}
	
	memcpy(seg_result->candidates[replace_index].indel_records, vote_table ->indel_recorder[iii][jjj], sizeof(short)*3*LRMMAX_INDEL_SECTIONS);
}

int LRMdo_one_voting_read_process_samechro(LRMcontext_t * context, unsigned int p1, unsigned int p2){
	char * chro_name1, *chro_name2;
	int chro_pos1, chro_pos2;
	LRMlocate_gene_position(context, p1, &chro_name1, & chro_pos1);
	LRMlocate_gene_position(context, p2, &chro_name2, & chro_pos2);
	
	return chro_name1 == chro_name2; // they can be compared in this way because they are pointers in the sam_bam_chromosome_list.
}


#define LRMseg_fetch_result(mapr, sid) ( (mapr) -> segment_results + ((iteration_context -> is_reversed == 0)?(sid):( iteration_context -> total_segments - sid - 1 ) ) )

void LRMdo_one_voting_read_process_votetab(LRMcontext_t * context, LRMthread_context_t * thread_context, LRMread_iteration_context_t * iteration_context, int this_seg_id){
	LRMread_mapping_result_t * mapping_result = context -> read_mapping_results + iteration_context -> read_no_in_chunk;
	LRMsegment_mapping_result_t * seg_result = LRMseg_fetch_result( mapping_result, this_seg_id );
	seg_result -> extracted_subreads = iteration_context -> extracted_subreads;
	
	//if(this_seg_id == 13) LRMprint_v(context, iteration_context, 2);

	int iii, jjj, hhh;
	for(iii=0; iii < LRMGENE_VOTE_TABLE_SIZE; iii++){
		for(jjj = 0; jjj < iteration_context -> vote_table.items[iii]; jjj++){
			if( iteration_context -> vote_table.votes [iii][jjj] < context -> min_voting_number  ||  iteration_context -> vote_table.votes [iii][jjj] <= seg_result -> candidates[LRMSEGMENT_MAX_CANDIDATES - 1].votes) continue;
			int replace_index = LRMSEGMENT_MAX_CANDIDATES - 1;
			int kkk;
			for(kkk = 0; kkk < LRMSEGMENT_MAX_CANDIDATES; kkk ++){
				if(seg_result->candidates[kkk].votes < iteration_context -> vote_table.votes [iii][jjj]){
					replace_index = kkk;
					break;
				}
			}
			LRMdo_one_voting_read_process_setres(context, iteration_context, seg_result, replace_index, &iteration_context -> vote_table, iii, jjj, this_seg_id);
		}
	}
	
	if(context -> do_junction_detection)
		for(hhh = 0; hhh < LRMSEGMENT_MAX_CANDIDATES; hhh++){

			if(0){
				char p1txt[100];
				LRMpos2txt(context, seg_result->candidates[hhh].first_base_position, p1txt);
				LRMprintf("process_votetab: [%d] votes=%d, pos=%s (%u)\n", hhh, seg_result->candidates[hhh].votes, p1txt, seg_result->candidates[hhh].first_base_position);
			}

			if(seg_result -> candidates[hhh].votes<1)break;
			if((seg_result -> candidates[hhh].masks & LRMIS_NEGATIVE_STRAND) != (iteration_context -> is_reversed ?  LRMIS_NEGATIVE_STRAND : 0)) continue;
			seg_result -> candidates[hhh].secondary_votes = 0;

			unsigned int best_secondary_half_pos = 0;
			int best_secondary_score = -1, best_secondary_split_point = -1, best_secondary_is_GT_AG = -1, best_secondary_votes = -1, best_left_offset_indels = 0;
			
			for(iii=0; iii < LRMGENE_VOTE_TABLE_SIZE; iii++){
				for(jjj = 0; jjj < iteration_context -> vote_table.items[iii]; jjj++){
					if(iteration_context -> vote_table.votes [iii][jjj] > seg_result -> candidates[hhh].votes || iteration_context -> vote_table.votes [iii][jjj] <1) continue;
					
					long long dist0 = seg_result->candidates[hhh].first_base_position;
					dist0 -= iteration_context -> vote_table.pos[iii][jjj];
					int is_junction_distance = abs(dist0) > 3 && abs(dist0) < context -> max_junction_distance;
					int is_same_chro = LRMdo_one_voting_read_process_samechro(context, seg_result->candidates[hhh].first_base_position, iteration_context -> vote_table.pos[iii][jjj]);
					//LRMprintf("TEST JUNCTION COND: %u ~ %u : DIST=%d, SAME=%d\n", seg_result->candidates[hhh].first_base_position, iteration_context -> vote_table.pos[iii][jjj], is_junction_distance, is_same_chro );
					if(is_junction_distance && is_same_chro){
						int this_split_point = -1, this_is_GT_AG = -1, left_indel_offset = 0;
						int indel_length_in_anchor = seg_result->candidates[hhh].indel_length_inside;
						int indel_length_in_secondary = iteration_context -> vote_table.current_indel_cursor[iii][jjj];
						int this_score = LRMdonor_score(context, thread_context, iteration_context, seg_result -> candidates + hhh, this_seg_id, iteration_context ->vote_table.pos[iii][jjj] , iteration_context ->vote_table.coverage_start[iii][jjj] , iteration_context -> vote_table.coverage_end[iii][jjj], indel_length_in_anchor, indel_length_in_secondary, & this_split_point, & this_is_GT_AG , &left_indel_offset);

						if(0 && this_score > 0){
							char pos1txt[100], pos2txt[100];
							LRMpos2txt(context, seg_result -> candidates[hhh].first_base_position, pos1txt);
							LRMpos2txt(context, iteration_context ->vote_table. pos[iii][jjj], pos2txt);
							LRMprintf("TEST JUNCTION SCORE CAND %d: %s ~ %s = %d, <?< %d ; VOTES=%d + %d\n", hhh, pos1txt, pos2txt, this_score, best_secondary_score, seg_result -> candidates[hhh].votes, iteration_context -> vote_table.votes [iii][jjj]);
						}
						
						if(this_score > best_secondary_score ){
							best_secondary_half_pos =  iteration_context ->vote_table. pos[iii][jjj];
							best_secondary_split_point = this_split_point;
							best_secondary_is_GT_AG = this_is_GT_AG;
							best_secondary_votes = iteration_context -> vote_table.votes [iii][jjj];
							best_secondary_score = this_score;
							best_left_offset_indels = left_indel_offset;
						}
					}
				}
			}

			if(best_secondary_score > 0){
				seg_result -> candidates[hhh].secondary_position = best_secondary_half_pos;
				seg_result -> candidates[hhh].secondary_votes = best_secondary_votes;
				seg_result -> candidates[hhh].junction_split_point = best_secondary_split_point;
				seg_result -> candidates[hhh].junction_is_GT_AG = best_secondary_is_GT_AG;
				seg_result -> candidates[hhh].junction_left_offset_indels = best_left_offset_indels;
			}
		}
	//END: if context -> do_junction_detection
}

void LRMdo_one_voting_read_segment_extraction(LRMcontext_t * context, LRMthread_context_t * thread_context, LRMread_iteration_context_t * iteration_context, int this_seg_id){
	int seg_len = iteration_context -> segment_lengths[this_seg_id];
	char * seg_qual = iteration_context -> segment_quals[this_seg_id];

	//LRMprintf("EXTR: seg %d\n", this_seg_id);

	iteration_context -> extracted_subreads = min(context -> max_subreads_per_segment ,(seg_len - 15 - context  -> current_index.index_gap)/context  -> current_index.index_gap + 1);
	float subread_gap = (seg_len - 15 - context  -> current_index.index_gap + 1)*1. / iteration_context -> extracted_subreads;
	int subr_i, pos_i;
	

	//LRMprintf("EXTSUB: %s [%d] LEN=%d\t" , iteration_context -> read_name, this_seg_id , seg_len);
	for(subr_i = 0; subr_i < iteration_context -> extracted_subreads; subr_i++){
		pos_i = subr_i * subread_gap;
		iteration_context -> subread_offsets[this_seg_id][subr_i]=pos_i;

		//#warning "FOR COMPARISON ONLY ===================="
		continue;

		int highest_qual = -1;
		int total_qual = 0;
		int search_end = pos_i + subread_gap - 1,  search_end_1 = pos_i + 15 + context  -> current_index.index_gap;

		for(; pos_i < search_end_1; pos_i++)
			total_qual += seg_qual[ pos_i ];
		highest_qual = total_qual;

		for(; pos_i < search_end; pos_i++){
			total_qual += seg_qual[ pos_i ];
			total_qual -= seg_qual[ pos_i -15 - context  -> current_index.index_gap];
			if(total_qual > highest_qual) {
				highest_qual = total_qual;
				iteration_context -> subread_offsets[this_seg_id][subr_i]=pos_i-14 - context  -> current_index.index_gap;
			}
		}
	//	LRMprintf("%d:%d\t", (int)(subr_i * subread_gap), iteration_context -> subread_offsets[this_seg_id][subr_i]);
	}
	//LRMprintf("\n");
	//LRMprintf("   %d len = %d reads (seg %d)\n", iteration_context -> segment_lengths[this_seg_id], iteration_context -> extracted_subreads, this_seg_id);
}

void LRMdo_one_voting_read_segment(LRMcontext_t * context, LRMthread_context_t * thread_context, LRMread_iteration_context_t * iteration_context, int this_seg_id){
	LRMinit_gene_vote((& iteration_context-> vote_table));

	char * seg_text = iteration_context -> segment_texts[this_seg_id];
	int seg_len = iteration_context -> segment_lengths[this_seg_id];

	LRMdo_one_voting_read_segment_extraction(context, thread_context,iteration_context , this_seg_id);

	int this_subread_no, this_gap_offset;
	for(this_subread_no=0; this_subread_no< iteration_context ->extracted_subreads;this_subread_no++){
		for(this_gap_offset=0; this_gap_offset<context  -> current_index.index_gap; this_gap_offset++){
			int this_subread_offset = this_gap_offset + iteration_context -> subread_offsets[this_seg_id][this_subread_no];
			
			char * subread_string = seg_text + this_subread_offset;


			LRMgehash_key_t subread_integer = LRMgenekey2int(subread_string);
			if(0){
				int cc = subread_string[16];
				subread_string[16]=0;
				LRMprintf("Extract Subread: %s\n", subread_string);
				subread_string[16]=cc;
			}

			int mm_offset =0 ;//(iteration_context -> read_length < 2000)? 1:0;
			//mm_offset =(iteration_context -> read_length < 500)?2:mm_offset;
			
			LRMgehash_go_tolerance(context, thread_context, iteration_context,& context->current_index, subread_integer , this_subread_offset, seg_len, iteration_context -> is_reversed, & iteration_context-> vote_table, context -> max_read_indel_length, this_subread_no, ( context -> max_mismatched_bases_in_subread + mm_offset ));
		}
	}

	if(0 && this_seg_id == 47){
		int cch = seg_text[ seg_len ];
		seg_text[ seg_len] = 0;
		LRMprintf("\nREAD %s [seg %d] STAGE %s : %.*s\n", iteration_context -> read_name, this_seg_id, iteration_context -> is_reversed?"NEG":"POS", seg_len, seg_text);
		LRMprint_v(context, iteration_context, 2);
		seg_text[ seg_len ] = cch;
	}
	LRMdo_one_voting_read_process_votetab(context, thread_context, iteration_context, this_seg_id);

	LRMread_mapping_result_t * mapping_result = context -> read_mapping_results + iteration_context -> read_no_in_chunk;
	LRMsegment_mapping_result_t * seg_result = LRMseg_fetch_result( mapping_result, this_seg_id );

	//if(iteration_context -> is_reversed)LRMprintf("MAPPED SEG VOTE[0]=%d\n", seg_result -> candidates[0].votes);
	if(iteration_context -> is_reversed && seg_result -> candidates[0].votes> 1) iteration_context -> mapped_segments ++;
}

int LRMfind_subread_end(int len, int total_subreads, int subread){
	return subread * 16;
}

void LRMdo_one_voting_insert_chro_events(LRMcontext_t * context, LRMthread_context_t * thread_context, LRMread_iteration_context_t * iteration_context, int seg_id){
	int x2;
	
	LRMread_mapping_result_t * mapping_result = context -> read_mapping_results + iteration_context -> read_no_in_chunk;
	LRMsegment_mapping_result_t * seg_result = LRMseg_fetch_result( mapping_result, seg_id );
	
	if(context -> do_junction_detection)
		for(x2 = 0; x2 < LRMSEGMENT_MAX_CANDIDATES; x2++){
			LRMsegment_mapping_candidate_t * cand_res = seg_result -> candidates + x2;
			//LRMprintf("TRY INSERT JUNCTION: [%d] , VOTE=%d, 2ndVOTE=%d\n", x2, cand_res -> votes, cand_res -> secondary_votes);
			if(cand_res -> secondary_votes > 0){
				LRMevent_t new_event;
				memset(&new_event, 0, sizeof(LRMevent_t));
				new_event.event_type = LRMEVENT_TYPE_JUNCTION;
				new_event.small_side = - 1 + min(cand_res->first_base_position, cand_res->secondary_position) + cand_res->junction_split_point + cand_res->junction_left_offset_indels;
				new_event.large_side = max(cand_res->first_base_position, cand_res->secondary_position) + cand_res->junction_split_point;
				new_event.masks = cand_res-> junction_is_GT_AG?LRM_EVENT_IS_GT_AT_DONOR:0;

				//LRMprintf("INSERT JUNCTION EVENT: %u~%u\n", new_event.small_side, new_event.large_side);
				
				int retv = LRMchro_event_new(context, thread_context, iteration_context, &new_event);
				if(retv) return;
			}
		}
	//END: if context -> do_junction_detection
	
	// find and insert indels
	for(x2 = 0; x2 < LRMSEGMENT_MAX_CANDIDATES; x2++){
		LRMsegment_mapping_candidate_t * cand_res = seg_result -> candidates + x2;
		int last_correct_subread = cand_res -> indel_records[1]-1, last_indel = 0;
		int indel_i = 0;

		if(0){
			char ptxt[100];
			LRMpos2txt(context, cand_res -> first_base_position, ptxt);
			LRMprintf("CANDIDATE %s [SEG-%d][%d] START AT %s\n", iteration_context -> read_name, seg_id, x2, ptxt);
		}

		for(indel_i=1; indel_i<LRMMAX_INDEL_SECTIONS; indel_i++){
			//LRMprintf("CANDIDATE   INDEL[%d] = %d %d %d\n", indel_i,  cand_res -> indel_records[indel_i*3],  cand_res -> indel_records[indel_i*3+1],  cand_res -> indel_records[indel_i*3+2] );
			if( cand_res -> indel_records[indel_i*3]<1)break;

			int next_correct_subread = cand_res -> indel_records[indel_i*3] - 1;
			int last_correct_base = iteration_context -> subread_offsets[seg_id][last_correct_subread] - 10;
			int first_correct_base = iteration_context -> subread_offsets[seg_id][next_correct_subread]+ 13;

			int expected_indels_in_region=cand_res->indel_records[indel_i*3+2] - last_indel;
			last_correct_base = max(0, last_correct_base);
			last_correct_base = min(iteration_context -> read_length-1, last_correct_base);
			first_correct_base = min(first_correct_base, iteration_context -> read_length-1);
			first_correct_base = max(0, first_correct_base);
			first_correct_base = max(first_correct_base, last_correct_base);
			last_correct_subread = cand_res->indel_records[indel_i*3+1]-1;

			//LRMprintf("CANDIDATE   EXPINDEL=%d  , GAP_BASES = %d, %d\n", expected_indels_in_region, first_correct_base, last_correct_base);

			if(abs(expected_indels_in_region) <= context -> max_read_indel_length && first_correct_base - last_correct_base > 1){
				int currently_reversed = 1;
				char * corrected_read= iteration_context -> segment_texts[seg_id];
				
				if(( (cand_res -> masks & LRMIS_NEGATIVE_STRAND ) == 0 &&  currently_reversed) || 
  				  ((  cand_res -> masks & LRMIS_NEGATIVE_STRAND ) != 0 && !currently_reversed)) {
					LRMreverse_read( corrected_read , iteration_context -> segment_lengths[seg_id] );
					currently_reversed = !currently_reversed;
				}
				unsigned int chro_cursor = cand_res -> first_base_position + last_correct_base + last_indel, total_mismatched;
				int move_i, indel_movements = LRMindel_dynamic_search(context, thread_context, - expected_indels_in_region /* inversed definition */ , chro_cursor, corrected_read,  last_correct_base, first_correct_base, &total_mismatched, 0, iteration_context -> read_name);
				//LRMprintf("%s from %d MOVES=%s\n", (cand_res -> masks & LRMIS_NEGATIVE_STRAND )?"REV":"STD", last_correct_base , indel_movement_buff);

				if(total_mismatched <= 1 || (total_mismatched <= 2 && first_correct_base - last_correct_base > 30) || (total_mismatched <= 10 && first_correct_base - last_correct_base > 100)){
					int current_chr=-1, current_len = 0;
					for(move_i = 0; move_i < 1+ indel_movements; move_i++){
						int nch = thread_context -> dynamic_programming_movement_buffer[move_i];
						nch = (nch=='X')?'M':nch;
						if(current_chr!=nch){
							if(current_chr>0 && current_chr != 'M'){
								LRMevent_t new_event;
								memset(&new_event, 0, sizeof(LRMevent_t));
								new_event.indel_length = current_chr == 'D' ? current_len : - current_len;
								new_event.event_type = LRMEVENT_TYPE_INDEL;
								new_event.large_side = chro_cursor;
								new_event.small_side = current_chr == 'D' ? chro_cursor - current_len - 1 : (chro_cursor - 1);
								new_event.masks = cand_res-> junction_is_GT_AG?LRM_EVENT_IS_GT_AT_DONOR:0;
								
								if(0){
									char p1txt[100], p2txt[100], p0txt[100];
									LRMpos2txt(context, new_event.small_side , p1txt);
									LRMpos2txt(context, new_event.large_side , p2txt);
									LRMpos2txt(context, cand_res -> first_base_position , p0txt);
									if(1|| ( new_event.small_side >=  197828782 - 3 && new_event.small_side <= 197828782 + 5)){
										LRMprintf("\nINSERT INDEL EVENT FROM %s: %s~%s ; LEN=%d\n", iteration_context -> read_name, p1txt, p2txt, new_event.indel_length);
										LRMprintf("INSERT INDEL AT %s + %d + %d\n" , p0txt, last_correct_base, last_indel);
										LRMprintf("%s MOVES=%s\n\n", (cand_res -> masks & LRMIS_NEGATIVE_STRAND )?"REV":"STD" , thread_context -> dynamic_programming_movement_buffer);
									}
								}

								int retv = LRMchro_event_new(context, thread_context, iteration_context, &new_event);
								if(retv) return;
							}
							current_chr = nch;
							current_len = 0;
						}
						current_len++;
						if(nch !='I') chro_cursor ++;
					}
				}
				if(currently_reversed == 0) LRMreverse_read( corrected_read , iteration_context -> segment_lengths[seg_id] );
			}
			last_indel = cand_res->indel_records[indel_i*3+2];
		}
	}
}

void LRMdo_one_voting_read(LRMcontext_t * context, LRMthread_context_t * thread_context, LRMread_iteration_context_t * iteration_context){
	LRMread_mapping_result_t * mapping_result = context -> read_mapping_results + iteration_context -> read_no_in_chunk;
	if(iteration_context -> total_segments < 1) return;
	
	mapping_result -> segment_results = malloc(sizeof(LRMsegment_mapping_result_t) * iteration_context->total_segments);

	for( iteration_context -> added_mismatch_allowed_in_subread = 0;  iteration_context -> added_mismatch_allowed_in_subread < 1;  iteration_context -> added_mismatch_allowed_in_subread ++){
		iteration_context -> mapped_segments = 0;
		memset(mapping_result -> segment_results, 0, sizeof(LRMsegment_mapping_result_t) * iteration_context->total_segments);

		for(iteration_context->is_reversed = 0; iteration_context->is_reversed< 2; iteration_context->is_reversed++){
			int seg_id;
			for(seg_id=0; seg_id<iteration_context -> total_segments; seg_id++){
				LRMdo_one_voting_read_segment(context, thread_context, iteration_context, seg_id);

				if(0)if(iteration_context->is_reversed) LRMdo_one_voting_insert_chro_events(context, thread_context, iteration_context, seg_id);
			}
			
			if(0 == iteration_context->is_reversed) LRMreverse_read_and_qual(context, thread_context, iteration_context);
		}

	//	LRMprintf("Trying MM Offset = %d ; mapped segs = %d >= %d\n",iteration_context -> added_mismatch_allowed_in_subread , iteration_context -> mapped_segments ,  max(3, iteration_context -> total_segments / 10));
		if(iteration_context -> mapped_segments >= 1+0* max(3, iteration_context -> total_segments / 10))break;
		else LRMreverse_read_and_qual(context, thread_context, iteration_context);
	}
}


typedef struct{
	unsigned int read_head_pos;
	unsigned int votes;
	short masks;
	int segment_number;
	int segment_id[ LRMMAX_READ_LENGTH / LRMSEGMENT_MIN_DIST +1 ];
	int realign_cand_id[ LRMMAX_READ_LENGTH / LRMSEGMENT_MIN_DIST +1 ];
} LRMread_final_candidate_t;

void LRMfix_cigar(LRMcontext_t * context, LRMthread_context_t *  thread_context,  LRMread_iteration_context_t * iteration_context, char * cigar){
	int ci, nch;
	unsigned int tmpi = 0;
	unsigned int lastlen = 0;
	int lastopt = 0, outi = 0;
	int read_cursor = 0;
	for(ci=0; 0!=(nch=cigar[ci]); ci++){
		if(nch <= '9' && nch >= '0'){
			tmpi = 10*tmpi +(nch-'0');
		}else if(nch >= 'A' && nch <= 'Z'){
			int nnch = cigar[ci+1];
			if(tmpi>0) outi+=sprintf(cigar+outi, "%u%c", tmpi, nch);
			cigar[ci+1] = nnch;
			tmpi = 0;
		}
	}

	outi = 0;
	tmpi = 0;
	for(ci=0; 0!=(nch=cigar[ci]); ci++){
		if(nch <= '9' && nch >= '0'){
			tmpi = 10*tmpi +(nch-'0');
		}else{
			if(nch >= 'A' && nch <= 'Z'){
				if(nch != lastopt){
					if(lastlen>0){

						if(1)if((lastopt == 'D' && nch == 'I')||(lastopt == 'I' && nch == 'D')){
							if(lastlen > tmpi){
								lastlen -= tmpi;
								nch = 'M';
							}else if(lastlen < tmpi){
								lastopt = 'M';
								tmpi -= lastlen;
							}else{
								nch = 'M';
								lastlen = 0;
							}
						}

						if(lastopt == 'S' || lastopt == 'M' || lastopt == 'I'){
							lastlen = min(lastlen, iteration_context -> read_length - read_cursor);
							read_cursor += lastlen;
						}

						if(lastlen>0)outi+=sprintf(cigar+outi, "%u%c", lastlen, lastopt);
					}

					lastopt = nch;
					lastlen = 0;
				}
				lastlen += tmpi;
			}
			tmpi = 0;
		}
	}

	if(lastlen>0){
		//LRMprintf("LASTLEN=MIN   %u   %d - %u\n", lastlen, iteration_context -> read_length ,  read_cursor);
		if(lastopt == 'S' || lastopt == 'M' || lastopt == 'I')
			lastlen = min(lastlen, iteration_context -> read_length - read_cursor);
			
		if(lastlen>0)outi+=sprintf(cigar+outi, "%u%c", lastlen, lastopt);
	}
}

long long LRMcalculate_written_chro_pos(LRMcontext_t * context, LRMthread_context_t * thread_context, LRMread_iteration_context_t * iteration_context, int written_read_cursor, int start_read_cursor, char * cigar, unsigned int start_chro_pos){
	if(written_read_cursor <=start_read_cursor) return start_chro_pos;
	int tmpi = 0;
	int ci=0, nch;

	while(0!=(nch = cigar[ci++])){
		if(nch <='9' && nch >= '0') tmpi = tmpi*10+(nch-'0');
		else{
			if(nch == 'M'||nch == 'S' ||nch == 'N'||nch == 'D') start_chro_pos += tmpi;
			if(nch == 'M'||nch == 'S' ||nch == 'I') start_read_cursor += tmpi;
			if(start_read_cursor >=written_read_cursor) {
				if(nch == 'I') return start_chro_pos;
				else return start_chro_pos - (start_read_cursor - written_read_cursor);
			}
			
			tmpi = 0;
		}
	}

	//if( start_read_cursor == written_read_cursor ) return start_chro_pos;
	return -1;
}

int LRMmoves_to_cigar(LRMcontext_t * context, LRMthread_context_t * thread_context, LRMread_iteration_context_t * iteration_context, long long * cigar_chro_cursor, long long * cigar_read_cursor, int * target_cigar_ptr, long long *last_written_cigar_chro, int * correct_mapping, int moves, int * cigar_opts, int * last_written_cigar_read, int * softclipping_bases, int * matched_bases, int * mismatched_bases, int align_i){
	int tmpi = 0, move_i;
	char * target_cigar = iteration_context -> merged_cigar[align_i];
	//LRMprintf("MOVES=%d\n", moves);
	for(move_i = 0; move_i < moves; move_i++){
		char nch = thread_context -> dynamic_programming_indel_movement_buf[move_i];
		if(nch == '.') continue;
		if(matched_bases && nch == 'M') (*matched_bases)++;
		if(mismatched_bases && nch == 'X') (*mismatched_bases)++;
		nch =(nch == 'X'?'M':nch);
		char nnh = thread_context -> dynamic_programming_indel_movement_buf[move_i + 1];
		nnh =(nnh == 'X'?'M':nnh);
		tmpi ++;
		if(nnh != nch){
			if(nch == 'M' || nch == 'D' || nch == 'S') (*cigar_chro_cursor) += tmpi;
			if(nch == 'M' || nch == 'I' || nch == 'S') (*cigar_read_cursor) += tmpi;
			if(softclipping_bases&& nch == 'S') (*softclipping_bases) += tmpi;

			if((*cigar_opts) < context -> max_cigar_opts_in_read - 5 && (*target_cigar_ptr) < LRMMERGE_CIGAR_SIZE - 70){
				(*target_cigar_ptr) +=  snprintf( target_cigar + (*target_cigar_ptr), LRMMERGE_CIGAR_SIZE - 40 - (*target_cigar_ptr), "%d%c", tmpi, nch);
				*last_written_cigar_read = (*cigar_read_cursor);
				*last_written_cigar_chro = max((*last_written_cigar_chro), (*cigar_chro_cursor));

				if(nch == 'M') (*correct_mapping)=1;
			}
			(*cigar_opts) ++;
			tmpi = 0;
		}
	}
	return 0;
}

#define LRM_USE_CIGAR_RESCURE 1
int cluster_debug = 0;

int sort_segvote_compare(void * a, int l, int r){
	void ** vsort = a;
	unsigned int * segment_vote_list = vsort[2];
	if(segment_vote_list[l] > segment_vote_list[r]) return 1;
	if(segment_vote_list[l] < segment_vote_list[r]) return -1;
	return 0;
}

void sort_segvote_exchange(void * a, int l, int r){
	unsigned int x,t;
	void ** vsort = a;
	for(x=0;x<4;x++){
		unsigned int * aa = vsort[x];
		if(!aa) continue;
		t = aa[l];
		aa[l] = aa[r];
		aa[r] = t;
	}
}

void sort_segvote_merge(void * arr, int start, int items, int items2){
	void ** arrr = (void **) arr;
	unsigned int * m1 = malloc(sizeof(int) * (items+items2)), *m2 = malloc(sizeof(int) * (items+items2)), *m3 = malloc(sizeof(int) * (items+items2)), *m4 = malloc(sizeof(int) * (items+items2));
	unsigned int * i1 = arrr[0], * i2 = arrr[1], * i3 = arrr[2], * i4 = arrr[3];

	int read_1_ptr = start, read_2_ptr = start+items, write_ptr;
	for(write_ptr=0; write_ptr<items+items2; write_ptr++){
		if((read_1_ptr >= start+items)||(read_2_ptr < start+items+items2 && sort_segvote_compare(arr, read_1_ptr, read_2_ptr) > 0)){
			m1[write_ptr] = i1[read_2_ptr];
			m2[write_ptr] = i2[read_2_ptr];
			m3[write_ptr] = i3[read_2_ptr];
			if(i4)m4[write_ptr] = i4[read_2_ptr];
			read_2_ptr ++;
		}else{
			m1[write_ptr] = i1[read_1_ptr];
			m2[write_ptr] = i2[read_1_ptr];
			m3[write_ptr] = i3[read_1_ptr];
			if(i4)m4[write_ptr] = i4[read_1_ptr];
			read_1_ptr ++;
		}
	}
	memcpy(i1 + start, m1, sizeof(int) * (items+items2));
	memcpy(i2 + start, m2, sizeof(int) * (items+items2));
	memcpy(i3 + start, m3, sizeof(int) * (items+items2));
	if(i4)memcpy(i4 + start, m4, sizeof(int) * (items+items2));

	free(m1);
	free(m2);
	free(m3);
	free(m4);
}

int LRMfind_leftS_size(char * c){
	int tmpi=0,x=0,nch;
	while(0!=(nch = c[x++] )){
		if(isdigit(nch)){
			tmpi=tmpi*10+(nch-'0');
		}else{
			if(nch =='S') return tmpi;
			return 0;
		}
	}
	return -1;
}

#define LRM_MERGE_SEEDS 300
int reconcile_debug = 0;
int LRMread_final_result_merge(LRMcontext_t * context, LRMthread_context_t * thread_context, LRMread_iteration_context_t * iteration_context, LRMread_mapping_result_t * read_res){
	int x1, x2, x3, correct_mapping = 0;
	long long this_chromosome_end = 0;
	char * this_chromosome_name = NULL;
	read_res -> best_candidate = NULL;

	unsigned int * segment_id_list, * segment_cand_id_list, * segment_vote_list;
	unsigned int * segment_linear_list;
	int segment_id_list_size = 100;
	int segment_id_list_no = 0;

	segment_id_list = malloc(sizeof(int) * segment_id_list_size);
	segment_cand_id_list = malloc(sizeof(int) * segment_id_list_size);
	segment_vote_list = malloc(sizeof(int) * segment_id_list_size);
	segment_linear_list = malloc(sizeof(int) * segment_id_list_size);

	for(x1=0;x1<iteration_context->total_segments;x1++){
		for(x2 = 0; x2 < LRMMERGING_MAX_CANDIDATES; x2++){
			unsigned int seg_cand_votes = iteration_context -> segment_best_votes[x1][x2];
			if(seg_cand_votes < 1) break;
			if(segment_id_list_no >= segment_id_list_size - 1){
				segment_id_list_size = segment_id_list_size * 14/10;
				segment_id_list = realloc(segment_id_list,sizeof(int) * segment_id_list_size);
				segment_cand_id_list = realloc(segment_cand_id_list,sizeof(int) * segment_id_list_size);
				segment_vote_list = realloc(segment_vote_list,sizeof(int) * segment_id_list_size);
				segment_linear_list = realloc(segment_linear_list,sizeof(int) * segment_id_list_size);
			}

			segment_id_list[segment_id_list_no] = x1;
			segment_cand_id_list[segment_id_list_no] = x2;
			segment_vote_list[segment_id_list_no] = seg_cand_votes;
			segment_linear_list[segment_id_list_no] = iteration_context -> segment_best_pos[x1][x2] + LRMfind_leftS_size(iteration_context -> segment_cigars[x1][x2]);
			segment_id_list_no ++;

			if(reconcile_debug){
				char ptxt[100];
				int read_pos = iteration_context -> segment_texts[x1] - iteration_context -> read_text + LRMfind_leftS_size(iteration_context -> segment_cigars[x1][x2]);
				if(iteration_context -> segment_best_masks[x1][x2] & LRMIS_NEGATIVE_STRAND) read_pos += iteration_context -> segment_lengths[x1];
				LRMpos2txt(context, segment_linear_list[segment_id_list_no-1], ptxt);
				LRMprintf("INIT_LOC of SEG %d, CAND %d: strand: %s read_pos: %ld + %d, chro_pos: %s\tV=%d\n",x1,x2,(iteration_context -> segment_best_masks[x1][x2] & LRMIS_NEGATIVE_STRAND)?"NEG":"POS" , iteration_context -> segment_texts[x1] - iteration_context -> read_text, LRMfind_leftS_size(iteration_context -> segment_cigars[x1][x2]) ,ptxt, seg_cand_votes);
			}
		}
	}

	void * vsort[4];
	vsort[0] = segment_id_list;
	vsort[1] = segment_cand_id_list;
	vsort[2] = segment_vote_list;
	vsort[3] = segment_linear_list;
	merge_sort(vsort, segment_id_list_no, sort_segvote_compare, sort_segvote_exchange, sort_segvote_merge);
	free(segment_vote_list);
	segment_vote_list = NULL;

	unsigned int topseg_id_list[LRM_MERGE_SEEDS];
	unsigned int topseg_cand_id_list[LRM_MERGE_SEEDS];
	unsigned int topseg_pos_list[LRM_MERGE_SEEDS];
	int topseg_masks[LRM_MERGE_SEEDS];
	int topseg_no = 0;

	for(; topseg_no < LRM_MERGE_SEEDS; topseg_no++){
		if ( topseg_no > segment_id_list_no - 1)break;
		topseg_id_list[ topseg_no ] = segment_id_list[segment_id_list_no - 1 - topseg_no];
		topseg_cand_id_list[ topseg_no ] = segment_cand_id_list[segment_id_list_no - 1 - topseg_no];
		topseg_pos_list[topseg_no] = segment_linear_list[segment_id_list_no - 1 - topseg_no];
	}

	vsort[2] = segment_linear_list;
	vsort[3] = NULL;
	merge_sort(vsort, segment_id_list_no, sort_segvote_compare, sort_segvote_exchange, sort_segvote_merge);

	if(reconcile_debug) for(x2 = 0; x2 < segment_id_list_no; x2++){
		char ptxt[100];
		LRMpos2txt(context, segment_linear_list[x2], ptxt);
		LRMprintf("ALL_LOC: %s\tV=%d\n", ptxt, iteration_context -> segment_best_votes[segment_id_list[x2]][ segment_cand_id_list[x2]]);
	}

	int best_align_i;
	int trying_aligns = max(2, context -> max_best_alignments);

	HashTable * candarray_used_segment_id = HashTableCreate(100);
	HashTable * candarray_used_cand_id = HashTableCreate(100);

	for(best_align_i = 0; best_align_i<trying_aligns; best_align_i++){
	
		ArrayList * candarray_going_large_segid[LRM_MERGE_SEEDS];
		ArrayList * candarray_going_large_candid[LRM_MERGE_SEEDS];
		ArrayList * candarray_going_small_segid[LRM_MERGE_SEEDS];
		ArrayList * candarray_going_small_candid[LRM_MERGE_SEEDS];
		int candarray_votes[LRM_MERGE_SEEDS]; 
		int best_best_votes[trying_aligns] , best_best_chains[trying_aligns];
		memset(best_best_votes,0,sizeof(int)*trying_aligns);
	
		for(x1 = 0; x1 < topseg_no; x1++){
			unsigned int left_max_pos = topseg_pos_list[x1] - max(500000, 2*iteration_context -> read_length);
			if(topseg_pos_list[x1] < max(500000, 2*iteration_context -> read_length)) left_max_pos = 0;
			unsigned int right_max_pos = topseg_pos_list[x1] + max(500000, 2*iteration_context -> read_length);
	
			int left_index = binary_search_less_equal(segment_linear_list, segment_id_list_no, left_max_pos)-1;
			left_index = max(0,left_index);
			int right_index = binary_search_less_equal(segment_linear_list, segment_id_list_no, right_max_pos)+1;
			right_index = min(segment_id_list_no, right_index);
	
			if(left_index>0) assert(segment_linear_list[left_index] < topseg_pos_list[x1] );
			if(right_index < segment_id_list_no){
				if(segment_linear_list[right_index] < topseg_pos_list[x1]) LRMprintf("ERROR_RIGHT: rmax=%d, total=%d, %u < %u\n", right_index, segment_id_list_no, segment_linear_list[right_index] , topseg_pos_list[x1]);
				assert( segment_linear_list[right_index] > topseg_pos_list[x1] );
			}
	
			int this_seed_cand_index = -1, going_large_cand_index;
			for(x2 = left_index; x2 < right_index; x2++){
				if(reconcile_debug){
					char p1txt[100], p2txt[100];
					LRMpos2txt(context, topseg_pos_list[x1],p1txt);
					LRMpos2txt(context, segment_linear_list[x2],p2txt);
					LRMprintf("SEED[%d] :%s\tTEST:%s\n", x1, p1txt, p2txt);
				}
				if(reconcile_debug) if(x2+1 < segment_id_list_no){
					if(segment_linear_list[x2]>segment_linear_list[x2+1])LRMprintf("ERROR ORDER: x2=%d, %u > %u\n",x2, segment_linear_list[x2], segment_linear_list[x2+1]);
					assert( segment_linear_list[x2] <= segment_linear_list[x2+1]);
				}
				if(segment_id_list[x2] == topseg_id_list[x1] && segment_cand_id_list[x2] == topseg_cand_id_list[x1])
					this_seed_cand_index = x2;
			}

			assert(this_seed_cand_index >=0);
	
			candarray_going_large_segid[x1] = ArrayListCreate(10);
			candarray_going_large_candid[x1] = ArrayListCreate(10);
			candarray_going_small_segid[x1] = ArrayListCreate(10);
			candarray_going_small_candid[x1] = ArrayListCreate(10);

			if(0){
				char p1txt[100];
				LRMpos2txt(context, topseg_pos_list[x1],p1txt);
				if(HashTableGet(candarray_used_segment_id, NULL + 1 + segment_id_list[this_seed_cand_index]) && HashTableGet(candarray_used_cand_id, NULL + 1 + segment_cand_id_list[this_seed_cand_index]))
					LRMprintf("IGNORE #%d : %s\n", x1, p1txt);
				else
					LRMprintf("ACCEPT #%d : %s\n", x1, p1txt);
			}

			if(HashTableGet(candarray_used_segment_id, NULL + 1 + segment_id_list[this_seed_cand_index]) && HashTableGet(candarray_used_cand_id, NULL + 1 + segment_cand_id_list[this_seed_cand_index]))
				continue;

			candarray_votes[x1]=iteration_context -> segment_best_votes[segment_id_list[this_seed_cand_index]][ segment_cand_id_list[this_seed_cand_index]];
	
			for(going_large_cand_index=0; going_large_cand_index<2; going_large_cand_index++){
				int current_cand_index = this_seed_cand_index;
				int x2_delta = going_large_cand_index?1:-1;
				if(this_seed_cand_index == left_index && (!going_large_cand_index))continue;
				if(this_seed_cand_index == right_index && going_large_cand_index)continue;
	
				for(x2 = this_seed_cand_index + x2_delta; x2!= left_index -1 && x2 != right_index ; x2 += x2_delta){
					int test_cand_seg_id = segment_id_list[x2];
					int test_cand_cand_id = segment_cand_id_list[x2];
					char * test_cand_cigar = iteration_context -> segment_cigars[test_cand_seg_id][test_cand_cand_id];
					int test_cand_read_pos = iteration_context -> segment_texts[test_cand_seg_id] - iteration_context -> read_text;
				//	LRMprintf("ROUND %d , GV: %s\n" , best_align_i, test_cand_cigar);
					test_cand_read_pos +=  LRMfind_leftS_size(test_cand_cigar);
					unsigned int test_cand_linear_location = segment_linear_list[x2];
					int test_cand_masks = iteration_context -> segment_best_masks[test_cand_seg_id][test_cand_cand_id];
	
					int current_cand_seg_id = segment_id_list[current_cand_index];
					int current_cand_cand_id = segment_cand_id_list[current_cand_index];
					int current_cand_read_pos = iteration_context -> segment_texts[current_cand_seg_id] - iteration_context -> read_text + LRMfind_leftS_size(iteration_context -> segment_cigars[current_cand_seg_id][current_cand_cand_id]);
					unsigned int current_cand_read_chro = segment_linear_list[current_cand_index];
					int current_cand_masks = iteration_context -> segment_best_masks[current_cand_seg_id][current_cand_cand_id];
					if(current_cand_index == this_seed_cand_index) topseg_masks[x1] = current_cand_masks;
	
					if( (test_cand_masks & LRMIS_NEGATIVE_STRAND) != (current_cand_masks & LRMIS_NEGATIVE_STRAND) ) continue;
					if(current_cand_seg_id == test_cand_seg_id) continue;
	
					ArrayList * candarray_going_segid = going_large_cand_index?candarray_going_large_segid[x1]:candarray_going_small_segid[x1];
					ArrayList * candarray_going_candid = going_large_cand_index?candarray_going_large_candid[x1]:candarray_going_small_candid[x1];
	
					if(   going_large_cand_index  && (current_cand_masks & LRMIS_NEGATIVE_STRAND)  && test_cand_seg_id >=  segment_id_list[this_seed_cand_index]) continue;
					if( (!going_large_cand_index) && (current_cand_masks & LRMIS_NEGATIVE_STRAND)  && test_cand_seg_id <=  segment_id_list[this_seed_cand_index]) continue;
					if(   going_large_cand_index  && 0==(current_cand_masks & LRMIS_NEGATIVE_STRAND)  && test_cand_seg_id <=  segment_id_list[this_seed_cand_index]) continue;
					if( (!going_large_cand_index) && 0==(current_cand_masks & LRMIS_NEGATIVE_STRAND)  && test_cand_seg_id >=  segment_id_list[this_seed_cand_index]) continue;
	
					int is_repeated = 0;
					for(x3 = 0; x3 < candarray_going_segid -> numOfElements; x3++){
						int cand_oldid = ArrayListGet(candarray_going_segid, x3) - NULL - 1;
						if(cand_oldid == test_cand_seg_id) is_repeated = 1;
					}
					if(is_repeated) continue;
	
					int move_on_read = test_cand_read_pos - current_cand_read_pos;
					long long int move_on_chro = test_cand_linear_location;
					move_on_chro -= current_cand_read_chro;
	
					if(reconcile_debug){
						char p1txt[100], p2txt[100];
						LRMpos2txt(context, current_cand_read_chro , p1txt);
						LRMpos2txt(context, test_cand_linear_location , p2txt);
						if(this_seed_cand_index == 324)LRMprintf("Trying Curr=%s (seg %d), Test=%s (seg %d), MoveChr=%lld, MoveRead=%d, current_cand_masks=%d, going_large_cand_index=%d\n", p1txt, current_cand_seg_id, p2txt, x2, move_on_chro, move_on_read, current_cand_masks, going_large_cand_index);
					}
	
					if(abs(move_on_read)<3 || abs(move_on_chro)<3) continue;
	
					if(1){
						int segid_diff = test_cand_seg_id - current_cand_seg_id;
						if(   going_large_cand_index  && (current_cand_masks & LRMIS_NEGATIVE_STRAND)  && segid_diff >= 0) continue;
						if( (!going_large_cand_index) && (current_cand_masks & LRMIS_NEGATIVE_STRAND)  && segid_diff <= 0) continue;
	
						if(   going_large_cand_index  && 0==(current_cand_masks & LRMIS_NEGATIVE_STRAND)  && segid_diff <= 0) continue;
						if( (!going_large_cand_index) && 0==(current_cand_masks & LRMIS_NEGATIVE_STRAND)  && segid_diff >= 0) continue;
					}
	
					if(   going_large_cand_index  && (current_cand_masks & LRMIS_NEGATIVE_STRAND)  && move_on_read >=0 ) continue;
					if((! going_large_cand_index) && (current_cand_masks & LRMIS_NEGATIVE_STRAND)  && move_on_read <=0 ) continue;
	
					if(   going_large_cand_index  && 0==(current_cand_masks & LRMIS_NEGATIVE_STRAND)  && move_on_read <=0 ) continue;
					if((! going_large_cand_index) && 0==(current_cand_masks & LRMIS_NEGATIVE_STRAND)  && move_on_read >=0 ) continue;
	
					int allowed_differential = max(abs(move_on_read), abs(move_on_chro)) *4 / 5;
					allowed_differential = max(160, allowed_differential);
	
					if(abs(move_on_chro) > abs(move_on_read) + allowed_differential || abs(move_on_chro) < abs(move_on_read) - allowed_differential) continue;
					if(abs(move_on_chro) > context -> result_merge_tolerance) continue;
	
					ArrayListPush(candarray_going_segid, NULL+1+test_cand_seg_id);
					ArrayListPush(candarray_going_candid, NULL+1+test_cand_cand_id);

					HashTablePut(candarray_used_segment_id, NULL+1+test_cand_seg_id, NULL+1);
					HashTablePut(candarray_used_cand_id, NULL+1+test_cand_cand_id, NULL+1);

					if(reconcile_debug){
						char ptxt[100];
						LRMpos2txt(context, test_cand_linear_location, ptxt);
						LRMprintf("    Added %s; new V = %d + %d\n", ptxt, iteration_context -> segment_best_votes[test_cand_seg_id][test_cand_cand_id], candarray_votes[x1]);
					}
					candarray_votes[x1] += iteration_context -> segment_best_votes[test_cand_seg_id][test_cand_cand_id];

					int replace_index = -1, repeat_index = -1;
					if(candarray_votes[x1] > best_best_votes[trying_aligns-1]){

						for(x3 = 0 ; x3 < trying_aligns; x3++){
							if(best_best_chains[x3] == x1){
								repeat_index = x3;
								break;
							}
						}

						if(repeat_index >=0){
							for(x3 = repeat_index; x3 < trying_aligns-1; x3++){
								best_best_votes[x3] = best_best_votes[x3 +1];
								best_best_chains[x3] = best_best_chains[x3 +1];
							}

							best_best_votes[trying_aligns-1] = 0;
							best_best_chains[trying_aligns-1] = 0;
						}

						for(x3 = 0 ; x3 < trying_aligns; x3++){
							if(best_best_votes[x3] < candarray_votes[x1]){
								replace_index = x3;
								break;
							}
						}
						if(replace_index>=0){
							for(x3 = trying_aligns-1; x3 > replace_index; x3--){
								best_best_votes[x3] = best_best_votes[x3 -1];
								best_best_chains[x3] = best_best_chains[x3 -1];
							}

							best_best_votes[replace_index] = candarray_votes[x1];
							best_best_chains[replace_index] = x1;
						}
					}
					current_cand_index = x2;
				}
			}
		}
	
		if(reconcile_debug){
			LRMprintf("\nREAD LEN = %d\n",  iteration_context -> read_length);
			for(x1 = 0; x1 < topseg_no; x1++){
				LRMprintf("CHAIN (%d) %s: votes=%d, segs=%ld + %ld + 1\n", x1,(topseg_masks[x1]&LRMIS_NEGATIVE_STRAND)?"NEG":"POS", candarray_votes[x1], candarray_going_large_segid[x1]->numOfElements , candarray_going_small_segid[x1]->numOfElements);
			}
		}

		int best_best_chain = -1;
		if(best_best_votes[best_align_i] >0)
			best_best_chain = best_best_chains[best_align_i];
	
		LRMread_final_candidate_t * best_cand_rec = NULL;
		if(best_best_chain >= 0){
			if(reconcile_debug)LRMprintf("==========================\nBEST_BEST chain #%d, seed index: %d\n", best_best_chain, topseg_id_list[best_best_chain]);

			ArrayList * resarr_left_segid = (topseg_masks[best_best_chain]  & LRMIS_NEGATIVE_STRAND) ?candarray_going_large_segid[best_best_chain]:candarray_going_small_segid[best_best_chain];
			ArrayList * resarr_left_candid = (topseg_masks[best_best_chain]  & LRMIS_NEGATIVE_STRAND) ?candarray_going_large_candid[best_best_chain]:candarray_going_small_candid[best_best_chain];
		
			ArrayList * resarr_right_segid = (topseg_masks[best_best_chain]  & LRMIS_NEGATIVE_STRAND) ?candarray_going_small_segid[best_best_chain]:candarray_going_large_segid[best_best_chain];
			ArrayList * resarr_right_candid = (topseg_masks[best_best_chain]  & LRMIS_NEGATIVE_STRAND) ?candarray_going_small_candid[best_best_chain]:candarray_going_large_candid[best_best_chain];
		
			best_cand_rec = malloc(sizeof(LRMread_final_candidate_t));
			for(x2 = 0 ; x2 < resarr_left_segid -> numOfElements + 1 + resarr_right_candid -> numOfElements; x2++){
				ArrayList * mesegid = NULL, *mecandid =  NULL;
				int meindex = -1;
				if(x2 < resarr_left_segid -> numOfElements){
					mesegid = resarr_left_segid;
					mecandid = resarr_left_candid;
					meindex = resarr_left_segid -> numOfElements - 1 - x2;
				}
				if(x2 >= resarr_left_segid -> numOfElements + 1){
					mesegid = resarr_right_segid;
					mecandid = resarr_right_candid;
					meindex = x2 - resarr_left_segid -> numOfElements - 1;
				}
				
				int seed_seg_id = topseg_id_list[best_best_chain];
				int seed_cand_id = topseg_cand_id_list[best_best_chain];
				if(meindex>=0){
					seed_seg_id = ArrayListGet(mesegid, meindex)-NULL-1;
					seed_cand_id = ArrayListGet(mecandid, meindex)-NULL-1;
				}
		
				if(reconcile_debug){
					char ptxt[100];
					LRMpos2txt(context, iteration_context -> segment_best_pos[seed_seg_id][seed_cand_id], ptxt);
					LRMprintf("BEST_BEST [%d] is SEG %d: %s  %u\n",x2, seed_seg_id, ptxt, iteration_context -> segment_best_pos[seed_seg_id][seed_cand_id]);
				}
	
				if(meindex == 0){
					best_cand_rec -> read_head_pos = iteration_context  -> segment_best_pos[seed_seg_id][seed_cand_id] - (iteration_context -> segment_texts [seed_seg_id] - iteration_context -> read_text);
					best_cand_rec -> masks = iteration_context -> segment_best_masks[seed_seg_id][seed_cand_id];
				}
				best_cand_rec -> segment_id[x2] = seed_seg_id;
				best_cand_rec -> realign_cand_id[x2] = seed_cand_id;
			}
		
			read_res -> final_pos = best_cand_rec -> read_head_pos;
			read_res -> masks = best_cand_rec -> masks;
			read_res -> votes[best_align_i] = best_best_votes[best_align_i];
			best_cand_rec -> segment_number = resarr_left_segid -> numOfElements + 1 + resarr_right_candid -> numOfElements;
	
			for(x1 = 0; x1 < topseg_no; x1++){
				ArrayListDestroy(candarray_going_large_segid[x1]);
				ArrayListDestroy(candarray_going_large_candid[x1]);
				ArrayListDestroy(candarray_going_small_segid[x1]);
				ArrayListDestroy(candarray_going_small_candid[x1]);
			}
		}

		if(best_cand_rec){
			long long final_mapping_pos = -1;
			long long merged_chro_cursor = -1, merged_read_cursor = -1;
			long long cigar_read_cursor = -1, cigar_chro_cursor = -1;
			char * target_cigar = iteration_context -> merged_cigar[best_align_i], *old_cigar, ** old_txt_ptr = NULL;
			int target_cigar_ptr = 0, last_seg_last_base_read = -1, cigar_opts = 0, last_written_cigar_read = 0, *old_seg_length, read_matched_bases = 0, read_mismatched_bases = 0;
			long long last_seg_last_base_chro = -1, last_written_cigar_chro = 0;
			unsigned int * old_best_pos;

			old_cigar = malloc((LRMMAX_READ_LENGTH / LRMSEGMENT_MIN_DIST+1) * (LRMSEGMENT_CIGAR_SIZE + 1));
			old_txt_ptr = malloc(sizeof(void *) *(LRMMAX_READ_LENGTH / LRMSEGMENT_MIN_DIST+1));	
			old_seg_length = malloc(sizeof(int) *(LRMMAX_READ_LENGTH / LRMSEGMENT_MIN_DIST+1));
			old_best_pos = malloc(sizeof(int) *(LRMMAX_READ_LENGTH / LRMSEGMENT_MIN_DIST+1));

			memset(target_cigar, 0, LRMMERGE_CIGAR_SIZE+1);
			if(best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND ) LRMreverse_read(iteration_context -> read_text, iteration_context -> read_length);
			// remove "S" sections from the middle parts
	
			long long validateCigar_cursor = -1;
			int written_read_cursor = 0, dynamic_done;
			for(x1 = 0; x1 < best_cand_rec -> segment_number ; x1 ++){
				int this_seg_id = ( best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND ) ? best_cand_rec -> segment_id[best_cand_rec -> segment_number - x1 - 1] : best_cand_rec -> segment_id[x1];
				int this_segcand_id = ( best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND ) ? best_cand_rec -> realign_cand_id[best_cand_rec -> segment_number - x1 - 1] : best_cand_rec -> realign_cand_id[x1];
	
				strcpy(old_cigar + this_seg_id * (LRMSEGMENT_CIGAR_SIZE + 1), iteration_context -> segment_cigars[this_seg_id][this_segcand_id]);
				old_seg_length[this_seg_id] = iteration_context -> segment_lengths[this_seg_id] ;
				old_txt_ptr[this_seg_id] = iteration_context -> segment_texts[this_seg_id];
				old_best_pos[this_seg_id] = iteration_context -> segment_best_pos[this_seg_id][this_segcand_id];
	
				if(1){
					int cigar_max = strlen( iteration_context -> segment_cigars[this_seg_id][this_segcand_id]) + 1;
					cigar_max = min( LRMMERGE_CIGAR_SIZE, cigar_max );
					char * new_cigar = malloc(cigar_max+1);
					new_cigar[0]=0;
					int new_cigar_ptr = 0;
					int cci = 0, tmpi = 0, nch, is_first_section = 1;
	
					if(0){//&& FIXLENstrcmp("0a7d1c2c-1acd-4073-8497-e8766a77fff9_Basecall_1D_template" , iteration_context -> read_name)==0){
						char och = iteration_context -> segment_texts[this_seg_id][ iteration_context -> segment_lengths[this_seg_id] ];
						iteration_context -> segment_texts[this_seg_id][ iteration_context -> segment_lengths[this_seg_id] ] = 0;
						LRMprintf("CIGAR OLD of SEG %d :%s, NEW: %p, LEN=%d NCH='%c'(%d)     PTR SIZE=%d\nREAD = %s\n", this_seg_id, iteration_context -> segment_cigars[this_seg_id][this_segcand_id], new_cigar, tmpi, nch, nch, cigar_max - new_cigar_ptr, iteration_context -> segment_texts[this_seg_id]);
						iteration_context -> segment_texts[this_seg_id][ iteration_context -> segment_lengths[this_seg_id] ] = och;
					}
	
					while(0!=(nch = iteration_context -> segment_cigars[this_seg_id][this_segcand_id] [cci++])){
						if(nch >='0' && nch <='9')
							tmpi = tmpi*10+(nch - '0');
						else{
							if(nch == 'S'){
								if(is_first_section == ((best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND)?0:1))
									iteration_context -> segment_texts[this_seg_id] += tmpi;
								if(is_first_section)
									iteration_context -> segment_best_pos[this_seg_id][this_segcand_id] += tmpi;
								iteration_context -> segment_lengths[this_seg_id] -= tmpi;
							}else if(tmpi > 0){
								int has_substantial = nch == 'M' || nch == 'I';
								if(!has_substantial){
									int ncci, nnch, ntmpi=0;
									for(ncci = cci; 0!=(nnch = iteration_context -> segment_cigars[this_seg_id][this_segcand_id] [ncci]); ncci++){
										if(nnch !='0' && isdigit(nnch)) ntmpi = 1;
										if(( nnch == 'I' || nnch == 'M' ) && ntmpi){
											has_substantial = 1;
											break;
										}
										if(isalpha(nnch)) ntmpi=0;
									}
								}
								if(has_substantial) new_cigar_ptr += snprintf(new_cigar+new_cigar_ptr, cigar_max - new_cigar_ptr, "%d%c", tmpi, nch);
							}
							is_first_section = 0;
							tmpi=0;
						}
					}
					strncpy(iteration_context -> segment_cigars[this_seg_id][this_segcand_id], new_cigar, LRMSEGMENT_CIGAR_SIZE);
					free(new_cigar);
					assert(0 < iteration_context -> segment_lengths[this_seg_id]);
				}
	
				dynamic_done = 0;
				if(1){
					int this_start_offset = iteration_context -> segment_texts[this_seg_id] - iteration_context -> read_text;
	
					assert(iteration_context -> segment_best_pos[this_seg_id][this_segcand_id] >=this_start_offset);
	
					if(best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND ){
						int R_pos = iteration_context -> read_length - (iteration_context -> segment_texts[this_seg_id] - iteration_context -> read_text + iteration_context -> segment_lengths[this_seg_id]);
						//assert(iteration_context -> segment_best_pos[this_seg_id][this_segcand_id] >=R_pos);
						if(iteration_context -> segment_best_pos[this_seg_id][this_segcand_id]< R_pos){
							LRMprintf("REVERSED_RPOS: %s\n", iteration_context -> read_name);
						}
						this_start_offset = R_pos;
					}
	
					if(0){
						char postxt[100];
						LRMpos2txt(context, iteration_context -> segment_best_pos[this_seg_id][this_segcand_id], postxt);
						LRMprintf("TRY SEGMENT: LEN %d, READ+%d, CHRO %s\n", iteration_context -> read_length, this_start_offset , postxt);
					}
	
					if(validateCigar_cursor<0) validateCigar_cursor = this_start_offset;
	
					if(final_mapping_pos < 0){
						final_mapping_pos = iteration_context -> segment_best_pos[this_seg_id][this_segcand_id];// - this_start_offset;
	
						if(this_start_offset > 0 && this_start_offset < LRMDYNAMIC_MAXIMUM_GAP_LENGTH -1 ){
							unsigned int total_mismatched_bases = 0;
							int moves = LRMindel_dynamic_search_unknownregion(context, thread_context,0, final_mapping_pos, iteration_context -> read_text , 0, this_start_offset, &total_mismatched_bases, iteration_context -> read_length / LRMLONGREAD_DENOMINATOR, iteration_context -> read_name);
							if(1 && moves >0){
								int softclipping_bases = 0;
								//LRMprintf("HEAD_MOVES=%d : %s\n", moves, thread_context -> dynamic_programming_indel_movement_buf);
								long long old_cigar_chro_cursor = cigar_chro_cursor;
								LRMmoves_to_cigar(context, thread_context, iteration_context, &cigar_chro_cursor, &cigar_read_cursor, &target_cigar_ptr, &last_written_cigar_chro, &correct_mapping, moves, &cigar_opts, &last_written_cigar_read, &softclipping_bases, &read_matched_bases, &read_mismatched_bases, best_align_i);
								final_mapping_pos -= (cigar_chro_cursor - old_cigar_chro_cursor) - softclipping_bases;
								dynamic_done = 1;
							}
						}
						if(this_start_offset > 0 && !dynamic_done)
							if(cigar_opts < context -> max_cigar_opts_in_read - 5 && target_cigar_ptr < LRMMERGE_CIGAR_SIZE - 70)
								target_cigar_ptr +=  snprintf( target_cigar + target_cigar_ptr, LRMMERGE_CIGAR_SIZE - 40 - target_cigar_ptr, "%dS", this_start_offset);
	
						written_read_cursor = this_start_offset;
						cigar_read_cursor = this_start_offset;
						cigar_opts ++;
						cigar_chro_cursor = iteration_context -> segment_best_pos[this_seg_id][this_segcand_id];
						last_written_cigar_chro = cigar_chro_cursor;
						LRMlocate_chro_length(context, final_mapping_pos, &this_chromosome_name ,&this_chromosome_end);
					}
					merged_chro_cursor =  iteration_context -> segment_best_pos[this_seg_id][this_segcand_id];
					merged_read_cursor = this_start_offset;
	
					if(0){
						char postxt[100];
						LRMpos2txt(context, iteration_context -> segment_best_pos[this_seg_id][this_segcand_id], postxt );
						LRMprintf("FINAL MERGING : %s [%d / %d] ; CHRO POS = %s (%s) ; READ POS = %d ; CIGAR = %s\n", iteration_context -> read_name, this_seg_id, iteration_context -> total_segments, postxt, (best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND)?"NEG":"POS", this_start_offset, iteration_context -> segment_cigars[this_seg_id][this_segcand_id] );
					}
	
					if(1){
						long long chro_pos_from_new = LRMcalculate_written_chro_pos(context, thread_context, iteration_context, written_read_cursor, merged_read_cursor, iteration_context -> segment_cigars[this_seg_id][this_segcand_id], merged_chro_cursor);
						if(chro_pos_from_new < 0){
							continue;
						}
	
						long long delta = 0;
						if(last_seg_last_base_chro>0)
							delta =chro_pos_from_new - last_seg_last_base_chro;
	
						long long deltaOFF = delta - (this_start_offset -  last_seg_last_base_read);
	
						if(0){
							char postxt[100];
							LRMpos2txt(context, last_seg_last_base_chro , postxt);
							LRMprintf(" ========== DYNAMIC : %d ~ %d FROM %s ============= DELTA=%lld, %d > %lld\n", last_seg_last_base_read, this_start_offset , postxt , deltaOFF, LRMDYNAMIC_MAXIMUM_GAP_LENGTH -1 , this_start_offset - last_seg_last_base_read - min(0, deltaOFF));
						}
	
						if( this_start_offset > last_seg_last_base_read + 5 && delta > 0 && abs(deltaOFF) <= context -> max_dynamic_indel_length  && LRMDYNAMIC_MAXIMUM_GAP_LENGTH -1 > this_start_offset - last_seg_last_base_read - min(0, deltaOFF)){
							unsigned int total_mismatched_bases = 0;
							int moves = LRMindel_dynamic_search(context, thread_context,-(int)deltaOFF, last_seg_last_base_chro,  iteration_context -> read_text , last_seg_last_base_read, this_start_offset , &total_mismatched_bases, iteration_context -> read_length / LRMLONGREAD_DENOMINATOR , iteration_context -> read_name);
							//LRMprintf("MIDDLE_MOVES=%d : %s\n", moves, thread_context -> dynamic_programming_indel_movement_buf);
							LRMmoves_to_cigar(context, thread_context, iteration_context, &cigar_chro_cursor, &cigar_read_cursor, &target_cigar_ptr, &last_written_cigar_chro, &correct_mapping, moves, &cigar_opts, &last_written_cigar_read, NULL, &read_matched_bases, &read_mismatched_bases, best_align_i);
							written_read_cursor = this_start_offset;
						}else{
							if(last_seg_last_base_chro>0 && this_start_offset > last_seg_last_base_read) {
	
								cigar_chro_cursor += this_start_offset -  last_seg_last_base_read;
								cigar_read_cursor += this_start_offset -  last_seg_last_base_read;
	
								if(cigar_opts < context -> max_cigar_opts_in_read - 5 && target_cigar_ptr < LRMMERGE_CIGAR_SIZE - 70){
									last_written_cigar_read = cigar_read_cursor;
									last_written_cigar_chro = max(last_written_cigar_chro,cigar_chro_cursor);
									target_cigar_ptr +=  snprintf( target_cigar + target_cigar_ptr, LRMMERGE_CIGAR_SIZE - 40 - target_cigar_ptr, "%dM", this_start_offset -  last_seg_last_base_read);
									if(this_start_offset -  last_seg_last_base_read > 0)correct_mapping = 1;
								}
								cigar_opts ++;
	
								//LRMprintf("======== GAP MMM = %d\n", this_start_offset -  last_seg_last_base_read);
								delta -= this_start_offset -  last_seg_last_base_read;
								written_read_cursor = this_start_offset;
							}
	
							if(delta){
	
								long long delta_written = abs(delta);
	
								if(delta < 0) delta_written = min(delta_written, iteration_context -> read_length - cigar_read_cursor);
	
								if(delta>0) cigar_chro_cursor += delta_written;
								else cigar_read_cursor += delta_written;
	
								if(cigar_opts < context -> max_cigar_opts_in_read - 5 && target_cigar_ptr < LRMMERGE_CIGAR_SIZE - 70){
									target_cigar_ptr +=  snprintf( target_cigar + target_cigar_ptr, LRMMERGE_CIGAR_SIZE - 40 - target_cigar_ptr, "%lld%c" , delta_written, delta > 0?'D':'I' );
									last_written_cigar_read = cigar_read_cursor;
									last_written_cigar_chro = max(last_written_cigar_chro,cigar_chro_cursor);
								}
								cigar_opts ++;
								//LRMprintf("======== GAP NNN = %lld\n", delta);
	
								if(delta < 0)
									written_read_cursor += delta_written;
							}
						}
					}
	
					if(merged_chro_cursor >= 0){
						int cci = 0, tmpi = 0, nch;
						while(0!=(nch = iteration_context -> segment_cigars[this_seg_id][this_segcand_id] [cci++])){
							if(nch >='0' && nch <='9')
								tmpi = tmpi*10+(nch - '0');
							else{
								if(nch == 'M' || nch == 'S' || nch == 'D' || nch == 'N')merged_chro_cursor += tmpi;
								if(nch == 'M' || nch == 'S' || nch == 'I') merged_read_cursor += tmpi;
								
								if(written_read_cursor <=merged_read_cursor){
										int writting_optlen = tmpi;
										if(nch == 'M' || nch == 'S' || nch == 'I'){
											writting_optlen = (merged_read_cursor - written_read_cursor);
											cigar_read_cursor += writting_optlen;
										}
										if(nch == 'M' || nch == 'S' || nch == 'D' || nch == 'N')
											cigar_chro_cursor += writting_optlen;
	
										if(cigar_opts < context -> max_cigar_opts_in_read - 5 && target_cigar_ptr < LRMMERGE_CIGAR_SIZE - 70){
											target_cigar_ptr +=  snprintf( target_cigar + target_cigar_ptr, LRMMERGE_CIGAR_SIZE - 40 - target_cigar_ptr, "%d%c" , writting_optlen, nch);
											last_written_cigar_read = cigar_read_cursor;
											last_written_cigar_chro = max(last_written_cigar_chro,cigar_chro_cursor);
	 
											if(nch=='M' && writting_optlen > 0){
												correct_mapping =1;
												read_matched_bases += writting_optlen;
											}
										}
										cigar_opts ++;
										written_read_cursor = merged_read_cursor;
								}
								tmpi = 0;
							}
						}
						last_seg_last_base_read = LRM_USE_CIGAR_RESCURE? cigar_read_cursor :merged_read_cursor;
						last_seg_last_base_chro = LRM_USE_CIGAR_RESCURE? cigar_chro_cursor :merged_chro_cursor;
					}
				}


			}

			dynamic_done = 0;
			if(last_written_cigar_read < iteration_context -> read_length && (iteration_context -> read_length - last_written_cigar_read) <  LRMDYNAMIC_MAXIMUM_GAP_LENGTH -1){
				unsigned int total_mismatched_bases = 0;
				int moves = LRMindel_dynamic_search_unknownregion(context, thread_context,1, last_written_cigar_chro, iteration_context -> read_text , last_written_cigar_read,  iteration_context -> read_length, &total_mismatched_bases, iteration_context -> read_length / LRMLONGREAD_DENOMINATOR, iteration_context -> read_name);
				if(1 && moves >0){
					//LRMprintf("TAIL_MOVES=%d : %s\n", moves, thread_context -> dynamic_programming_indel_movement_buf);
					LRMmoves_to_cigar(context, thread_context, iteration_context, &cigar_chro_cursor, &cigar_read_cursor, &target_cigar_ptr, &last_written_cigar_chro, &correct_mapping, moves, &cigar_opts, &last_written_cigar_read, NULL, &read_matched_bases, &read_mismatched_bases, best_align_i);
					dynamic_done = 1;
				}
				//if(target_cigar_ptr < LRMMERGE_CIGAR_SIZE)target_cigar_ptr += snprintf( target_cigar + target_cigar_ptr, LRMMERGE_CIGAR_SIZE - target_cigar_ptr, "%dS", iteration_context -> read_length - last_written_cigar_read );
			}
	
			if(last_written_cigar_read < iteration_context -> read_length && !dynamic_done){
				if(cigar_opts < context -> max_cigar_opts_in_read - 5 && target_cigar_ptr < LRMMERGE_CIGAR_SIZE - 70){
					target_cigar_ptr +=  snprintf( target_cigar + target_cigar_ptr, LRMMERGE_CIGAR_SIZE - 40 - target_cigar_ptr, "%dS" , iteration_context -> read_length - last_written_cigar_read);
					last_written_cigar_read = iteration_context -> read_length;
				}
			}
	
			char * chro_name_end=NULL;
			int chro_pos_end = 0;
			LRMlocate_gene_position(context, last_written_cigar_chro, &chro_name_end, &chro_pos_end);
			
			if( read_matched_bases < (read_matched_bases + read_mismatched_bases) * 75 / 100 || read_matched_bases < context -> min_matched_bases || last_written_cigar_read != iteration_context -> read_length || last_written_cigar_chro > this_chromosome_end || chro_name_end != this_chromosome_name ){
				if(context -> show_read_validation)LRMprintf("Matched bases: %d in %d, last_written_cigar_read %d == %d, last_written_cigar_chro %s == %s\n",read_matched_bases,  (read_matched_bases + read_mismatched_bases), last_written_cigar_read, iteration_context -> read_length, chro_name_end, this_chromosome_name );
				correct_mapping = 0;
			}
	
			if(best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND ) LRMreverse_read(iteration_context -> read_text, iteration_context -> read_length);
	
			iteration_context -> merged_position[best_align_i] = final_mapping_pos;
			iteration_context -> merged_masks[best_align_i] = best_cand_rec -> masks;
	
			if(context -> show_read_validation){// || FIXLENstrcmp("1e5f9005-2d77-464c-ba1f-2dda2559d782_Basecall_1D_template" , iteration_context -> read_name)==0){
				if( correct_mapping ){
					char postxt[100];
					int mapped_length = 0;
	
					LRMfix_cigar(context, thread_context, iteration_context, target_cigar);
					LRMfix_cigar(context, thread_context, iteration_context, target_cigar);
					LRMfix_cigar(context, thread_context, iteration_context, target_cigar);
	
					LRMpos2txt(context , final_mapping_pos, postxt);
					LRMprintf("\nFINAL READ %s to %s (%s) (%lld)\n", iteration_context -> read_name, postxt, best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND?"NEG":"POS", final_mapping_pos);
					int matched_bases = LRMvalidate_mapping(context , iteration_context -> read_text, target_cigar, &context -> current_base_index, final_mapping_pos, best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND, &mapped_length , 1);
					LRMprintf("Matched %d in %d  : %s\n",  matched_bases, mapped_length, target_cigar);
					//LRMprintf("TWO CHROS: %s, %s ; MAPPED: %lld - %lld , END: %lld\n", chro_name_end, this_chromosome_name, final_mapping_pos, last_written_cigar_chro, this_chromosome_end);
					LRMprintf("Segments mapped = %d, %d ~ %d, out of %d\n",  best_cand_rec -> segment_number , best_cand_rec -> segment_id[0], best_cand_rec -> segment_id[best_cand_rec -> segment_number - 1], iteration_context -> total_segments);
					LRMprintf("\n\n");
				}
				LRMprintf("Matched bases = %d ; Mismatched bases = %d\n", read_matched_bases, read_mismatched_bases);
				if(!correct_mapping) LRMprintf("ERROR IN MAPPING !!!\n");
			}
			//if (!correct_mapping)LRMprintf("WARNING: unexpected mapping result of %s\n", iteration_context -> read_name);

			for(x1 = 0; x1 < best_cand_rec -> segment_number ; x1 ++){
				int this_seg_id = ( best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND ) ? best_cand_rec -> segment_id[best_cand_rec -> segment_number - x1 - 1] : best_cand_rec -> segment_id[x1];
				int this_segcand_id = ( best_cand_rec -> masks & LRMIS_NEGATIVE_STRAND ) ? best_cand_rec -> realign_cand_id[best_cand_rec -> segment_number - x1 - 1] : best_cand_rec -> realign_cand_id[x1];
		

				//LRMprintf("SEG_ID=%d\n", this_seg_id);
				strcpy(iteration_context -> segment_cigars[this_seg_id][this_segcand_id], old_cigar + this_seg_id * (LRMSEGMENT_CIGAR_SIZE + 1));
				iteration_context -> segment_lengths[this_seg_id] = old_seg_length[this_seg_id];
				iteration_context -> segment_texts[this_seg_id] = old_txt_ptr[this_seg_id];
				iteration_context -> segment_best_pos[this_seg_id][this_segcand_id] = old_best_pos[this_seg_id];
			}
			iteration_context -> merged_matched_bases[best_align_i]  = read_matched_bases;
			free(best_cand_rec);

			free(old_seg_length);
			free(old_cigar);
			free(old_txt_ptr);
			free(old_best_pos);

			if(0)if(1||cigar_opts >66000 || target_cigar_ptr > LRMMERGE_CIGAR_SIZE - 1000){
				LRMprintf("CLOSE_TO_LIM: [%s , %d] %s, Ops=%d, Len=%d ~ %d\n", correct_mapping?"CORRECT": "ERROR", best_align_i,  iteration_context -> read_name , cigar_opts, target_cigar_ptr, LRMMERGE_CIGAR_SIZE);
			}

		} // end : if the best candidate list is not NULL

		if(0 &&FIXLENstrcmp("0a7d1c2c-1acd-4073-8497-e8766a77fff9_Basecall_1D_template" , iteration_context -> read_name)==0){
			LRMprintf("TEST_M %s ; Has_M = %d ; Cigar = %s\n", iteration_context -> read_name, correct_mapping, iteration_context -> merged_cigar[best_align_i]);
		}
		if(!correct_mapping){
			read_res -> votes[best_align_i] =0;
			iteration_context -> merged_cigar[best_align_i][0]=0;
		}

	}
	
	free(segment_id_list);
	free(segment_cand_id_list);
	free(segment_linear_list);
	HashTableDestroy(candarray_used_segment_id);
	HashTableDestroy(candarray_used_cand_id);
	
	return 0;
}


void LRMdo_one_realign_read(LRMcontext_t * context, LRMthread_context_t * thread_context, LRMread_iteration_context_t * iteration_context){
	int flags=4;
	int map_quality = 0;
	int mis_matched = 0;
	int seg_i;

	LRMread_mapping_result_t * read_res = context -> read_mapping_results + iteration_context -> read_no_in_chunk;
	memset(iteration_context -> segment_best_candidate_score, 0, sizeof(int) *(LRMMAX_READ_LENGTH / LRMSEGMENT_MIN_DIST+1) * LRMMERGING_MAX_CANDIDATES);
	memset(iteration_context -> segment_best_votes, 0, sizeof(int) *(LRMMAX_READ_LENGTH / LRMSEGMENT_MIN_DIST+1) * LRMMERGING_MAX_CANDIDATES);

	for(seg_i = 0; seg_i < iteration_context -> total_segments ; seg_i++){
		LRMrealign_context_t realign_context;
		int * this_best_scores = iteration_context -> segment_best_candidate_score[seg_i];
		
		int cand_i;
		for(cand_i = 0; cand_i < LRMSEGMENT_MAX_CANDIDATES; cand_i ++){
			LRMsegment_mapping_candidate_t * cand_res = read_res -> segment_results[seg_i].candidates + cand_i;

			if(0 && FIXLENstrcmp("0a7d1c2c-1acd-4073-8497-e8766a77fff9_Basecall_1D_template",  iteration_context -> read_name) == 0){
				char postxt[100];
				LRMpos2txt(context, cand_res -> first_base_position , postxt);
				LRMprintf("TRY REALIGN READ %s [%d , %d] : V=%d ; POS=%s (%s)\n", iteration_context -> read_name, seg_i, cand_i, cand_res -> votes, postxt, (cand_res -> masks & LRMIS_NEGATIVE_STRAND)?"NEG":"POS");
			}
			if(cand_res -> votes < 1) break;
			
			memset(&realign_context, 0, sizeof(LRMrealign_context_t));
			realign_context.current_segment_id = seg_i;
			realign_context.current_candidate_id = cand_i;
			LRMrealign_one_segment(context, thread_context, iteration_context, &realign_context);
			if(realign_context.best_stack_score[0]+realign_context.best_stack_score[1] > this_best_scores[LRMMERGING_MAX_CANDIDATES-1]){
				int replace_i, replace_index = LRMMERGING_MAX_CANDIDATES-1, is_repeated = 0;
				for(replace_i = LRMMERGING_MAX_CANDIDATES-1; replace_i >= 0; replace_i --){
					if(realign_context.best_stack_score[0]+realign_context.best_stack_score[1] > this_best_scores[replace_i])
						replace_index = replace_i;
					if(iteration_context -> segment_best_pos[seg_i][replace_i] == realign_context.best_chro_pos) is_repeated = 1;
				}
				
				if(0 == is_repeated){
					for(replace_i = LRMMERGING_MAX_CANDIDATES - 2 ; replace_i >= replace_index; replace_i--){
						strncpy(iteration_context -> segment_cigars[seg_i][replace_i+1], iteration_context -> segment_cigars[seg_i][replace_i], LRMSEGMENT_CIGAR_SIZE);
						iteration_context -> segment_best_pos[seg_i][replace_i+1] = iteration_context -> segment_best_pos[seg_i][replace_i];
						iteration_context -> segment_best_masks[seg_i][replace_i+1] = iteration_context -> segment_best_masks[seg_i][replace_i];
						iteration_context -> segment_best_candidate_score[seg_i][replace_i+1] = iteration_context -> segment_best_candidate_score[seg_i][replace_i];
						iteration_context -> segment_best_votes[seg_i][replace_i+1] = iteration_context -> segment_best_votes[seg_i][replace_i];
					}
					
					strncpy(iteration_context -> segment_cigars[seg_i][replace_index], realign_context.best_cigar, LRMSEGMENT_CIGAR_SIZE);
					iteration_context -> segment_best_pos[seg_i][replace_index] = realign_context.best_chro_pos;
					iteration_context -> segment_best_masks[seg_i][replace_index] = cand_res -> masks;
					iteration_context -> segment_best_candidate_score[seg_i][replace_index] = realign_context.best_stack_score[0]+realign_context.best_stack_score[1] ;
					iteration_context -> segment_best_votes[seg_i][replace_index] = cand_res -> votes;
				}
			}
		}
	}

	int best_align_i;
	for(best_align_i = 0; best_align_i < LRMMAX_MULTI_BEST; best_align_i++)
		strcpy(iteration_context -> merged_cigar[best_align_i], "*");
	LRMread_final_result_merge(context, thread_context, iteration_context, read_res);


	int best_matched_bases = 0;
	for(best_align_i = 0; best_align_i < LRMMAX_MULTI_BEST; best_align_i++){
		int x2, repeated = 0;
		for(x2 = 0; x2 < best_align_i; x2++){
			if(iteration_context -> merged_position[x2] == iteration_context -> merged_position[best_align_i]){
				repeated=1;
				break;
			}
		}
		if(repeated) strcpy(iteration_context -> merged_cigar[best_align_i], "*");

		char * chro_name="*";
		int chro_pos = -1;
		int not_found = LRMlocate_gene_position(context, iteration_context -> merged_position[best_align_i], &chro_name, & chro_pos);
		if(read_res -> votes[best_align_i] < 1 ||not_found) strcpy(iteration_context -> merged_cigar[best_align_i], "*");
	}
	for(best_align_i = 0; best_align_i < LRMMAX_MULTI_BEST; best_align_i++){
		if(iteration_context -> merged_cigar[best_align_i][0]==0) continue;
		if(iteration_context -> merged_cigar[best_align_i][0]=='*') continue;
		if(0){
			char p1txt[100];
			LRMpos2txt(context,  iteration_context -> merged_position[best_align_i], p1txt);
			LRMprintf("TEQLL #%d, score=%d, pos=%s  %u\n", best_align_i, iteration_context -> merged_matched_bases[best_align_i],p1txt, iteration_context -> merged_position[best_align_i]);
		}
		if(iteration_context -> merged_matched_bases[best_align_i] > best_matched_bases){
			best_matched_bases = iteration_context -> merged_matched_bases[best_align_i];
		}
	}

	int equally_best_results = 0;
	for(best_align_i = 0; best_align_i < LRMMAX_MULTI_BEST; best_align_i++){
		if(iteration_context -> merged_cigar[best_align_i][0]==0) continue;
		if(iteration_context -> merged_cigar[best_align_i][0]=='*') continue;
		if(iteration_context -> merged_matched_bases[best_align_i] < best_matched_bases){
			strcpy(iteration_context -> merged_cigar[best_align_i], "*");
		}else equally_best_results++;
	}

	if(context -> unique_only && equally_best_results>1){
		for(best_align_i = 0; best_align_i < LRMMAX_MULTI_BEST; best_align_i++)
			strcpy(iteration_context -> merged_cigar[best_align_i], "*");
		equally_best_results = 0;
	}


	int this_mapping_loc = 1;
	int total_output_records = max(1, min(context -> max_best_alignments, equally_best_results));
	//LRMprintf("EQLL %s #=%d, score=%d\tTOTAL_LOC=%d\tBB=%d\n", iteration_context -> read_name, equally_best_results, best_matched_bases, total_output_records, context -> max_best_alignments);
	for(best_align_i = 0; best_align_i < LRMMAX_MULTI_BEST; best_align_i++){
		char * chro_name="*";
		int chro_pos = -1;
		if(best_align_i>0 && equally_best_results < 1){
			if(iteration_context -> merged_cigar[best_align_i][0]==0) continue;
			if(iteration_context -> merged_cigar[best_align_i][0]=='*') continue;
		}

		if(equally_best_results >0){
			if(iteration_context -> merged_cigar[best_align_i][0]==0) continue;
			if(iteration_context -> merged_cigar[best_align_i][0]=='*') continue;
		}

		if(this_mapping_loc >=total_output_records+1)continue;

		if( iteration_context -> merged_cigar[best_align_i][0]!=0 && iteration_context -> merged_cigar[best_align_i][0]!='*' ){
			int not_found = LRMlocate_gene_position(context, iteration_context -> merged_position[best_align_i], &chro_name, & chro_pos);
			if(not_found){
				flags = 4;
				strcpy(iteration_context -> merged_cigar[best_align_i], "*");
				chro_pos = -1;
				chro_name = "*";
			}else{
					map_quality = 10;// read_res -> votes + 10;
					if( iteration_context -> merged_masks[best_align_i] & LRMIS_NEGATIVE_STRAND){
						flags=16;
						LRMreverse_read_and_qual(context, thread_context, iteration_context);
					}else flags = 0;
			}
		}else flags = 4;
	
		LRMfix_cigar(context, thread_context, iteration_context, iteration_context -> merged_cigar[best_align_i]);
		LRMfix_cigar(context, thread_context, iteration_context, iteration_context -> merged_cigar[best_align_i]);
		LRMfix_cigar(context, thread_context, iteration_context, iteration_context -> merged_cigar[best_align_i]);
	
		LRMwrite_chunk_add_buffered_output(context, thread_context, iteration_context, flags, chro_name, chro_pos, map_quality, iteration_context -> merged_cigar[best_align_i], mis_matched, total_output_records, this_mapping_loc++);
	}
	if( equally_best_results>0 ) thread_context -> mapped_reads ++;
}

int LRMchunk_read_iteration(LRMcontext_t * context, int thread_id, int task){
	LRMthread_context_t * thread_context = context -> thread_contexts+ thread_id;

	LRMread_iteration_context_t * iteration_context;
	iteration_context = malloc(sizeof(LRMread_iteration_context_t));
	//LRMprintf(" ============  LITR_CONTEXT PTR=%p, SIZE=%lld  \n", iteration_context, sizeof(LRMread_iteration_context_t));
	memset(iteration_context, 0, sizeof(LRMread_iteration_context_t));
	while(1){
		int retv = LRMfetch_next_read(context, thread_context, &iteration_context-> read_length, iteration_context->read_name, iteration_context->read_text, iteration_context->qual_text, &iteration_context -> read_no_in_chunk);
		if(retv) break;

		LRMsplit_read_to_segments(context, iteration_context);
		if(task==LRMRUNNING_STEP_VOTING)
			LRMdo_one_voting_read(context, thread_context, iteration_context);
		else if(task==LRMRUNNING_STEP_REALIGN)
			LRMdo_one_realign_read(context, thread_context, iteration_context);
		else assert(0);
		
		if(iteration_context -> read_no_in_chunk % 2000 == 0)
			LRMprintf("Processing %d-th read for task %d; used %.1f minutes\n", context -> all_processed_reads + iteration_context -> read_no_in_chunk, task, (LRMmiltime() - context -> start_running_time)/60);

		//LRMprintf("R:%s, T:%s\n", iteration_context -> read_name, iteration_context -> read_text);
	}
	free(iteration_context);
	return 0;
}

int LRMfinalise_chunk_reads(LRMcontext_t* context){
	context ->  all_processed_reads += context -> processed_reads_in_chunk;
	return 0;
}

int FIXLENstrcmp(char * fixed_len, char * rname){
        int x=0;
        for(; fixed_len[x]; x++){
                if(rname[x]!=fixed_len[x]) return 1;
        }
        return 0;
}

