// File: /workspace/github_cpp_makefile_star2_O3/bobverity[P]MavericK/misc.cpp
// Function: approx_gamma(double)
double approx_gamma(double x) {

	if (x <= 0.0)
	{
		std::stringstream os;
        os << "Invalid input argument " << x <<  ". Argument must be positive.";
        throw std::invalid_argument( os.str() );
	}
    
    // Split the function domain into three intervals:
    // (0, 0.001), [0.001, 12), and (12, infinity)
    
    ///////////////////////////////////////////////////////////////////////////
    // First interval: (0, 0.001)
	//
	// For small x, 1/gamma(x) has power series x + gamma x^2  - ...
	// So in this range, 1/gamma(x) = x + gamma x^2 with error on the order of x^3.
	// The relative error over this interval is less than 6e-7.
    
	const double gamma = 0.577215664901532860606512090; // Euler's gamma constant
    
    if (x < 0.001)
        return 1.0/(x*(1.0 + gamma*x));
    
    ///////////////////////////////////////////////////////////////////////////
    // Second interval: [0.001, 12)
    
	if (x < 12.0)
    {
        // The algorithm directly approximates gamma over (1,2) and uses
        // reduction identities to reduce other arguments to this interval.
		
		double y = x;
        int n = 0;
        bool arg_was_less_than_one = (y < 1.0);
        
        // Add or subtract integers as necessary to bring y into (1,2)
        // Will correct for this below
        if (arg_was_less_than_one)
        {
            y += 1.0;
        }
        else
        {
            n = static_cast<int> (floor(y)) - 1;  // will use n later
            y -= n;
        }
        
        // numerator coefficients for approximation over the interval (1,2)
        static const double p[] =
        {
            -1.71618513886549492533811E+0,
            2.47656508055759199108314E+1,
            -3.79804256470945635097577E+2,
            6.29331155312818442661052E+2,
            8.66966202790413211295064E+2,
            -3.14512729688483675254357E+4,
            -3.61444134186911729807069E+4,
            6.64561438202405440627855E+4
        };
        
        // denominator coefficients for approximation over the interval (1,2)
        static const double q[] =
        {
            -3.08402300119738975254353E+1,
            3.15350626979604161529144E+2,
            -1.01515636749021914166146E+3,
            -3.10777167157231109440444E+3,
            2.25381184209801510330112E+4,
            4.75584627752788110767815E+3,
            -1.34659959864969306392456E+5,
            -1.15132259675553483497211E+5
        };
        
        double num = 0.0;
        double den = 1.0;
        int i;
        
        double z = y - 1;
        for (i = 0; i < 8; i++)
        {
            num = (num + p[i])*z;
            den = den*z + q[i];
        }
        double result = num/den + 1.0;
        
        // Apply correction if argument was not initially in (1,2)
        if (arg_was_less_than_one)
        {
            // Use identity gamma(z) = gamma(z+1)/z
            // The variable "result" now holds gamma of the original y + 1
            // Thus we use y-1 to get back the orginal y.
            result /= (y-1.0);
        }
        else
        {
            // Use the identity gamma(z+n) = z*(z+1)* ... *(z+n-1)*gamma(z)
            for (i = 0; i < n; i++)
                result *= y++;
        }
        
		return result;
    }
    
    ///////////////////////////////////////////////////////////////////////////
    // Third interval: [12, infinity)
    
    if (x > 171.624)
    {
		// Correct answer too large to display. Force +infinity.
		double temp = numeric_limits<double>::max();
		return temp*2.0;
    }
    
    return exp(approx_lgamma(x));
}

// Assembly:
pushq %rbp
pushq %r14
pushq %rbx
subq $0x1b0, %rsp            # imm = 0x1B0
movapd %xmm0, %xmm4
xorpd %xmm0, %xmm0
ucomisd %xmm4, %xmm0
jae 0x1c896
movsd 0x1a45d(%rip), %xmm0    # 0x36bc0
ucomisd %xmm4, %xmm0
jbe 0x1c795
movsd 0x1a477(%rip), %xmm1    # 0x36be8
mulsd %xmm4, %xmm1
movsd 0x19ae3(%rip), %xmm0    # 0x36260
addsd %xmm0, %xmm1
mulsd %xmm4, %xmm1
divsd %xmm1, %xmm0
addq $0x1b0, %rsp            # imm = 0x1B0
popq %rbx
popq %r14
popq %rbp
retq
movsd 0x1a42b(%rip), %xmm0    # 0x36bc8
ucomisd %xmm4, %xmm0
jbe 0x1c7c1
movsd 0x19ab5(%rip), %xmm5    # 0x36260
ucomisd %xmm4, %xmm5
jbe 0x1c7d9
movsd 0x19aa7(%rip), %xmm1    # 0x36260
addsd %xmm4, %xmm1
xorl %eax, %eax
jmp 0x1c809
ucomisd 0x1a40f(%rip), %xmm4    # 0x36bd8
jbe 0x1c864
movsd 0x1a3f9(%rip), %xmm0    # 0x36bd0
jmp 0x1c789
movapd %xmm4, %xmm0
movsd %xmm4, (%rsp)
callq 0x3500
movsd 0x19a71(%rip), %xmm5    # 0x36260
movsd (%rsp), %xmm4
cvttsd2si %xmm0, %eax
decl %eax
xorps %xmm0, %xmm0
cvtsi2sd %eax, %xmm0
movapd %xmm4, %xmm1
subsd %xmm0, %xmm1
movsd 0x1a3cf(%rip), %xmm2    # 0x36be0
addsd %xmm1, %xmm2
xorpd %xmm0, %xmm0
xorl %ecx, %ecx
leaq 0x1a59e(%rip), %rdx     # 0x36dc0
leaq 0x1a5d7(%rip), %rsi     # 0x36e00
addsd (%rcx,%rdx), %xmm0
mulsd %xmm2, %xmm0
mulsd %xmm2, %xmm5
addsd (%rcx,%rsi), %xmm5
addq $0x8, %rcx
cmpq $0x40, %rcx
jne 0x1c829
movsd 0x19a13(%rip), %xmm3    # 0x36260
ucomisd %xmm4, %xmm3
divsd %xmm5, %xmm0
addsd %xmm3, %xmm0
jbe 0x1c87d
divsd %xmm2, %xmm0
jmp 0x1c789
movapd %xmm4, %xmm0
callq 0x1c976
addq $0x1b0, %rsp            # imm = 0x1B0
popq %rbx
popq %r14
popq %rbp
jmp 0x35a0
testl %eax, %eax
jle 0x1c789
mulsd %xmm1, %xmm0
addsd %xmm3, %xmm1
decl %eax
jne 0x1c885
jmp 0x1c789
movsd %xmm4, (%rsp)
leaq 0x28(%rsp), %rdi
callq 0x3210
leaq 0x38(%rsp), %rbx
leaq 0x1a4ca(%rip), %rsi     # 0x36d7b
movl $0x17, %edx
movq %rbx, %rdi
callq 0x3390
movq %rbx, %rdi
movsd (%rsp), %xmm0
callq 0x34a0
leaq 0x1a4c1(%rip), %rsi     # 0x36d93
movl $0x1c, %edx
movq %rax, %rdi
callq 0x3390
movl $0x10, %edi
callq 0x3160
movq %rax, %rbx
leaq 0x40(%rsp), %rsi
leaq 0x8(%rsp), %rdi
callq 0x3490
movb $0x1, %bpl
leaq 0x8(%rsp), %rsi
movq %rbx, %rdi
callq 0x3560
xorl %ebp, %ebp
movq 0x246bc(%rip), %rsi     # 0x40fd0
movq 0x246a5(%rip), %rdx     # 0x40fc0
movq %rbx, %rdi
callq 0x3540
movq %rax, %r14
leaq 0x18(%rsp), %rax
movq -0x10(%rax), %rdi
cmpq %rax, %rdi
je 0x1c939
callq 0x3310
testb %bpl, %bpl
jne 0x1c943
jmp 0x1c950
movq %rax, %r14
movq %rbx, %rdi
callq 0x3230
jmp 0x1c950
movq %rax, %r14
movq 0x24629(%rip), %rsi     # 0x40f80
leaq 0x28(%rsp), %rdi
callq 0x3250
leaq 0xa8(%rsp), %rdi
callq 0x30f0
movq %r14, %rdi
callq 0x3580