// File: /workspace/github_cpp_makefile_star2_O2/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:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x68, %rsp
movl %ecx, 0x20(%rsp)
movl %edx, %ebp
movl %esi, %r14d
movq %rdi, %r13
cmpb $0x1, 0x2cdd6(%rip)     # 0x351c2
jne 0x8409
movq 0x2cbeb(%rip), %rax     # 0x34fe0
movq (%rax), %rdi
leaq 0x1e621(%rip), %rsi     # 0x26a20
movl %r14d, %edx
xorl %eax, %eax
callq 0x2240
pushq $-0x1
popq %rcx
movq %r13, %rdi
movl %r14d, %esi
movl $0xf4240, %edx          # imm = 0xF4240
callq 0xf7d6
movq %rax, %rbx
xorl %eax, %eax
testl %r14d, %r14d
movl $0x0, %ecx
cmovgl %r14d, %ecx
movabsq $0x3ff0000000000000, %rdx # imm = 0x3FF0000000000000
movsd 0x1bbc9(%rip), %xmm0    # 0x24008
cmpq %rax, %rcx
je 0x8456
ucomisd 0x8(%rbx,%rax,8), %xmm0
jbe 0x8451
movq %rdx, 0x8(%rbx,%rax,8)
incq %rax
jmp 0x843f
movl %r14d, 0x10(%rsp)
movl %r14d, %eax
cltd
idivl %ebp
movl %eax, %r14d
cmpb $0x0, 0x2cd57(%rip)     # 0x351c2
je 0x8488
movq 0x2cb6c(%rip), %rax     # 0x34fe0
movq (%rax), %rdi
leaq 0x1e5cc(%rip), %rsi     # 0x26a4a
movl %r14d, %edx
xorl %eax, %eax
callq 0x2240
leal 0x2(%r14), %eax
shlq $0x3, %rax
cmpl $-0x2, %r14d
pushq $-0x1
popq %r12
cmovgeq %rax, %r12
movq %r12, %rdi
callq 0x2040
movq %rax, %r15
movq %r12, %rdi
callq 0x2040
movq %rax, %r12
movslq %ebp, %rax
xorl %ecx, %ecx
testl %r14d, %r14d
cmovlel %ecx, %r14d
xorl %edx, %edx
cmpq %r14, %rcx
je 0x8511
movslq %edx, %rsi
incq %rsi
andq $0x0, (%r15,%rcx,8)
andq $0x0, (%r12,%rcx,8)
leaq 0x1(%rcx), %rdi
movq %rdi, %r8
imulq %rax, %r8
xorpd %xmm0, %xmm0
xorpd %xmm1, %xmm1
cmpq %r8, %rsi
jge 0x850a
addsd (%r13,%rsi,8), %xmm1
movsd %xmm1, (%r15,%rcx,8)
addsd (%rbx,%rsi,8), %xmm0
movsd %xmm0, (%r12,%rcx,8)
incq %rsi
jmp 0x84e8
addl %ebp, %edx
movq %rdi, %rcx
jmp 0x84c0
movl $0xf4240, %edi          # imm = 0xF4240
callq 0x1e201
movq %rax, 0x40(%rsp)
cvtsi2sd %ebp, %xmm7
cvtsi2sdl 0x20(%rsp), %xmm0
movsd %xmm0, 0x50(%rsp)
movslq 0x10(%rsp), %rbp
movq $0x0, 0x38(%rsp)
movsd 0x1bb0a(%rip), %xmm6    # 0x24050
movsd 0x1bb01(%rip), %xmm9    # 0x24050
mulsd %xmm7, %xmm9
movl $0x0, 0xc(%rsp)
movsd %xmm7, 0x18(%rsp)
movsd %xmm9, 0x30(%rsp)
xorpd %xmm4, %xmm4
xorl %r13d, %r13d
xorpd %xmm3, %xmm3
xorpd %xmm8, %xmm8
cmpq %r13, %r14
je 0x869a
movsd (%r15,%r13,8), %xmm1
ucomisd %xmm1, %xmm7
ja 0x8674
movsd (%r12,%r13,8), %xmm2
ucomisd %xmm2, %xmm7
ja 0x8674
movsd %xmm8, 0x48(%rsp)
movsd %xmm3, 0x10(%rsp)
movapd %xmm4, 0x20(%rsp)
movapd %xmm1, %xmm0
movsd %xmm1, 0x60(%rsp)
movsd 0x50(%rsp), %xmm1
divsd %xmm1, %xmm0
cvttsd2si %xmm0, %edi
movapd %xmm2, %xmm0
divsd %xmm1, %xmm0
cvttsd2si %xmm0, %esi
movq 0x40(%rsp), %rdx
movsd %xmm2, 0x58(%rsp)
callq 0x1976d
movsd 0x1e3af(%rip), %xmm1    # 0x269a0
ucomisd %xmm0, %xmm1
movsd 0x60(%rsp), %xmm1
divsd 0x58(%rsp), %xmm1
jbe 0x8639
ucomisd 0x1ba5d(%rip), %xmm1    # 0x24068
movsd 0x1ba3d(%rip), %xmm6    # 0x24050
movsd 0x30(%rsp), %xmm9
movapd 0x20(%rsp), %xmm4
movsd 0x10(%rsp), %xmm3
movsd 0x48(%rsp), %xmm8
jbe 0x867c
addsd 0x1b9d1(%rip), %xmm4    # 0x24008
jmp 0x866e
ucomisd 0x1ba27(%rip), %xmm1    # 0x24068
movsd 0x1ba07(%rip), %xmm6    # 0x24050
movsd 0x30(%rsp), %xmm9
movapd 0x20(%rsp), %xmm4
movsd 0x10(%rsp), %xmm3
movsd 0x48(%rsp), %xmm8
jbe 0x866e
addsd 0x1b99a(%rip), %xmm8    # 0x24008
movsd 0x18(%rsp), %xmm7
incq %r13
jmp 0x8579
movsd 0x1b9b4(%rip), %xmm0    # 0x24038
ucomisd %xmm1, %xmm0
movsd 0x18(%rsp), %xmm7
jbe 0x8674
addsd 0x1b970(%rip), %xmm3    # 0x24008
jmp 0x8674
movapd %xmm4, %xmm1
addsd %xmm8, %xmm1
addsd %xmm3, %xmm4
movq 0x38(%rsp), %r13
testb $0x1, %r13b
jne 0x86d5
movsd 0x1b966(%rip), %xmm2    # 0x24020
movapd %xmm2, %xmm0
cmpltpd %xmm1, %xmm0
cmpltpd %xmm4, %xmm2
andpd %xmm0, %xmm2
movd %xmm2, %r13d
andb $0x1, %r13b
movapd %xmm8, %xmm2
divsd %xmm1, %xmm2
movapd %xmm3, %xmm5
divsd %xmm4, %xmm5
cmpb $0x1, 0x2cad5(%rip)     # 0x351c2
je 0x8765
cmpl $0x1f4, 0xc(%rsp)       # imm = 0x1F4
je 0x87be
testb $0x1, %r13b
je 0x871f
ucomisd 0x1e29d(%rip), %xmm2    # 0x269a8
ja 0x871f
movsd 0x1e293(%rip), %xmm0    # 0x269a8
ucomisd %xmm5, %xmm0
jbe 0x87be
pushq $0x1
popq %rax
cmpq %rbp, %rax
jge 0x873a
movsd (%rbx,%rax,8), %xmm0
addsd %xmm6, %xmm0
movsd %xmm0, (%rbx,%rax,8)
incq %rax
jmp 0x8722
xorl %eax, %eax
cmpq %rax, %r14
je 0x8757
movsd (%r12,%rax,8), %xmm0
addsd %xmm9, %xmm0
movsd %xmm0, (%r12,%rax,8)
incq %rax
jmp 0x873c
movq %r13, 0x38(%rsp)
incl 0xc(%rsp)
jmp 0x8569
movq 0x2c874(%rip), %rax     # 0x34fe0
movq (%rax), %rdi
leaq 0x1e2e1(%rip), %rsi     # 0x26a57
movl 0xc(%rsp), %edx
movapd %xmm8, %xmm0
movsd %xmm2, 0x20(%rsp)
movsd 0x20(%rsp), %xmm2
movb $0x6, %al
movsd %xmm5, 0x10(%rsp)
callq 0x2240
movsd 0x10(%rsp), %xmm5
movsd 0x20(%rsp), %xmm2
movsd 0x30(%rsp), %xmm9
movsd 0x1b89d(%rip), %xmm6    # 0x24050
movsd 0x18(%rsp), %xmm7
jmp 0x86ef
movq 0x2c81b(%rip), %rax     # 0x34fe0
movq (%rax), %rdi
leaq 0x1e2b1(%rip), %rsi     # 0x26a80
movl 0xc(%rsp), %edx
movapd %xmm2, %xmm0
movapd %xmm5, %xmm1
movb $0x2, %al
callq 0x2240
movq 0x40(%rsp), %rdi
testq %rdi, %rdi
je 0x87f1
callq 0x2210
movq %r15, %rdi
callq 0x2210
movq %r12, %rdi
callq 0x2210
movq %rbx, %rax
addq $0x68, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq