// File: 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:
subq $0xb8, %rsp
movq %rdi, 0xb0(%rsp)
movl %esi, 0xac(%rsp)
movl %edx, 0xa8(%rsp)
movl %ecx, 0xa4(%rsp)
movl 0xac(%rsp), %eax
addl $0x2, %eax
cltq
movl $0x8, %ecx
mulq %rcx
movq %rax, %rdi
seto %cl
movq $-0x1, %rax
testb $0x1, %cl
cmovneq %rax, %rdi
callq 0x2040
movq %rax, 0x98(%rsp)
movl $0x0, 0x94(%rsp)
movl 0xa8(%rsp), %eax
movl $0x2, %ecx
cltd
idivl %ecx
movl %eax, 0x90(%rsp)
movl $0xfff0bdc0, 0x8c(%rsp) # imm = 0xFFF0BDC0
movl $0xf4240, 0x88(%rsp) # imm = 0xF4240
movl $0x1, 0x84(%rsp)
movl 0x84(%rsp), %eax
cmpl 0xac(%rsp), %eax
jg 0x22a6f
movl 0x8c(%rsp), %eax
movq 0xb0(%rsp), %rcx
movslq 0x84(%rsp), %rdx
cvttsd2si (%rcx,%rdx,8), %ecx
cmpl %ecx, %eax
jge 0x22a1b
movq 0xb0(%rsp), %rax
movslq 0x84(%rsp), %rcx
cvttsd2si (%rax,%rcx,8), %eax
movl %eax, 0x8c(%rsp)
movl 0x88(%rsp), %eax
movq 0xb0(%rsp), %rcx
movslq 0x84(%rsp), %rdx
cvttsd2si (%rcx,%rdx,8), %ecx
cmpl %ecx, %eax
jle 0x22a57
movq 0xb0(%rsp), %rax
movslq 0x84(%rsp), %rcx
cvttsd2si (%rax,%rcx,8), %eax
movl %eax, 0x88(%rsp)
jmp 0x22a59
movl 0x84(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x84(%rsp)
jmp 0x229cb
movl 0x8c(%rsp), %eax
addl $0x3e8, %eax            # imm = 0x3E8
movl %eax, 0x8c(%rsp)
movl 0x88(%rsp), %eax
subl $0x3e8, %eax            # imm = 0x3E8
movl %eax, 0x88(%rsp)
movq $0x0, 0x78(%rsp)
movl $0x0, 0x74(%rsp)
movl $0xf4240, 0x70(%rsp)    # imm = 0xF4240
movl 0xa8(%rsp), %eax
addl $0x2, %eax
cltq
movl $0x8, %ecx
mulq %rcx
movq %rax, %rdi
seto %cl
movq $-0x1, %rax
testb $0x1, %cl
cmovneq %rax, %rdi
callq 0x2040
movq %rax, 0x68(%rsp)
movl $0x0, 0x64(%rsp)
movl $0x0, 0x60(%rsp)
movl $0x1, 0x5c(%rsp)
movl 0x5c(%rsp), %eax
cmpl 0xac(%rsp), %eax
jg 0x23030
movl 0x5c(%rsp), %eax
cmpl 0x90(%rsp), %eax
jle 0x22b27
movl 0x5c(%rsp), %eax
subl 0x90(%rsp), %eax
movl %eax, 0x18(%rsp)
jmp 0x22b32
movl $0x1, %eax
movl %eax, 0x18(%rsp)
jmp 0x22b32
movl 0x18(%rsp), %eax
movl %eax, 0x58(%rsp)
movl 0x5c(%rsp), %eax
addl 0x90(%rsp), %eax
cmpl 0xac(%rsp), %eax
jg 0x22b5f
movl 0x5c(%rsp), %eax
addl 0x90(%rsp), %eax
movl %eax, 0x14(%rsp)
jmp 0x22b6a
movl 0xac(%rsp), %eax
movl %eax, 0x14(%rsp)
movl 0x14(%rsp), %eax
movl %eax, 0x54(%rsp)
cmpq $0x0, 0x78(%rsp)
jne 0x22cd9
movl 0x8c(%rsp), %eax
subl 0x88(%rsp), %eax
addl $0x2, %eax
cltq
movl $0x4, %ecx
mulq %rcx
movq %rax, %rdi
seto %cl
movq $-0x1, %rax
testb $0x1, %cl
cmovneq %rax, %rdi
callq 0x2040
movq %rax, 0x78(%rsp)
movq 0x78(%rsp), %rdi
movl 0x8c(%rsp), %eax
subl 0x88(%rsp), %eax
addl $0x1, %eax
movslq %eax, %rdx
shlq $0x2, %rdx
xorl %esi, %esi
callq 0x2110
movl 0x88(%rsp), %ecx
movq 0x78(%rsp), %rax
movslq %ecx, %rdx
xorl %ecx, %ecx
subq %rdx, %rcx
shlq $0x2, %rcx
addq %rcx, %rax
movq %rax, 0x78(%rsp)
movl 0x58(%rsp), %eax
movl %eax, 0x50(%rsp)
movl 0x50(%rsp), %eax
cmpl 0x54(%rsp), %eax
jg 0x22cd4
movq 0xb0(%rsp), %rax
movslq 0x50(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
cvtsi2sdl 0xa4(%rsp), %xmm1
ucomisd %xmm1, %xmm0
jne 0x22c39
jp 0x22c39
jmp 0x22cc2
movq 0x78(%rsp), %rax
movq 0xb0(%rsp), %rcx
movslq 0x50(%rsp), %rdx
cvttsd2si (%rcx,%rdx,8), %ecx
movslq %ecx, %rcx
movl (%rax,%rcx,4), %edx
addl $0x1, %edx
movl %edx, (%rax,%rcx,4)
cvtsi2sdl 0x74(%rsp), %xmm1
movq 0xb0(%rsp), %rax
movslq 0x50(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
ucomisd %xmm1, %xmm0
jbe 0x22c90
movq 0xb0(%rsp), %rax
movslq 0x50(%rsp), %rcx
cvttsd2si (%rax,%rcx,8), %eax
movl %eax, 0x74(%rsp)
cvtsi2sdl 0x70(%rsp), %xmm0
movq 0xb0(%rsp), %rax
movslq 0x50(%rsp), %rcx
ucomisd (%rax,%rcx,8), %xmm0
jbe 0x22cc0
movq 0xb0(%rsp), %rax
movslq 0x50(%rsp), %rcx
cvttsd2si (%rax,%rcx,8), %eax
movl %eax, 0x70(%rsp)
jmp 0x22cc2
jmp 0x22cc4
movl 0x50(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x50(%rsp)
jmp 0x22c03
jmp 0x22e6b
movl 0x64(%rsp), %eax
movl %eax, 0x4c(%rsp)
movl 0x4c(%rsp), %eax
cmpl 0x58(%rsp), %eax
jge 0x22d42
movq 0xb0(%rsp), %rax
movslq 0x4c(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
cvtsi2sdl 0xa4(%rsp), %xmm1
ucomisd %xmm1, %xmm0
jne 0x22d10
jp 0x22d10
jmp 0x22d33
movq 0x78(%rsp), %rax
movq 0xb0(%rsp), %rcx
movslq 0x4c(%rsp), %rdx
cvttsd2si (%rcx,%rdx,8), %ecx
movslq %ecx, %rcx
movl (%rax,%rcx,4), %edx
addl $-0x1, %edx
movl %edx, (%rax,%rcx,4)
jmp 0x22d35
movl 0x4c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x4c(%rsp)
jmp 0x22ce1
movl 0x60(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x48(%rsp)
movl 0x48(%rsp), %eax
cmpl 0x54(%rsp), %eax
jg 0x22e1e
movq 0xb0(%rsp), %rax
movslq 0x48(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
cvtsi2sdl 0xa4(%rsp), %xmm1
ucomisd %xmm1, %xmm0
jne 0x22d83
jp 0x22d83
jmp 0x22e0c
movq 0x78(%rsp), %rax
movq 0xb0(%rsp), %rcx
movslq 0x48(%rsp), %rdx
cvttsd2si (%rcx,%rdx,8), %ecx
movslq %ecx, %rcx
movl (%rax,%rcx,4), %edx
addl $0x1, %edx
movl %edx, (%rax,%rcx,4)
cvtsi2sdl 0x74(%rsp), %xmm1
movq 0xb0(%rsp), %rax
movslq 0x48(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
ucomisd %xmm1, %xmm0
jbe 0x22dda
movq 0xb0(%rsp), %rax
movslq 0x48(%rsp), %rcx
cvttsd2si (%rax,%rcx,8), %eax
movl %eax, 0x74(%rsp)
cvtsi2sdl 0x70(%rsp), %xmm0
movq 0xb0(%rsp), %rax
movslq 0x48(%rsp), %rcx
ucomisd (%rax,%rcx,8), %xmm0
jbe 0x22e0a
movq 0xb0(%rsp), %rax
movslq 0x48(%rsp), %rcx
cvttsd2si (%rax,%rcx,8), %eax
movl %eax, 0x70(%rsp)
jmp 0x22e0c
jmp 0x22e0e
movl 0x48(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x48(%rsp)
jmp 0x22d4d
movl $0x0, 0x44(%rsp)
movl 0x70(%rsp), %eax
movl %eax, 0x40(%rsp)
movl 0x40(%rsp), %eax
cmpl 0x74(%rsp), %eax
jg 0x22e69
movl 0x40(%rsp), %eax
cmpl 0xa4(%rsp), %eax
je 0x22e5a
movq 0x78(%rsp), %rax
movslq 0x40(%rsp), %rcx
movl (%rax,%rcx,4), %eax
addl 0x44(%rsp), %eax
movl %eax, 0x44(%rsp)
jmp 0x22e5c
movl 0x40(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x40(%rsp)
jmp 0x22e2e
jmp 0x22e6b
movl $0x0, 0x3c(%rsp)
movl 0x70(%rsp), %eax
movl %eax, 0x38(%rsp)
movl 0x38(%rsp), %eax
cmpl 0x74(%rsp), %eax
jg 0x22eb6
movl 0x38(%rsp), %eax
cmpl 0xa4(%rsp), %eax
je 0x22ea7
movq 0x78(%rsp), %rax
movslq 0x38(%rsp), %rcx
movl (%rax,%rcx,4), %eax
addl 0x3c(%rsp), %eax
movl %eax, 0x3c(%rsp)
jmp 0x22ea9
movl 0x38(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x38(%rsp)
jmp 0x22e7b
movl $0x0, 0x34(%rsp)
movl $0x0, 0x30(%rsp)
movl 0x70(%rsp), %eax
movl %eax, 0x2c(%rsp)
movl 0x2c(%rsp), %eax
cmpl 0x74(%rsp), %eax
jg 0x22f33
movl 0x2c(%rsp), %eax
cmpl 0xa4(%rsp), %eax
je 0x22f24
movq 0x78(%rsp), %rax
movslq 0x2c(%rsp), %rcx
movl (%rax,%rcx,4), %eax
addl 0x30(%rsp), %eax
movl %eax, 0x30(%rsp)
movl 0x30(%rsp), %eax
movl %eax, 0x10(%rsp)
movl 0x3c(%rsp), %eax
movl $0x2, %ecx
cltd
idivl %ecx
movl %eax, %ecx
movl 0x10(%rsp), %eax
cmpl %ecx, %eax
jle 0x22f22
movl 0x2c(%rsp), %eax
movl %eax, 0x34(%rsp)
jmp 0x22f33
jmp 0x22f24
jmp 0x22f26
movl 0x2c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x2c(%rsp)
jmp 0x22ece
movl $0x0, 0x28(%rsp)
movl 0x70(%rsp), %eax
movl %eax, 0x24(%rsp)
movl 0x24(%rsp), %eax
cmpl 0x74(%rsp), %eax
jg 0x22f85
movl 0x24(%rsp), %eax
cmpl 0xa4(%rsp), %eax
je 0x22f76
movq 0x78(%rsp), %rax
movslq 0x24(%rsp), %rcx
cmpl $0x0, (%rax,%rcx,4)
jle 0x22f74
movl 0x24(%rsp), %eax
movl %eax, 0x28(%rsp)
jmp 0x22f85
jmp 0x22f76
jmp 0x22f78
movl 0x24(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x24(%rsp)
jmp 0x22f43
movl $0x0, 0x20(%rsp)
movl 0x74(%rsp), %eax
movl %eax, 0x1c(%rsp)
movl 0x1c(%rsp), %eax
cmpl 0x70(%rsp), %eax
jl 0x22fd7
movl 0x1c(%rsp), %eax
cmpl 0xa4(%rsp), %eax
je 0x22fc8
movq 0x78(%rsp), %rax
movslq 0x1c(%rsp), %rcx
cmpl $0x0, (%rax,%rcx,4)
jle 0x22fc6
movl 0x1c(%rsp), %eax
movl %eax, 0x20(%rsp)
jmp 0x22fd7
jmp 0x22fc8
jmp 0x22fca
movl 0x1c(%rsp), %eax
addl $-0x1, %eax
movl %eax, 0x1c(%rsp)
jmp 0x22f95
cvtsi2sdl 0x34(%rsp), %xmm0
movq 0x98(%rsp), %rax
movslq 0x5c(%rsp), %rcx
movsd %xmm0, (%rax,%rcx,8)
movl 0x58(%rsp), %eax
movl %eax, 0x64(%rsp)
movl 0x54(%rsp), %eax
movl %eax, 0x60(%rsp)
movl 0x28(%rsp), %eax
movl %eax, 0x70(%rsp)
movl 0x20(%rsp), %eax
movl %eax, 0x74(%rsp)
movl 0x94(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x94(%rsp)
movl 0x5c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x5c(%rsp)
jmp 0x22af8
movq 0x68(%rsp), %rax
movq %rax, 0x8(%rsp)
cmpq $0x0, %rax
je 0x2304a
movq 0x8(%rsp), %rdi
callq 0x2220
movq 0x78(%rsp), %rax
movslq 0x88(%rsp), %rcx
shlq $0x2, %rcx
addq %rcx, %rax
movq %rax, (%rsp)
cmpq $0x0, %rax
je 0x23071
movq (%rsp), %rdi
callq 0x2220
movq 0x98(%rsp), %rax
addq $0xb8, %rsp
retq
nopw %cs:(%rax,%rax)