// File: /workspace/github_cpp_makefile_star2_O3/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, %ebx
movq %rdi, %r14
leal 0x2(%rbx), %eax
shlq $0x3, %rax
cmpl $-0x2, %esi
movq $-0x1, %rdi
cmovgeq %rax, %rdi
callq 0x2040
movq %rbx, %r11
movq %rax, 0x20(%rsp)
testl %r11d, %r11d
jle 0x132d8
movl %r12d, %eax
shrl $0x1f, %eax
addl %r12d, %eax
sarl %eax
leal 0x1(%r11), %r10d
movl $0xf4240, %ecx          # imm = 0xF4240
movl $0xfff0bdc0, %edx       # imm = 0xFFF0BDC0
movl $0x1, %esi
movl %ecx, %edi
cvttsd2si (%r14,%rsi,8), %ecx
cmpl %ecx, %edx
cmovlel %ecx, %edx
cmpl %ecx, %edi
cmovll %edi, %ecx
incq %rsi
cmpq %rsi, %r10
jne 0x12f8d
testl %r11d, %r11d
jle 0x132d8
cvtsi2sd %ebp, %xmm2
subl %ecx, %edx
addl $0xfffffc18, %ecx       # imm = 0xFFFFFC18
leal 0x7d0(%rdx), %esi
leal 0x7d2(%rdx), %edi
movslq %edi, %rdi
shlq $0x2, %rdi
cmpl $-0x2, %esi
movq $-0x1, %rsi
cmovgeq %rdi, %rsi
movq %rsi, 0x50(%rsp)
addl $0x7d1, %edx            # imm = 0x7D1
movslq %edx, %rdx
shlq $0x2, %rdx
movq %rdx, 0x48(%rsp)
movslq %ecx, %rdx
movq %rdx, 0x28(%rsp)
negq %rdx
movq %rdx, 0x40(%rsp)
cltq
movq %rax, 0x60(%rsp)
movslq %ebp, %rax
movq %rax, 0x58(%rsp)
negq %rax
movq %rax, 0x30(%rsp)
movl $0xf4240, %ebx          # imm = 0xF4240
xorl %ebp, %ebp
movl $0x1, %r15d
xorl %r12d, %r12d
xorl %r13d, %r13d
xorl %eax, %eax
movq %r11, 0x8(%rsp)
movq %r10, (%rsp)
movsd %xmm2, 0x18(%rsp)
movl %r12d, %edx
movl %ebp, %ecx
movl %r15d, %r12d
movq 0x60(%rsp), %rsi
subl %esi, %r12d
cmpq %rsi, %r15
movl $0x1, %edi
cmovlel %edi, %r12d
leal (%rsi,%r15), %ebp
cmpl %r11d, %ebp
cmovgel %r11d, %ebp
testq %rax, %rax
je 0x130e9
cmpl %r12d, %edx
jge 0x13096
movslq %edx, %rdx
movslq %r12d, %rsi
movsd (%r14,%rdx,8), %xmm0
ucomisd %xmm2, %xmm0
jne 0x13084
jnp 0x1308e
cvttsd2si %xmm0, %edi
movslq %edi, %rdi
decl (%rax,%rdi,4)
incq %rdx
cmpq %rdx, %rsi
jne 0x13076
cmpl %ebp, %ecx
jge 0x13193
movslq %ecx, %rcx
movslq %ebp, %rdx
movsd 0x8(%r14,%rcx,8), %xmm0
ucomisd %xmm2, %xmm0
jne 0x130b3
jnp 0x130dc
cvttsd2si %xmm0, %esi
movslq %esi, %rdi
incl (%rax,%rdi,4)
xorps %xmm1, %xmm1
cvtsi2sd %r13d, %xmm1
ucomisd %xmm1, %xmm0
cmoval %edi, %r13d
xorps %xmm1, %xmm1
cvtsi2sd %ebx, %xmm1
ucomisd %xmm0, %xmm1
jbe 0x130dc
movl %esi, %ebx
incq %rcx
cmpq %rdx, %rcx
jl 0x130a4
jmp 0x13193
movq 0x50(%rsp), %rdi
callq 0x2040
movl %r12d, 0x10(%rsp)
movq %rbp, %r12
movq %rax, %rbp
movq %rax, %rdi
xorl %esi, %esi
movq 0x48(%rsp), %rdx
callq 0x20f0
movq 0x40(%rsp), %rax
leaq (,%rax,4), %rax
addq %rbp, %rax
movq %r12, %rbp
movl 0x10(%rsp), %r12d
cmpl %ebp, %r12d
jle 0x1313b
movq 0x8(%rsp), %r11
movq (%rsp), %r10
movsd 0x18(%rsp), %xmm2
jmp 0x13193
movslq %r12d, %rcx
movslq %ebp, %rdx
decq %rcx
movq 0x8(%rsp), %r11
movq (%rsp), %r10
movsd 0x18(%rsp), %xmm2
movsd 0x8(%r14,%rcx,8), %xmm0
ucomisd %xmm2, %xmm0
jne 0x13162
jnp 0x1318b
cvttsd2si %xmm0, %esi
movslq %esi, %rdi
incl (%rax,%rdi,4)
xorps %xmm1, %xmm1
cvtsi2sd %r13d, %xmm1
ucomisd %xmm1, %xmm0
cmoval %edi, %r13d
xorps %xmm1, %xmm1
cvtsi2sd %ebx, %xmm1
ucomisd %xmm0, %xmm1
jbe 0x1318b
movl %esi, %ebx
incq %rcx
cmpq %rdx, %rcx
jl 0x13153
xorpd %xmm0, %xmm0
cmpl %r13d, %ebx
jg 0x131ce
movslq %ebx, %rcx
movq 0x58(%rsp), %rdx
subq %rcx, %rdx
leaq (%rax,%rcx,4), %rsi
movl %r13d, %edi
subl %ebx, %edi
incl %edi
xorl %r9d, %r9d
xorl %r8d, %r8d
cmpq %r9, %rdx
je 0x131c1
addl (%rsi,%r9,4), %r8d
incq %r9
cmpl %r9d, %edi
jne 0x131b8
cmpl %r13d, %ebx
jle 0x131f5
xorl %r8d, %r8d
xorl %edi, %edi
movq 0x20(%rsp), %rcx
movsd %xmm0, (%rcx,%r15,8)
incq %r15
movl %r8d, %ebx
movl %edi, %r13d
cmpq %r10, %r15
jne 0x1303f
jmp 0x132ca
movq %rbp, 0x10(%rsp)
movl %r8d, %edi
shrl $0x1f, %edi
addl %r8d, %edi
sarl %edi
movl %ebx, %r8d
movl %r13d, %r9d
notl %r9d
movq %r8, 0x38(%rsp)
leaq (%r9,%r8), %r11
xorl %r10d, %r10d
xorl %ebp, %ebp
cmpq %r10, %rdx
je 0x13229
addl (%rsi,%r10,4), %ebp
cmpl %edi, %ebp
jg 0x1323a
incq %r10
movl %r11d, %r8d
addl %r10d, %r8d
jne 0x1321c
xorpd %xmm0, %xmm0
jmp 0x13245
addl %ebx, %r10d
xorps %xmm0, %xmm0
cvtsi2sd %r10d, %xmm0
xorl %edi, %edi
cmpl %r13d, %ebx
movq 0x8(%rsp), %r11
movq 0x10(%rsp), %rbp
jle 0x13262
xorl %r8d, %r8d
movq (%rsp), %r10
jmp 0x131d3
addq 0x38(%rsp), %r9
xorl %r8d, %r8d
cmpq %r8, %rdx
je 0x13276
cmpl $0x0, (%rsi,%r8,4)
jg 0x13286
incq %r8
movl %r9d, %r10d
addl %r8d, %r10d
jne 0x1326a
xorl %r8d, %r8d
jmp 0x13289
addl %ebx, %r8d
movq (%rsp), %r10
cmpl %r13d, %ebx
jg 0x131d3
movslq %r13d, %rdx
incq %rdx
movq 0x30(%rsp), %r9
leaq (%r9,%rdx), %rsi
cmpq $0x1, %rsi
je 0x132b2
cmpl $0x0, -0x4(%rax,%rdx,4)
jg 0x132c2
decl %r13d
decq %rdx
cmpq %rcx, %rdx
jg 0x132a1
jmp 0x131d3
movl %r13d, %edi
jmp 0x131d3
movq 0x28(%rsp), %rcx
leaq (%rax,%rcx,4), %rdi
callq 0x2220
movq 0x20(%rsp), %rax
addq $0x68, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq