// File: /workspace/github_cpp_makefile_star2_O2/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 0x1664c
movsd 0x11637(%rip), %xmm0    # 0x27b50
ucomisd %xmm4, %xmm0
jbe 0x1654b
movsd 0x11651(%rip), %xmm1    # 0x27b78
mulsd %xmm4, %xmm1
movsd 0x10cdd(%rip), %xmm0    # 0x27210
addsd %xmm0, %xmm1
mulsd %xmm4, %xmm1
divsd %xmm1, %xmm0
addq $0x1b0, %rsp            # imm = 0x1B0
popq %rbx
popq %r14
popq %rbp
retq
movsd 0x11605(%rip), %xmm0    # 0x27b58
ucomisd %xmm4, %xmm0
jbe 0x16577
movsd 0x10caf(%rip), %xmm5    # 0x27210
ucomisd %xmm4, %xmm5
jbe 0x1658f
movsd 0x10ca1(%rip), %xmm1    # 0x27210
addsd %xmm4, %xmm1
xorl %eax, %eax
jmp 0x165bc
ucomisd 0x115e9(%rip), %xmm4    # 0x27b68
jbe 0x16619
movsd 0x115d3(%rip), %xmm0    # 0x27b60
jmp 0x1653f
movapd %xmm4, %xmm0
movsd %xmm4, (%rsp)
callq 0x3530
movsd 0x10c6b(%rip), %xmm5    # 0x27210
movsd (%rsp), %xmm4
cvttsd2si %xmm0, %eax
decl %eax
cvtsi2sd %eax, %xmm0
movapd %xmm4, %xmm1
subsd %xmm0, %xmm1
movsd 0x115ac(%rip), %xmm2    # 0x27b70
addsd %xmm1, %xmm2
xorpd %xmm0, %xmm0
xorl %ecx, %ecx
leaq 0x1179b(%rip), %rdx     # 0x27d70
leaq 0x117d4(%rip), %rsi     # 0x27db0
cmpq $0x40, %rcx
je 0x165fa
addsd (%rcx,%rdx), %xmm0
mulsd %xmm2, %xmm0
mulsd %xmm2, %xmm5
addsd (%rcx,%rsi), %xmm5
addq $0x8, %rcx
jmp 0x165dc
movsd 0x10c0e(%rip), %xmm3    # 0x27210
ucomisd %xmm4, %xmm3
divsd %xmm5, %xmm0
addsd %xmm3, %xmm0
jbe 0x16632
divsd %xmm2, %xmm0
jmp 0x1653f
movapd %xmm4, %xmm0
callq 0x16700
addq $0x1b0, %rsp            # imm = 0x1B0
popq %rbx
popq %r14
popq %rbp
jmp 0x35c0
xorl %ecx, %ecx
testl %eax, %eax
cmovgl %eax, %ecx
subl $0x1, %ecx
jb 0x1653f
mulsd %xmm1, %xmm0
addsd %xmm3, %xmm1
jmp 0x16639
movsd %xmm4, (%rsp)
leaq 0x28(%rsp), %rdi
callq 0x3250
leaq 0x38(%rsp), %rdi
leaq 0x116a4(%rip), %rsi     # 0x27d0b
callq 0x3350
movq %rax, %rdi
movsd (%rsp), %xmm0
callq 0x34d0
leaq 0x116a3(%rip), %rsi     # 0x27d23
movq %rax, %rdi
callq 0x3350
pushq $0x10
popq %rdi
callq 0x3190
movq %rax, %rbx
leaq 0x40(%rsp), %rsi
leaq 0x8(%rsp), %rdi
callq 0x34b0
movb $0x1, %bpl
leaq 0x8(%rsp), %rsi
movq %rbx, %rdi
callq 0x3580
xorl %ebp, %ebp
movq 0x1d915(%rip), %rsi     # 0x33fd0
movq 0x1d8fe(%rip), %rdx     # 0x33fc0
movq %rbx, %rdi
callq 0x3560
movq %rax, %r14
leaq 0x8(%rsp), %rdi
callq 0x3170
testb %bpl, %bpl
jne 0x166e1
jmp 0x166ee
movq %rax, %r14
movq %rbx, %rdi
callq 0x3270
jmp 0x166ee
movq %rax, %r14
leaq 0x28(%rsp), %rdi
callq 0x3290
movq %r14, %rdi
callq 0x35a0