// File: /workspace/github_cpp_makefile_star2_O2/gersteinlab[P]MUSIC/src/ms_filter_utils.cpp
// Function: median_filter_data(double*, int, int, int)
double* median_filter_data(double* track_data,
	int l_track_data, 
	int l_averaging_win,
	int skip_value) // This is the value to be skipped from the window values.
{
	// Do copying from start to end.
    double* cur_filtered_track = new double[l_track_data + 2];

	int n_signal_wins = 0;
	int half_l_averaging = l_averaging_win / 2;

	// Set the maximum value for the histogram.
	int MAX_VAL = -1000*1000;
	int MIN_VAL = 1000*1000;
	for(int i_sig = 1; i_sig <= l_track_data; i_sig++)
	{
		if(MAX_VAL < (int)(track_data[i_sig]))
		{
			MAX_VAL = (int)(track_data[i_sig]);
		}

		if(MIN_VAL > (int)(track_data[i_sig]))
		{
			MIN_VAL = (int)(track_data[i_sig]);
		}
	} // i_sig loop.

	// Make sure that this is the maximum.
	MAX_VAL += 1000;
	MIN_VAL -= 1000;
		
	// Initialize the current pdf.
	int* cur_win_pdf = NULL;
	int cur_win_max = 0;
	int cur_win_min = 1000*1000;
	double* cur_win_vals = new double[l_averaging_win + 2];
	int prev_avg_start = 0;
	int prev_avg_end = 0;

	// Go over all the positions as the middle of the filtering window.
	for(int cur_win_mid = 1; cur_win_mid <= l_track_data; cur_win_mid++)
	{
		int cur_avg_start = (cur_win_mid > half_l_averaging)?(cur_win_mid - half_l_averaging):(1);
		int cur_avg_end = (cur_win_mid + half_l_averaging <= l_track_data)?(cur_win_mid + half_l_averaging):(l_track_data);
		if(cur_win_pdf == NULL)
		{
			cur_win_pdf = new int[MAX_VAL - MIN_VAL+ 2];
			memset(cur_win_pdf, 0, sizeof(int) * (MAX_VAL - MIN_VAL + 1));
			//t_string::set_byte_buffer(cur_win_pdf, sizeof(int) * (MAX_VAL - MIN_VAL + 1), 0);
			cur_win_pdf -= MIN_VAL;

			// Generate the pdf, get the minimum and maximum in the current window.
			for(int i = cur_avg_start; i <= cur_avg_end; i++)
			{
				if(track_data[i] != skip_value)
				{
					cur_win_pdf[(int)(track_data[i])]++;

					if(cur_win_max < track_data[i])
					{
						cur_win_max = (int)track_data[i];
					}
					
					if(cur_win_min > track_data[i])
					{
						cur_win_min = (int)track_data[i];
					}
				} // skip_Value check.
			} // i loop.
		} // cur_win_pdf is NULL check.
		else
		{
			// Remove the old values from the pdf, add the new values.
			for(int i = prev_avg_start; i < cur_avg_start; i++)
			{
				if(track_data[i] != skip_value)
				{
					cur_win_pdf[(int)(track_data[i])]--;
				}
			} // i loop.

			// Update the min and max only for the values that are new in this window.
			for(int i = prev_avg_end+1; i <= cur_avg_end; i++)
			{
				if(track_data[i] != skip_value)
				{
					cur_win_pdf[(int)(track_data[i])]++;

					if(cur_win_max < track_data[i])
					{
						cur_win_max = (int)track_data[i];
					}
					
					if(cur_win_min > track_data[i])
					{
						cur_win_min = (int)track_data[i];
					}
				} // skip_value check.
			}

			// Sanity Check: The total # of points must be equal to the window length.
			int n_total_pts = 0;
			for(int i = cur_win_min; i <= cur_win_max; i++)
			{
				if(i != skip_value)
				{
					n_total_pts += cur_win_pdf[i];
				}
			} // i loop.
		} // cur_win_pdf is NULL check.

		// Count the total number of points without the skip value.
		int n_total = 0;
		for(int i = cur_win_min; i <= cur_win_max; i++)
		{
			if(i != skip_value)
			{
				n_total += cur_win_pdf[i];
			}
		} // i loop.
		
		// Generate the window median.
		int cur_win_median = 0;
		int n_cur_total = 0;
		for(int i = cur_win_min; i <= cur_win_max; i++)
		{
			if(i != skip_value)
			{
				n_cur_total += cur_win_pdf[i];
				if(n_cur_total > n_total/2)
				{
					// We found the median, can break out of the loop.
					cur_win_median = i;
					break;
				}
			}
		} // i loop.

		// Track the minimum and maximum from the histogram to update them for the current window.
		int updated_win_min = 0;			
		for(int i = cur_win_min; i <= cur_win_max; i++)
		{
			if(i != skip_value)
			{
				if(cur_win_pdf[i] > 0)
				{
					updated_win_min = i;
					break;
				}
			} // skip_value check.
		} // i loop.

		int updated_win_max = 0;
		for(int i = cur_win_max; i >= cur_win_min; i--)
		{
			if(i != skip_value)
			{
				if(cur_win_pdf[i] > 0)
				{
					updated_win_max = i;
					break;
				}
			} // skip_value check.
		} // i loop.

		// Set the median.
		//int median_per_pdf = cur_win_median;
		cur_filtered_track[cur_win_mid] = cur_win_median;

		// Update the previous averaging window limits.
		prev_avg_start = cur_avg_start;
		prev_avg_end = cur_avg_end;
		cur_win_min = updated_win_min;
		cur_win_max = updated_win_max;

#undef _QSORT_CHECK_
#ifdef _QSORT_CHECK_
		// Get the median via qsort and compare as a sanity check.
		int n_valid_vals = 0;
		for(int i = cur_avg_start; i <= cur_avg_end; i++)
		{
			if(track_data[i] != skip_value)
			{
				cur_win_vals[n_valid_vals] = track_data[i];
				n_valid_vals++;
			}
		} // i loop.
		qsort(cur_win_vals, n_valid_vals, sizeof(double), sort_doubles_descending);
		//per_win_profile[n_signal_wins] = cur_win_vals[l_averaging_win/2];

		if(cur_win_vals[n_valid_vals/2] != median_per_pdf)
		{
			fprintf(stderr, "Medians do not match: %d, %d:\n", (int)cur_win_vals[n_valid_vals/2], median_per_pdf);
			for(int i = cur_avg_start; i <= cur_avg_end; i++)
			{
				fprintf(stderr, "%lf ", track_data[i]);
			} // i loop.
			fprintf(stderr, "\n");
			getc(stdin);
		}
#endif // _QSORT_CHECK_

		n_signal_wins++;
	} // main signal filtering loop.

	// Free pdf memory.
	delete [] cur_win_vals;
	delete [] (cur_win_pdf+MIN_VAL);

	return(cur_filtered_track);
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x68, %rsp
movl %ecx, %ebp
movl %edx, %r12d
movl %esi, %r15d
movq %rdi, %r14
leal 0x2(%r15), %edi
shlq $0x3, %rdi
cmpl $-0x2, %esi
pushq $-0x1
popq %rbx
cmovlq %rbx, %rdi
callq 0x2040
movq %r15, %r8
xorl %edx, %edx
testl %r8d, %r8d
movl $0x0, %edi
cmovgl %r8d, %edi
movq %rax, %r9
leal 0x1(%rdi), %r10d
movl $0xfff0bdc0, %esi       # imm = 0xFFF0BDC0
movl $0xf4240, %ecx          # imm = 0xF4240
cmpq %rdx, %rdi
je 0xf99a
cvttsd2si 0x8(%r14,%rdx,8), %eax
cmpl %eax, %esi
cmovlel %eax, %esi
cmpl %eax, %ecx
cmovgel %eax, %ecx
incq %rdx
jmp 0xf97f
pushq $0x2
popq %r11
movl %r12d, %eax
cltd
idivl %r11d
cvtsi2sd %ebp, %xmm3
subl %ecx, %esi
addl $0xfffffc18, %ecx       # imm = 0xFFFFFC18
leal 0x7d0(%rsi), %edx
leal 0x7d2(%rsi), %edi
movslq %edi, %rdi
shlq $0x2, %rdi
cmpl $-0x2, %edx
cmovlq %rbx, %rdi
movq %rdi, 0x30(%rsp)
addl $0x7d1, %esi            # imm = 0x7D1
movslq %esi, %rdx
shlq $0x2, %rdx
movq %rdx, 0x28(%rsp)
movslq %ecx, %rdx
movq %rdx, 0x18(%rsp)
negq %rdx
movq %rdx, 0x20(%rsp)
cltq
movq %rax, 0x58(%rsp)
movslq %ebp, %r15
movl $0xf4240, %r13d         # imm = 0xF4240
xorl %ecx, %ecx
pushq $0x1
popq %rdi
xorl %r12d, %r12d
xorl %edx, %edx
xorl %eax, %eax
movq %r9, 0x10(%rsp)
movq %r8, 0x48(%rsp)
movq %r10, 0x40(%rsp)
movsd %xmm3, 0x38(%rsp)
cmpq %r10, %rdi
je 0xfbf9
movl %edi, %ebp
movq 0x58(%rsp), %rsi
subl %esi, %ebp
cmpq %rsi, %rdi
pushq $0x1
popq %rbx
cmovlel %ebx, %ebp
addl %edi, %esi
cmpl %r8d, %esi
cmovgel %r8d, %esi
movl %ebp, 0xc(%rsp)
movslq %ebp, %rbp
movl %esi, 0x8(%rsp)
movslq %esi, %rbx
testq %rcx, %rcx
movq %rdi, 0x60(%rsp)
je 0xfad0
movslq %edx, %rdx
cmpq %rbp, %rdx
jge 0xfa84
movsd (%r14,%rdx,8), %xmm0
ucomisd %xmm3, %xmm0
jne 0xfa75
jnp 0xfa7f
cvttsd2si %xmm0, %esi
movslq %esi, %rsi
decl (%rcx,%rsi,4)
incq %rdx
jmp 0xfa62
movslq %eax, %rdx
movl %r13d, %eax
cvtsi2sd %r13d, %xmm0
movl %r12d, %edi
cmpq %rbx, %rdx
jge 0xfb63
movsd 0x8(%r14,%rdx,8), %xmm1
incq %rdx
ucomisd %xmm3, %xmm1
jne 0xfaad
jnp 0xfa92
cvttsd2si %xmm1, %r13d
movslq %r13d, %r12
incl (%rcx,%r12,4)
cvtsi2sd %edi, %xmm2
ucomisd %xmm2, %xmm1
cmovbel %edi, %r12d
ucomisd %xmm1, %xmm0
movl %r12d, %edi
jbe 0xfa92
jmp 0xfa87
movq 0x30(%rsp), %rdi
callq 0x2040
movq %rax, 0x50(%rsp)
movq %rax, %rdi
xorl %esi, %esi
movq 0x28(%rsp), %rdx
callq 0x20f0
movsd 0x38(%rsp), %xmm3
movq 0x20(%rsp), %rax
movq 0x50(%rsp), %rcx
leaq (%rcx,%rax,4), %rcx
cmpq %rbx, %rbp
jg 0xfb4a
movsd (%r14,%rbp,8), %xmm0
ucomisd %xmm3, %xmm0
jne 0xfb15
jnp 0xfb39
cvttsd2si %xmm0, %edx
movslq %edx, %rax
incl (%rcx,%rax,4)
cvtsi2sd %r12d, %xmm1
ucomisd %xmm1, %xmm0
cmovbel %r12d, %eax
cvtsi2sd %r13d, %xmm1
ucomisd %xmm0, %xmm1
jbe 0xfb3c
jmp 0xfb3f
movl %r12d, %eax
movl %r13d, %edx
incq %rbp
movl %eax, %r12d
movl %edx, %r13d
jmp 0xfb02
movl %r13d, %eax
movl %r12d, %edi
movq 0x48(%rsp), %r8
movq 0x10(%rsp), %r9
movq 0x40(%rsp), %r10
pushq $0x2
popq %r11
movslq %eax, %rsi
movslq %edi, %r12
xorl %eax, %eax
movq %rsi, %rdx
cmpq %r12, %rdx
jg 0xfb80
cmpq %rdx, %r15
je 0xfb7b
addl (%rcx,%rdx,4), %eax
incq %rdx
jmp 0xfb6e
cltd
idivl %r11d
xorl %edi, %edi
movq %rsi, %rdx
cmpq %r12, %rdx
jg 0xfb9f
cmpq %rdx, %r15
je 0xfb9a
addl (%rcx,%rdx,4), %edi
cmpl %eax, %edi
jg 0xfba5
incq %rdx
jmp 0xfb89
xorpd %xmm0, %xmm0
jmp 0xfba9
cvtsi2sd %edx, %xmm0
movq %rsi, %r13
movq 0x60(%rsp), %rdi
cmpq %r12, %r13
jg 0xfbc7
cmpq %r13, %r15
je 0xfbc2
cmpl $0x0, (%rcx,%r13,4)
jg 0xfbca
incq %r13
jmp 0xfbb1
xorl %r13d, %r13d
cmpq %rsi, %r12
jl 0xfbe0
cmpq %r12, %r15
je 0xfbdb
cmpl $0x0, (%rcx,%r12,4)
jg 0xfbe3
decq %r12
jmp 0xfbca
xorl %r12d, %r12d
movsd %xmm0, (%r9,%rdi,8)
incq %rdi
movl 0xc(%rsp), %edx
movl 0x8(%rsp), %eax
jmp 0xfa23
testq %rcx, %rcx
je 0xfc11
movq 0x18(%rsp), %rax
leaq (%rcx,%rax,4), %rdi
callq 0x2210
movq 0x10(%rsp), %r9
movq %r9, %rax
addq $0x68, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq