// File: gersteinlab[P]MUSIC/src/ms_peak_calling_utils.cpp
// Function: get_control_per_chip_profile(double*, int, int, int)
double* get_control_per_chip_profile(double* signal_profile, int l_profile, int l_p_win, int l_fragment)
{
if(__DUMP_PEAK_CALLING_UTILS_MSGS__)
{
	fprintf(stderr, "Generating smoothed track for %d values.\n", l_profile);
}

	int l_average = 1000*1000;
	double* window_averaged_signal_profile = mean_filter_data(signal_profile,
																l_profile, 
																l_average,
																-1);

	// Ceil the profile.
	for(int i = 1; i <= l_profile; i++)
	{
		//window_averaged_signal_profile[i] = ceil(window_averaged_signal_profile[i]);
		if(window_averaged_signal_profile[i] < 1)
		{
			window_averaged_signal_profile[i] = 1;
		}
	} // i loop.

	double target_FPRs = 0.10;

	int n_wins = l_profile / l_p_win;

if(__DUMP_PEAK_CALLING_UTILS_MSGS__)
{
	fprintf(stderr, "%d windows.\n", n_wins);
}

	double* avg_per_l_p_win_signal = new double[n_wins+2];
	double* avg_per_l_p_win_control = new double[n_wins+2];
	for(int i_win = 0; i_win < n_wins; i_win++)
	{
		avg_per_l_p_win_signal[i_win] = 0;
		avg_per_l_p_win_control[i_win] = 0;

		for(int i = i_win*l_p_win+1; i < i_win*l_p_win + l_p_win; i++)
		{
			avg_per_l_p_win_signal[i_win] += signal_profile[i];
			avg_per_l_p_win_control[i_win] += window_averaged_signal_profile[i];
		} // i loop.
	} // i_win loop.

	// Go over per signal values, 
	double* log_factorials = buffer_log_factorials(1000*1000);
	
	// We need at least 50 windows in each denominator.
	bool have_enough_wins = false;

	int max_n_iters = 500;

	bool constraint_satisfied = false;
	double cur_p_val_FPR = 0;
	double cur_FC_FPR = 0;
	int iter_i = 0;
	for(;
		!constraint_satisfied
		; iter_i++)
	{
		double n_p_val_sig_FC_sig = 0;
		double n_p_val_sig_FC_insig = 0;
		double n_p_val_insig_FC_sig = 0;
		double n_p_val_insig_FC_insig = 0;
		for(int i_win = 0; i_win < n_wins; i_win++)
		{
			if(avg_per_l_p_win_signal[i_win] < l_p_win ||
			avg_per_l_p_win_control[i_win] < l_p_win)
			{
				continue;
			}

			//double log_p_val = get_binomial_pvalue_per_region(signal_profile, window_averaged_signal_profile,
			//													i_win*l_p_win+1, i_win*l_p_win+l_p_win,
			//													log_factorials, 1, false, 0);

			double log_p_val = get_binomial_p_val_per_enriched_background_values(avg_per_l_p_win_signal[i_win] / l_fragment, 
																				avg_per_l_p_win_control[i_win] / l_fragment, 
																				log_factorials);

			if(log_p_val < log(0.05))
			{
				if(avg_per_l_p_win_signal[i_win] / avg_per_l_p_win_control[i_win] > 2)
				{
					n_p_val_sig_FC_sig++;
				}
				else if(avg_per_l_p_win_signal[i_win] / avg_per_l_p_win_control[i_win] < 1.5)
				{
					n_p_val_sig_FC_insig++;
				}
				else
				{
					// The stuff that is insignificantly enriched, but FC is not conclusive.
				}
			} // p-val significant check.
			else
			{
				if(avg_per_l_p_win_signal[i_win] / avg_per_l_p_win_control[i_win] > 2)
				{
					n_p_val_insig_FC_sig++;
				}
				else if(avg_per_l_p_win_signal[i_win] / avg_per_l_p_win_control[i_win] < 1.5)
				{
					n_p_val_insig_FC_insig++;
				}
				else
				{
					// The stuff that is insignificantly enriched, but FC is not conclusive.
				}
			} // p-val insignificant check.
		} // i_win loop.

		// If there was never enough windows, update this.
		if(!have_enough_wins)
		{
			have_enough_wins = ((n_p_val_sig_FC_sig + n_p_val_insig_FC_sig) > 100 &&
								(n_p_val_sig_FC_sig + n_p_val_sig_FC_insig) > 100);
		}

		// Stopping criteria:
		double p_val_fpr_estimate = n_p_val_insig_FC_sig / (n_p_val_sig_FC_sig + n_p_val_insig_FC_sig);
		double FC_fpr_estimate = n_p_val_sig_FC_insig / (n_p_val_sig_FC_sig + n_p_val_sig_FC_insig); 

		cur_p_val_FPR = p_val_fpr_estimate;
		cur_FC_FPR = FC_fpr_estimate;

if(__DUMP_PEAK_CALLING_UTILS_MSGS__)
{
		fprintf(stderr, "%d. iteration, %lf/%lf=%lf, %lf/%lf=%lf\n", iter_i, 
			n_p_val_insig_FC_sig, (n_p_val_sig_FC_sig + n_p_val_insig_FC_sig), p_val_fpr_estimate, 
			n_p_val_sig_FC_insig, (n_p_val_sig_FC_sig + n_p_val_sig_FC_insig), FC_fpr_estimate);
}

		// If we have tried a lot, just give up at this iteration.
		if(iter_i >= max_n_iters)
		{
			break;
		}

		// Are we in good region? If so update the control to create a more stringent background.
		if(!have_enough_wins ||						// The number of accessible windows must increase as we make the background more stringent.
			(p_val_fpr_estimate > target_FPRs ||	// This will decrease as we make control more stringent.
			FC_fpr_estimate < target_FPRs))			// This will increase as we make control more stringent.
		{
			// Update the profile.
			for(int i = 1; i < l_profile; i++)
			{
				window_averaged_signal_profile[i] += .05;
			} // i loop.

			// Update the per window control signal.
			for(int i_win = 0; i_win < n_wins; i_win++)
			{
				avg_per_l_p_win_control[i_win] += l_p_win * 0.05;
			} // i_win loop.			
		}
		else
		{
			break;
		}
	} // signal update loop.

	fprintf(stderr, "Set the control profile in %d iterations (%.3f, %.3f)\n", iter_i, cur_p_val_FPR, cur_FC_FPR);

	delete [] log_factorials;
	delete [] avg_per_l_p_win_signal;
	delete [] avg_per_l_p_win_control;

	return(window_averaged_signal_profile);
}

// Assembly:
subq $0xf8, %rsp
movq %rdi, 0xf0(%rsp)
movl %esi, 0xec(%rsp)
movl %edx, 0xe8(%rsp)
movl %ecx, 0xe4(%rsp)
testb $0x1, 0x52d5f(%rip)     # 0x651da
je 0x1249c
movq 0x52b5c(%rip), %rax     # 0x64fe0
movq (%rax), %rdi
movl 0xec(%rsp), %edx
leaq 0x4293b(%rip), %rsi     # 0x54dd0
movb $0x0, %al
callq 0x2250
movl $0xf4240, 0xe0(%rsp) # imm = 0xF4240
movq 0xf0(%rsp), %rdi
movl 0xec(%rsp), %esi
movl 0xe0(%rsp), %edx
movl $0xffffffff, %ecx       # imm = 0xFFFFFFFF
callq 0x22680
movq %rax, 0xd8(%rsp)
movl $0x1, 0xd4(%rsp)
movl 0xd4(%rsp), %eax
cmpl 0xec(%rsp), %eax
jg 0x1253b
movq 0xd8(%rsp), %rax
movslq 0xd4(%rsp), %rcx
movsd 0x3fb16(%rip), %xmm0    # 0x52018
ucomisd (%rax,%rcx,8), %xmm0
jbe 0x12526
movq 0xd8(%rsp), %rax
movslq 0xd4(%rsp), %rcx
movsd 0x3faf7(%rip), %xmm0    # 0x52018
movsd %xmm0, (%rax,%rcx,8)
jmp 0x12528
movl 0xd4(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xd4(%rsp)
jmp 0x124da
movsd 0x4280d(%rip), %xmm0    # 0x54d50
movsd %xmm0, 0xc8(%rsp)
movl 0xec(%rsp), %eax
cltd
idivl 0xe8(%rsp)
movl %eax, 0xc4(%rsp)
testb $0x1, 0x52c71(%rip)     # 0x651da
je 0x1258a
movq 0x52a6e(%rip), %rax     # 0x64fe0
movq (%rax), %rdi
movl 0xc4(%rsp), %edx
leaq 0x42877(%rip), %rsi     # 0x54dfa
movb $0x0, %al
callq 0x2250
movl 0xc4(%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, 0xb8(%rsp)
movl 0xc4(%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, 0xb0(%rsp)
movl $0x0, 0xac(%rsp)
movl 0xac(%rsp), %eax
cmpl 0xc4(%rsp), %eax
jge 0x12709
movq 0xb8(%rsp), %rax
movslq 0xac(%rsp), %rcx
xorps %xmm0, %xmm0
movsd %xmm0, (%rax,%rcx,8)
movq 0xb0(%rsp), %rax
movslq 0xac(%rsp), %rcx
xorps %xmm0, %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl 0xac(%rsp), %eax
imull 0xe8(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xa8(%rsp)
movl 0xa8(%rsp), %eax
movl 0xac(%rsp), %ecx
imull 0xe8(%rsp), %ecx
addl 0xe8(%rsp), %ecx
cmpl %ecx, %eax
jge 0x126f1
movq 0xf0(%rsp), %rax
movslq 0xa8(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
movq 0xb8(%rsp), %rax
movslq 0xac(%rsp), %rcx
addsd (%rax,%rcx,8), %xmm0
movsd %xmm0, (%rax,%rcx,8)
movq 0xd8(%rsp), %rax
movslq 0xa8(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
movq 0xb0(%rsp), %rax
movslq 0xac(%rsp), %rcx
addsd (%rax,%rcx,8), %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl 0xa8(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xa8(%rsp)
jmp 0x1265c
jmp 0x126f3
movl 0xac(%rsp), %eax
addl $0x1, %eax
movl %eax, 0xac(%rsp)
jmp 0x125ff
movl $0xf4240, %edi          # imm = 0xF4240
callq 0x3f420
movq %rax, 0xa0(%rsp)
movb $0x0, 0x9f(%rsp)
movl $0x1f4, 0x98(%rsp) # imm = 0x1F4
movb $0x0, 0x97(%rsp)
xorps %xmm0, %xmm0
movsd %xmm0, 0x88(%rsp)
xorps %xmm0, %xmm0
movsd %xmm0, 0x80(%rsp)
movl $0x0, 0x7c(%rsp)
movb 0x97(%rsp), %al
xorb $-0x1, %al
testb $0x1, %al
jne 0x12768
jmp 0x12b9f
xorps %xmm0, %xmm0
movsd %xmm0, 0x70(%rsp)
xorps %xmm0, %xmm0
movsd %xmm0, 0x68(%rsp)
xorps %xmm0, %xmm0
movsd %xmm0, 0x60(%rsp)
xorps %xmm0, %xmm0
movsd %xmm0, 0x58(%rsp)
movl $0x0, 0x54(%rsp)
movl 0x54(%rsp), %eax
cmpl 0xc4(%rsp), %eax
jge 0x129af
movq 0xb8(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm1
cvtsi2sdl 0xe8(%rsp), %xmm0
ucomisd %xmm1, %xmm0
ja 0x127e7
movq 0xb0(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm1
cvtsi2sdl 0xe8(%rsp), %xmm0
ucomisd %xmm1, %xmm0
jbe 0x127ec
jmp 0x1299f
movq 0xb8(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
cvtsi2sdl 0xe4(%rsp), %xmm1
divsd %xmm1, %xmm0
cvttsd2si %xmm0, %edi
movq 0xb0(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
cvtsi2sdl 0xe4(%rsp), %xmm1
divsd %xmm1, %xmm0
cvttsd2si %xmm0, %esi
movq 0xa0(%rsp), %rdx
callq 0x35f90
movsd %xmm0, 0x48(%rsp)
movsd 0x48(%rsp), %xmm0
movsd %xmm0, 0x28(%rsp)
movsd 0x3f7d7(%rip), %xmm0    # 0x52030
callq 0x2140
movsd 0x28(%rsp), %xmm1
ucomisd %xmm1, %xmm0
jbe 0x12907
movq 0xb8(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
movq 0xb0(%rsp), %rax
movslq 0x54(%rsp), %rcx
divsd (%rax,%rcx,8), %xmm0
movsd 0x3f7b6(%rip), %xmm1    # 0x52050
ucomisd %xmm1, %xmm0
jbe 0x128b6
movsd 0x3f770(%rip), %xmm0    # 0x52018
addsd 0x70(%rsp), %xmm0
movsd %xmm0, 0x70(%rsp)
jmp 0x12902
movq 0xb8(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm1
movq 0xb0(%rsp), %rax
movslq 0x54(%rsp), %rcx
divsd (%rax,%rcx,8), %xmm1
movsd 0x3f766(%rip), %xmm0    # 0x52048
ucomisd %xmm1, %xmm0
jbe 0x128fe
movsd 0x3f728(%rip), %xmm0    # 0x52018
addsd 0x68(%rsp), %xmm0
movsd %xmm0, 0x68(%rsp)
jmp 0x12900
jmp 0x12900
jmp 0x12902
jmp 0x1299d
movq 0xb8(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
movq 0xb0(%rsp), %rax
movslq 0x54(%rsp), %rcx
divsd (%rax,%rcx,8), %xmm0
movsd 0x3f71d(%rip), %xmm1    # 0x52050
ucomisd %xmm1, %xmm0
jbe 0x1294f
movsd 0x3f6d7(%rip), %xmm0    # 0x52018
addsd 0x60(%rsp), %xmm0
movsd %xmm0, 0x60(%rsp)
jmp 0x1299b
movq 0xb8(%rsp), %rax
movslq 0x54(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm1
movq 0xb0(%rsp), %rax
movslq 0x54(%rsp), %rcx
divsd (%rax,%rcx,8), %xmm1
movsd 0x3f6cd(%rip), %xmm0    # 0x52048
ucomisd %xmm1, %xmm0
jbe 0x12997
movsd 0x3f68f(%rip), %xmm0    # 0x52018
addsd 0x58(%rsp), %xmm0
movsd %xmm0, 0x58(%rsp)
jmp 0x12999
jmp 0x12999
jmp 0x1299b
jmp 0x1299d
jmp 0x1299f
movl 0x54(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x54(%rsp)
jmp 0x12794
testb $0x1, 0x9f(%rsp)
jne 0x12a05
movsd 0x70(%rsp), %xmm0
addsd 0x60(%rsp), %xmm0
xorl %eax, %eax
movsd 0x3f659(%rip), %xmm1    # 0x52028
ucomisd %xmm1, %xmm0
movb %al, 0x27(%rsp)
jbe 0x129f8
movsd 0x70(%rsp), %xmm0
addsd 0x68(%rsp), %xmm0
movsd 0x3f63b(%rip), %xmm1    # 0x52028
ucomisd %xmm1, %xmm0
seta %al
movb %al, 0x27(%rsp)
movb 0x27(%rsp), %al
andb $0x1, %al
movb %al, 0x9f(%rsp)
movsd 0x60(%rsp), %xmm0
movsd 0x70(%rsp), %xmm1
addsd 0x60(%rsp), %xmm1
divsd %xmm1, %xmm0
movsd %xmm0, 0x40(%rsp)
movsd 0x68(%rsp), %xmm0
movsd 0x70(%rsp), %xmm1
addsd 0x68(%rsp), %xmm1
divsd %xmm1, %xmm0
movsd %xmm0, 0x38(%rsp)
movsd 0x40(%rsp), %xmm0
movsd %xmm0, 0x88(%rsp)
movsd 0x38(%rsp), %xmm0
movsd %xmm0, 0x80(%rsp)
testb $0x1, 0x52778(%rip)     # 0x651da
je 0x12ab0
movq 0x52575(%rip), %rax     # 0x64fe0
movq (%rax), %rdi
movl 0x7c(%rsp), %edx
movsd 0x60(%rsp), %xmm0
movsd 0x70(%rsp), %xmm1
addsd 0x60(%rsp), %xmm1
movsd 0x40(%rsp), %xmm2
movsd 0x68(%rsp), %xmm3
movsd 0x70(%rsp), %xmm4
addsd 0x68(%rsp), %xmm4
movsd 0x38(%rsp), %xmm5
leaq 0x4235e(%rip), %rsi     # 0x54e07
movb $0x6, %al
callq 0x2250
movl 0x7c(%rsp), %eax
cmpl 0x98(%rsp), %eax
jl 0x12ac2
jmp 0x12b9f
testb $0x1, 0x9f(%rsp)
je 0x12af6
movsd 0x40(%rsp), %xmm0
ucomisd 0xc8(%rsp), %xmm0
ja 0x12af6
movsd 0x38(%rsp), %xmm1
movsd 0xc8(%rsp), %xmm0
ucomisd %xmm1, %xmm0
jbe 0x12b8b
movl $0x1, 0x34(%rsp)
movl 0x34(%rsp), %eax
cmpl 0xec(%rsp), %eax
jge 0x12b37
movq 0xd8(%rsp), %rax
movslq 0x34(%rsp), %rcx
movsd 0x3f510(%rip), %xmm0    # 0x52030
addsd (%rax,%rcx,8), %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl 0x34(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x34(%rsp)
jmp 0x12afe
movl $0x0, 0x30(%rsp)
movl 0x30(%rsp), %eax
cmpl 0xc4(%rsp), %eax
jge 0x12b89
cvtsi2sdl 0xe8(%rsp), %xmm0
movq 0xb0(%rsp), %rax
movslq 0x30(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm1
movsd 0x3f4c1(%rip), %xmm2    # 0x52030
mulsd %xmm2, %xmm0
addsd %xmm1, %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl 0x30(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x30(%rsp)
jmp 0x12b3f
jmp 0x12b8d
jmp 0x12b9f
jmp 0x12b8f
movl 0x7c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x7c(%rsp)
jmp 0x12756
movq 0x5243a(%rip), %rax     # 0x64fe0
movq (%rax), %rdi
movl 0x7c(%rsp), %edx
movsd 0x88(%rsp), %xmm0
movsd 0x80(%rsp), %xmm1
leaq 0x4226a(%rip), %rsi     # 0x54e30
movb $0x2, %al
callq 0x2250
movq 0xa0(%rsp), %rax
movq %rax, 0x18(%rsp)
cmpq $0x0, %rax
je 0x12bea
movq 0x18(%rsp), %rdi
callq 0x2220
movq 0xb8(%rsp), %rax
movq %rax, 0x10(%rsp)
cmpq $0x0, %rax
je 0x12c07
movq 0x10(%rsp), %rdi
callq 0x2220
movq 0xb0(%rsp), %rax
movq %rax, 0x8(%rsp)
cmpq $0x0, %rax
je 0x12c24
movq 0x8(%rsp), %rdi
callq 0x2220
movq 0xd8(%rsp), %rax
addq $0xf8, %rsp
retq
nopw %cs:(%rax,%rax)