/*
 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
//
//	LogDD.c
//	
//	Double-double Function Library
//	Copyright: © 1995-96 by Apple Computer, Inc., all rights reserved
//	
//	long double logl( long double x );
//	long double log10l( long double x );
//	long double log2l( long double x );
//	long double log1pl( long double x );
//
//			_LogInnerLD() is exported for use by other functions.
//

#include "math.h"
#include "fp_private.h"
#if (__WANT_LONG_DOUBLE_FORMAT__ - 0L == 128L)
#include "DD.h"

// Floating-point constants

static const double kLn2  = 6.9314718055994529e-1;			// {0x3FE62E42,0xFEFA39EF}
static const double kLn2b = 2.3190468138462996e-17;			// {0x3C7ABC9E,0x3B39803F}
static const double kLn2c = 5.7077084384162121e-34;			// {0x3907B57A,0x079A1934}
//static const double kLn2d = -3.58243221060181156e-50;		// {0xB5AACE93,0xA4EBE5D1}

static const double kBig = 6.755399441055744e+15;			// {0x43380000,0x00000000}
static const double recip15fac = 0.138750138750138750138750e-5; // 1/15!
static const double scaleup = 1.3407807929942597e+154;		// {0x5ff00000,0x00000000}
static const double kLog10eHi = 4.3429448190325182e-1;		// log10(e) {0x3FDBCB7B,0x1526E50E}
static const double kLog10eMid = 1.0983196502167651e-17;	// {0x3C695355,0xBAAAFAD3}
static const double kLog10eLow = 3.7171812331109602e-34;	// {0x38FEE191,0xF71A3015}
static const double kLog2eHi = 1.4426950408889634e+0;		// log2(e) {0x3ff71547,0x652b82fe}
static const double kLog2eMid = 2.0355273740931033e-17;		// {0x3c7777d0,0xffda0d24}
static const double kLog2eLow = -1.0614659956117258e-33;	// {0xb9160bb8,0xa5442ab9}

static const Double kInfinityu = {{0x7ff00000, 0x0}};

static const double coeff[] = {720720.0, -360360.0, 240240.0, -180180.0,
	144144.0, -120120.0, 102960.0, -90090.0, 80080.0, -72072.0, 65520.0,
	-60060.0, 55440.0, -51480.0, 48048.0, -45045.0};

extern uint32_t LogTableLD[];

/*
static uint32_t LogTableLD[] = {
	
	0x3FF02000, 0x00026746,  0x3FEFC07F, 0x01F74C65,  0x3F7FE02A, 0x6D72E887,  0x3B3F98BF, 0x5125A0D9,
	0x3FF06000, 0x000C15E4,  0x3FEF4465, 0x9E332ED4,  0x3F97B91B, 0x0AC9738C,  0x3B56E8A1, 0xB39F02BA,  
	0x3FF0A000, 0x0014A543,  0x3FEECC07, 0xB2DBAE11,  0x3FA39E87, 0xBC7A8F8A,  0x3B626302, 0xA58A48BC,  
	0x3FF0E000, 0x0011E06B,  0x3FEE573A, 0xC8E1C131,  0x3FAB42DD, 0x7337D5A8,  0x3B6B338C, 0x5DDA3A53,  
	0x3FF12000, 0x0013DA38,  0x3FEDE5D6, 0xE3D5DDB6,  0x3FB16536, 0xEFCC407C,  0x3B715E8E, 0x0264EABA,  
	0x3FF16000, 0x000A2948,  0x3FED77B6, 0x54A6F07C,  0x3FB51B07, 0x3F9BCF0A,  0x3B74DB56, 0xDE56C8EF,  
	0x3FF1A000, 0x0012B0CD,  0x3FED0CB5, 0x8F4FF21D,  0x3FB8C345, 0xD7411583,  0x3B786152, 0x587782F8,  
	0x3FF1E000, 0x000EE3A6,  0x3FECA4B3, 0x054705AF,  0x3FBC5E54, 0x90310477,  0x3B7C237F, 0x7657770C,
	0x3FF22000, 0x00153135,  0x3FEC3F8F, 0x01A2F19D,  0x3FBFEC91, 0x33073D1A,  0x3B7FACCC, 0x73F05939,
	0x3FF26000, 0x00241909,  0x3FEBDD2B, 0x895D49D0,  0x3FC1B72A, 0xD62ADC8D,  0x3B817554, 0xB20DE7D5,   
	0x3FF2A000, 0x002BCBD4,  0x3FEB7D6C, 0x3D998F3A,  0x3FC371FC, 0x214B8C8E,  0x3B82E934, 0xDA5043A5,   
	0x3FF2E000, 0x000DA3AC,  0x3FEB2036, 0x4058E6DD,  0x3FC526E5, 0xE3FE32D7,  0x3B851132, 0xC8E084BC,   
	0x3FF32000, 0x0021AE4E,  0x3FEAC570, 0x1A964A93,  0x3FC6D60F, 0xE7FB3D90,  0x3B86BC1B, 0x66F15A74,   
	0x3FF36000, 0x00196038,  0x3FEA6D01, 0xA6AD7E27,  0x3FC87FA0, 0x65C86E05,  0x3B887653, 0xDF907BC0,   
	0x3FF3A000, 0x0021FEB5,  0x3FEA16D3, 0xF94D19C3,  0x3FCA23BC, 0x20C06EFE,  0x3B8A0FA9, 0x3CB516A7,   
	0x3FF3E000, 0x000D0738,  0x3FE9C2D1, 0x4ED3BE0F,  0x3FCBC286, 0x7481747C,  0x3B8BA61B, 0x516322DC,   
	0x3FF42000, 0x0026BB6E,  0x3FE970E4, 0xF7DBC1DE,  0x3FCD5C21, 0x6C46142A,  0x3B8CF4E4, 0x5F70ECE3,   
	0x3FF46000, 0x001A700A,  0x3FE920FB, 0x49B04706,  0x3FCEF0AD, 0xCC826F72,  0x3B8ECCCF, 0xC2085FC9,   
	0x3FF4A000, 0x0013F66B,  0x3FE8D301, 0x8D1811ED,  0x3FD04025, 0x94F2C209,  0x3B903E90, 0x5D16156B,   
	0x3FF4E000, 0x0011F43B,  0x3FE886E5, 0xF09697FA,  0x3FD1058B, 0xF9E55645,  0x3B90EE57, 0x086B5AEC,   
	0x3FF52000, 0x00219250,  0x3FE83C97, 0x7A8C3A9F,  0x3FD1C898, 0xC1CF4F30,  0x3B919E40, 0xEDF99C22,   
	0x3FF56000, 0x0025844D,  0x3FE7F405, 0xFCD7747F,  0x3FD2895A, 0x144EDB60,  0x3B926EE9, 0x301CD24B,   
	0x3FF5A000, 0x00426EFC,  0x3FE7AD22, 0x08983088,  0x3FD347DD, 0x9B5D1A24,  0x3B934057, 0xA2FCAF6F,   
	0x3FF5E000, 0x0023BDD6,  0x3FE767DC, 0xE40E6B97,  0x3FD40430, 0x86EF39B2,  0x3B93FF8C, 0x8952354B,   
	0x3FF62000, 0x003B535A,  0x3FE72428, 0x7F08D1CD,  0x3FD4BE5F, 0x96231467,  0x3B94825C, 0xC3371E24,   
	0x3FF66000, 0x001DA1F2,  0x3FE6E1F7, 0x6B24E9B5,  0x3FD57677, 0x179A1CC5,  0x3B956330, 0xFA60E69E,   
	0x3FF6A000, 0x0037A0C1,  0x3FE6A13C, 0xD11BCEC4,  0x3FD62C82, 0xF35722D2,  0x3B95FA0B, 0x10B586EE,   
	0x3FF6E000, 0x001B76C0,  0x3FE661EC, 0x6A364397,  0x3FD6E08E, 0xAA78789F,  0x3B96D198, 0x6621D929,   
	0x3FF72000, 0x003ED5D5,  0x3FE623FA, 0x76C53936,  0x3FD792A5, 0x608B2E43,  0x3B976089, 0x3B698591,   
	0x3FF76000, 0x003610DA,  0x3FE5E75B, 0xB89D6C37,  0x3FD842D1, 0xDAB292E6,  0x3B982832, 0xEB89C0AF,   
	0x3FF7A000, 0x0016DE9E,  0x3FE5AC05, 0x6AEC6020,  0x3FD8F11E, 0x877456E8,  0x3B98DD6D, 0x9D2A6F65,   
	0x3FF7E000, 0x004C046C,  0x3FE571ED, 0x3C0C2377,  0x3FD99D95, 0x81E3A6B3,  0x3B99848C, 0x4BFDF0E3,   
	0x3FE82000, 0x00393E5C,  0x3FF53909, 0x48C1B475,  0xBFD21445, 0x6C76DD04,  0x3B91F8C3, 0x3DC493A4,   
	0x3FE86000, 0x003EFB2C,  0x3FF50150, 0x14CB09F5,  0xBFD16B5C, 0xCB079DCA,  0x3B910FAA, 0x072FC35E,   
	0x3FE8A000, 0x003E6A46,  0x3FF4CAB8, 0x86F0FC55,  0xBFD0C42D, 0x66BF2B99,  0x3B905CD8, 0x563746EF,   
	0x3FE8E000, 0x00477E7A,  0x3FF49539, 0xE377A7F3,  0xBFD01EAE, 0x556ED4C7,  0x3B8F705E, 0xB9C5B7AA,   
	0x3FE92000, 0x0037DAA4,  0x3FF460CB, 0xC7C8826B,  0xBFCEF5AD, 0xE3C07315,  0x3B8DE963, 0x957971E5,   
	0x3FE96000, 0x0031BEA8,  0x3FF42D66, 0x25AD915C,  0xBFCDB13D, 0xAFD99B61,  0x3B8D27CB, 0xE3668C21,   
	0x3FE9A000, 0x0031523A,  0x3FF3FB01, 0x3F899EFB,  0xBFCC6FFB, 0xC5F9B1E6,  0x3B8BE9E5, 0x5F85EE97,   
	0x3FE9E000, 0x00343918,  0x3FF3C995, 0xA453BC21,  0xBFCB31D8, 0x56597734,  0x3B8AF392, 0xD52D7571,   
	0x3FEA2000, 0x00199638,  0x3FF3991C, 0x2C054DA3,  0xBFC9F6C4, 0x068B3974,  0x3B89EE78, 0x9ACC9199,   
	0x3FEA6000, 0x0033C6EC,  0x3FF3698D, 0xF3B7EB7F,  0xBFC8BEAF, 0xEA3DB758,  0x3B889436, 0x63D97AE8,   
	0x3FEAA000, 0x00276244,  0x3FF33AE4, 0x5B3B4ABA,  0xBFC7898D, 0x8486F5D7,  0x3B86F513, 0xCA9E0A1C,   
	0x3FEAE000, 0x001DE89C,  0x3FF30D19, 0x011B9DF1,  0xBFC6574E, 0xBDFDA066,  0x3B86222A, 0xB12C0834,   
	0x3FEB2000, 0x0037A536,  0x3FF2E025, 0xC024C7B9,  0xBFC527E5, 0xE39B1FE9,  0x3B8458D5, 0xE445BFCF,   
	0x3FEB6000, 0x000CCE1A,  0x3FF2B404, 0xACF86B95,  0xBFC3FB45, 0xA55D490D,  0x3B83C23D, 0xACE1BFEC,   
	0x3FEBA000, 0x002CA378,  0x3FF288B0, 0x126ABD40,  0xBFC2D161, 0x0BB7AC3B,  0x3B82B40D, 0x1D2C66D4,   
	0x3FEBE000, 0x00343A2E,  0x3FF25E22, 0x705E28E9,  0xBFC1AA2B, 0x7D342442,  0x3B812B63, 0x558826AF,   
	0x3FEC2000, 0x0038FF08,  0x3FF23456, 0x7875D88C,  0xBFC08598, 0xB49AD49F,  0x3B7F3F8E, 0x64C28802,   
	0x3FEC6000, 0x001B33E6,  0x3FF20B47, 0x0C567468,  0xBFBEC739, 0x8214A4A6,  0x3B7E277B, 0xA41F0AAF,   
	0x3FECA000, 0x00119564,  0x3FF1E2EF, 0x3B34BBBD,  0xBFBC8858, 0x011F0A29,  0x3B7C3C3D, 0xB4C961E9,   
	0x3FECE000, 0x001996BC,  0x3FF1BB4A, 0x4037367A,  0xBFBA4E76, 0x3FCEDEB6,  0x3B7989B5, 0x3403569D,   
	0x3FED2000, 0x0027E986,  0x3FF19453, 0x80748B78,  0xBFB8197E, 0x2DE212FA,  0x3B779AB6, 0x2FAC97BD,   
	0x3FED6000, 0x0019EB78,  0x3FF16E06, 0x8933124A,  0xBFB5E95A, 0x4CB5AE64,  0x3B754C62, 0x7502E4A2,   
	0x3FEDA000, 0x0012ADBC,  0x3FF1485F, 0x0DFFE7A7,  0xBFB3BDF5, 0xA73085C0,  0x3B7355A8, 0xD9528443,   
	0x3FEDE000, 0x0020DF02,  0x3FF12358, 0xE74A54DA,  0xBFB1973B, 0xD02CA8E2,  0x3B711C99, 0x788FFA22,   
	0x3FEE2000, 0x001D96D8,  0x3FF0FEF0, 0x10EE3E82,  0xBFAEEA31, 0xBE0FD392,  0x3B6E25BF, 0x730D5F04,   
	0x3FEE6000, 0x000AA830,  0x3FF0DB20, 0xA8895CA2,  0xBFAAAEF2, 0xD0476EBF,  0x3B6A4412, 0x0CC9DB9D,   
	0x3FEEA000, 0x001B75A4,  0x3FF0B7E6, 0xEC16A042,  0xBFA67C94, 0xF109A73B,  0x3B653651, 0xD572AAB2,   
	0x3FEEE000, 0x0017A242,  0x3FF0953F, 0x38F457BA,  0xBFA252F3, 0x2E052CD8,  0x3B60EC4F, 0x64C2DE39,   
	0x3FEF2000, 0x000CF922,  0x3FF07326, 0x0A411C88,  0xBF9C63D2, 0xEA69DB02,  0x3B5C53BD, 0xE86F9708,   
	0x3FEF6000, 0x000BAB68,  0x3FF05197, 0xF7D12236,  0xBF9432A9, 0x241B2F6E,  0x3B53F5BE, 0x52B566FB,   
	0x3FEFA000, 0x000E227C,  0x3FF03091, 0xB51821B3,  0xBF882448, 0x9FF5499F,  0x3B472044, 0xA44CCF4D,   
	0x3FF00000, 0x00000000,  0x3FF00000, 0x00000000,  0x00000000, 0x00000000,  0x00000000, 0x00000000   
};
*/

struct LogTableEntry {
	double x;
	double u;    // u = 1/x
	double fhead;
	double ftail;
};


long double logl( long double x )
{
	double fpenv;
	DblDbl u;
	double extra;
	
    FEGETENVD(fpenv);
    FESETENVD(0.0);
	
	u.f = x;
	
	if (((u.i[0] & 0x7fffffffu) | u.i[1]) == 0) {		// x = 0.0
		u.d[0] = -1.0/__FABS(u.d[0]);
		u.d[1] = 0.0;
                FESETENVD(fpenv);
		return u.f;
	}
	if (u.i[0] < 0x7ff00000u) {						// x is finite and positive
		if ((u.i[0] == 0x3ff00000) && (u.i[1] | u.i[2] | u.i[3]) == 0) {
                        FESETENVD(fpenv);
			return 0.0L;								// x = 1.0
		}
		u.f = _LogInnerLD(u.d[0], u.d[1], 0.0, &extra, 0);
                FESETENVD(fpenv);
		return u.f;
	}
	if (u.d[0] != u.d[0]) {
                FESETENVD(fpenv);
		return x;										// NaN case
	}
	if ((u.i[0] & 0xfff00000u) == 0x7ff00000u) {
                FESETENVD(fpenv);
		return x;										// +infinity
	}
	else {    											// x < 0.0
		u.d[0] = 0.0 * kInfinityu.f + u.d[0];
		u.d[1] = 0.0;
                FESETENVD(fpenv);
		return u.f;
	}
}

long double log10l( long double x )
{
	double fpenv;
	DblDbl u;
	double result, resmid, resbot;
	double uu, vv, ww, xx, yy, c, top, bottom;
	
        FEGETENVD(fpenv);
        FESETENVD(0.0);
	
	u.f = x;
	
	if (((u.i[0] & 0x7fffffffu) | u.i[1]) == 0) {		// x = 0.0
		u.d[0] = -1.0/__FABS(u.d[0]);
		u.d[1] = 0.0;
                FESETENVD(fpenv);
		return u.f;
	}
	if (u.i[0] < 0x7ff00000u) {						// x is finite and positive
		if ((u.i[0] == 0x3ff00000) && (u.i[1] | u.i[2] | u.i[3]) == 0) {
                        FESETENVD(fpenv);
			return 0.0L;								// x = 1.0
		}
		u.f = _LogInnerLD(u.d[0], u.d[1], 0.0, &resbot, 1);
		result = u.d[0];
		resmid = u.d[1];

		uu = __FADD( result * kLog10eLow, __FADD( resmid * kLog10eMid, resbot * kLog10eHi ) );
		vv = result * kLog10eMid;
		ww = __FMSUB( result, kLog10eMid, vv);
		xx = resmid * kLog10eHi;
		yy = __FMSUB( resmid, kLog10eHi, xx );
		c = ww + yy + uu;
		top = result * kLog10eHi;
		ww = __FMSUB( result, kLog10eHi, top );
		uu = __FADD( vv, xx );
		if (__FABS(xx) > __FABS(vv))
			c =  xx - uu + vv + c;
		else
			c = vv - uu + xx + c;
		bottom = __FADD( uu, ww );
		if (__FABS(ww) > __FABS(uu))
			c = ww - bottom + uu + c;
		else
			c = uu - bottom + ww + c;
		u.d[0] = __FADD( top, bottom );
		u.d[1] = top - u.d[0] + bottom + c;
                FESETENVD(fpenv);
		return u.f;
	}
	if (u.d[0] != u.d[0]) {
                FESETENVD(fpenv);
		return x;										// NaN case
	}
	if ((u.i[0] & 0xfff00000u) == 0x7ff00000u) {
                FESETENVD(fpenv);
		return x;										// +infinity
	}
	else {												// x < 0.0
		u.d[0] = 0.0 * kInfinityu.f + u.d[0];
		u.d[1] = 0.0;
                FESETENVD(fpenv);
		return u.f;
	}
}

long double log2l( long double x )
{
	double fpenv;
	DblDbl u;
	double result, resmid, resbot;
	double uu, vv, ww, xx, yy, c, top, bottom;
	
        FEGETENVD(fpenv);
        FESETENVD(0.0);
	
	u.f = x;
	
	if (((u.i[0] & 0x7fffffffu) | u.i[1]) == 0) {		// x = 0.0
		u.d[0] = -1.0/__FABS(u.d[0]);
		u.d[1] = 0.0;
                FESETENVD(fpenv);
		return u.f;
	}
	if (u.i[0] < 0x7ff00000u) {						// x is finite and positive
		if ((u.i[0] == 0x3ff00000) && (u.i[1] | u.i[2] | u.i[3]) == 0) {
                        FESETENVD(fpenv);
			return 0.0L;								// x = 1.0
		}
		u.f = _LogInnerLD(u.d[0], u.d[1], 0.0, &resbot, 1);
		result = u.d[0];
		resmid = u.d[1];
		uu = __FADD( result * kLog2eLow, __FADD( resmid * kLog2eMid, resbot * kLog2eHi ) );
		vv = result * kLog2eMid;
		ww = __FMSUB( result, kLog2eMid, vv );
		xx = resmid * kLog2eHi;
		yy = __FMSUB( resmid, kLog2eHi, xx );
		c = ww + yy + uu;
		top = result * kLog2eHi;
		ww = __FMSUB( result, kLog2eHi, top );
		uu = __FADD( vv, xx );
		if (__FABS(xx) > __FABS(vv))
			c =  xx - uu + vv + c;
		else
			c = vv - uu + xx + c;
		bottom = __FADD( uu, ww );
		if (__FABS(ww) > __FABS(uu))
			c = ww - bottom + uu + c;
		else
			c = uu - bottom + ww + c;
		u.d[0] = __FADD( top, bottom );
		u.d[1] = top - u.d[0] + bottom + c;
                FESETENVD(fpenv);
		return u.f;
	}
	if (u.d[0] != u.d[0]) {
                FESETENVD(fpenv);
		return x;										// NaN case
	}
	if ((u.i[0] & 0xfff00000u) == 0x7ff00000u) {
                FESETENVD(fpenv);
		return x;										// +infinity
	}
	else {												// x < 0.0
		u.d[0] = 0.0 * kInfinityu.f + u.d[0];
		u.d[1] = 0.0;
                FESETENVD(fpenv);
		return u.f;
	}
}

long double log1pl( long double x )
{
	double fpenv;
	DblDbl u;
	double high, mid, low, extra, a, b, c, d, e, f;
	
	u.f = x;
	
	if (u.d[0] != u.d[0])								// x = NaN
		return x;
	if (((u.i[0] & 0x7fffffffu) | u.i[1]) == 0)		// x = 0.0
		return x;
        FEGETENVD(fpenv);
        FESETENVD(0.0);
	if ((x > -1.0L) && ((u.i[0] & 0x7ff00000) != 0x7ff00000)) { // -1<x< Inf
		if (1.0 > __FABS(u.d[0])) {						// 1.0 > |head| > |tail|
			high = 1.0;
			mid = u.d[0];
			low = u.d[1];
		}
		else if (__FABS(u.d[1]) > 1.0) {				// |head| > |tail| > 1.0
			high = u.d[0];
			mid = u.d[1];
			low = 1.0;
		}
		else {											// |head| >= 1.0 >= |low|
			high = u.d[0];
			mid = 1.0;
			low = u.d[1];
		}
		a = __FADD( mid, low );
		b = (mid - a) + low;
		c = __FADD( high, a );
		d = (high - c) + a;
		e = __FADD( d, b );
		f = (d - e) + b;
		u.f = _LogInnerLD(c, e, f, &extra, 2);
                FESETENVD(fpenv);
		return u.f;
	}
	if ((u.i[0] == 0xbff00000) && (u.i[1] | (u.i[2] & 0x7fffffffu)  | u.i[3]) == 0) {
														// x = -1.0
		u.d[0] = -INFINITY;
		u.d[1] = 0.0;
                FESETENVD(fpenv);
		return u.f;
	}
	if ((u.i[0] & 0xfff00000u) == 0x7ff00000u) {
                FESETENVD(fpenv);
		return x;										// x = +inf
	}
	else {												// x < -1.0
		u.d[0] = 0.0 * kInfinityu.f + u.d[0];
		u.d[1] = 0.0;
                FESETENVD(fpenv);
		return u.f;
	}
}

//**********************************************************************************
//
//    FUNCTION:  long double _LogInnerLD(double alpha, double beta, double gamma,
//               double *extra, int logtype)
//    
//    DESCRIPTION: This routine is called internally by the following functions:
//
//                 long double Log(long double);
//                 long double Log2(long double);
//                 long double Log10(long double);
//                 long double Log1Plus(long double);
//                 long double Power(long double, long double);
//
//                 1) logtype = 0 (called by Log()):
//                    on entry: (alpha, beta)
//                    on exit:  (head, tail) of  Log(alpha + beta)
//
//                 2) logtype = 1 (called by Power(), Log10(), Log2()):
//                    on entry: (alpha, beta)
//                    on exit:  (head, tail, extra) of Log(alpha + beta)
//
//                 3) logtype = 2 (called by Log1Plus()):
//                    on entry: (alpha, beta, gamma)
//                    on exit:  (head, tail) of Log(alpha + beta + gamma)
//
//                 This routine assumes that the rounding direction on entry
//                 is round-to-nearest,  and NaN, infinity, Signed zeros, 
//                 negative values have been pre-filtered so that the input
//                 is a postive normal/denormal values.
//
//*********************************************************************************

long double _LogInnerLD(double alpha, double beta, double gamma, double *extra,
						int logtype)
{
	int i, iexp, ndx;
	double head, tail, redarg, diff, diff2, quot, arg, rem;
	double reciparg, e, sum, arglow, u, x, y, remlost, arglost, temp, suml;
	double argl, argl2, hi, prod, c, enlog2hi, w, enlog2mid, prodl, sum2;
	double sum3, enlog2low, small, petit, smallres, summid, resmid, summid1;
	double resmid1, summid2, resmid2, top, resmid3, resmid4, bottom, resmid5;
	double residual, result, residual2, resmidtemp;
	Double test, rscale, scale;
	DblDbl out;
	struct LogTableEntry *TblPtr = (struct LogTableEntry *)LogTableLD;
	
	test.f = alpha;
	tail = beta;
	rscale.f = 0.0;
	scale.f = kBig;
	if (test.i[0] >= 0x07000000u) {   					// alpha > 2^(-911)
		iexp = (test.i[0] & 0x7ff00000);
		rscale.i[0] = 2046 * 1048576 - iexp;
		scale.i[1] = (iexp >> 20);
	}
	else {  											// alpha < 2^(-911)
		test.f *= scaleup;
		tail *= scaleup;
		if (logtype == 2) gamma *= scaleup;
		head = __FADD( test.f, tail );
		tail = test.f - head + tail;
		iexp = (test.i[0] & 0x7ff00000);
		rscale.i[0] = 2046 * 1048576 - iexp;
		scale.i[1] = (iexp >> 20);
		scale.f -= 512.0;
	}
	ndx = ((test.i[0] & 0x000fc000) >> 14);
	if (rscale.i[0] == 0)
		rscale.i[0] = 524288;
	head = test.f * rscale.f;
	tail = tail * rscale.f;
	if (logtype == 2)
		gamma = gamma * rscale.f;
	scale.f -= kBig + 1023.0;
	if (test.i[0] & 0x00080000) { 
		scale.f  += 1.0;
		head *= 0.5;
		tail *= 0.5;
		if (logtype == 2)
			gamma *= 0.5;
	}
	if (ndx == 0)
		if ((test.i[0] & 0x000fffffu) < 0x00002000u)
			ndx = 63;
	redarg = head - TblPtr[ndx].x;              
	diff = __FADD( redarg, tail );
	diff2 = redarg - diff + tail + gamma;
	quot = __FMUL( diff, TblPtr[ndx].u );                
	arg = __FMADD( __FNMSUB( quot, TblPtr[ndx].x, diff ), TblPtr[ndx].u, quot ); // quot + (diff - quot * TblPtr[ndx].x) * TblPtr[ndx].u;
	remlost = __FNMSUB( arg, TblPtr[ndx].x, diff );
	rem = __FADD( remlost,  diff2 );
	reciparg = 1.0 - arg;
	e = arg * arg;
	sum = __FMADD( arg, coeff[15], coeff[14] ); // coeff[14] + arg * coeff[15];
	arglow = rem * TblPtr[ndx].u;
	u = scale.f * kLn2c;
	x = scale.f * kLn2b;
	y = __FMSUB( scale.f, kLn2b, x );
	if (__FABS(remlost) > __FABS(diff2))
		remlost = remlost - rem + diff2;
	else
		remlost = diff2 - rem + remlost;
	arglost = __FNMSUB( arglow, TblPtr[ndx].x, rem );
	arglost = __FADD( remlost, arglost );
	arglost = arglost * TblPtr[ndx].u;
	sum = __FMADD( arg, sum, coeff[13] ); // coeff[13] + arg * sum;
	reciparg = __FMADD( reciparg, e, reciparg ); // reciparg + reciparg * e;
	sum = __FMADD( arg, sum, coeff[12] ); // coeff[12] + arg * sum;
	e = e * e;
	sum = __FMADD( arg, sum, coeff[11] ); // coeff[11] + arg * sum;
	reciparg = __FMADD( reciparg, e, reciparg ); // reciparg + reciparg * e;
	sum = __FMADD( arg, sum, coeff[10] ); // coeff[10] + arg * sum;
	e = e * e;
	sum = __FMADD( arg, sum, coeff[ 9] ); // coeff[9] + arg * sum;
	reciparg = __FMADD( reciparg, e, reciparg ); // reciparg + reciparg * e;
	temp = __FMADD( arg, sum, coeff[8] ); // coeff[8] + arg * sum;
	suml = __FSUB( coeff[8], temp );
	suml = __FMADD( arg, sum, suml ); // suml + arg * sum;
	sum = temp;
	argl = arglow * reciparg;
	argl2 = (__FNMSUB( argl, arg, arglow - argl) + arglost) *reciparg; // ((arglow - argl - argl * arg) + arglost) * reciparg;
	for (i = 7; i >= 2; i--) {
		hi = __FMADD( sum, arg, coeff[i] );
		suml = __FMADD( suml, arg, __FMADD( sum, arg, coeff[i] - hi ) ); // coeff[i] - hi + sum*arg + suml*arg;
		sum = hi;
	}
	prod = sum * recip15fac;
	c = u + y;
	enlog2hi = __FMUL( scale.f, kLn2 );
	w = __FMSUB( scale.f , kLn2 , enlog2hi );
	enlog2mid = __FADD( x , w );
	prodl = (__FNMSUB( prod, coeff[0], sum ) + suml) * recip15fac; // (sum - prod * coeff[0] + suml) * recip15fac;
	sum2 = __FMSUB( prod, arg, 0.5);
	suml = __FMADD( prodl, arg, __FMADD( prod, arg, __FMSUB( -1.0, 0.5, sum2 ) ) ); // -1.0 * 0.5 - sum2 + prod * arg + prodl * arg;
	sum3 = __FMADD( sum2, arg, (suml * arg) );
	suml = __FMADD( suml, arg, __FMSUB( sum2, arg, sum3 ) ); // sum2 * arg - sum3 + (suml * arg);
	sum = __FMADD( sum3, arg, (suml * arg) );
	suml = __FMADD( suml, arg, __FMSUB( sum3, arg, sum ) ); // sum3 * arg - sum + (suml * arg);
	if (scale.f == 0.0) {
		top = __FADD( arg,  sum );
		resmid = arg - top + sum;
		small = __FADD( suml, argl );
		smallres = argl - small + suml;
		if (TblPtr[ndx].x == 1.0) {
			bottom = __FADD( resmid, small );
			residual2 = smallres + argl2;
			residual = resmid - bottom + small + residual2;
		}
		else {
			hi = __FADD( TblPtr[ndx].fhead, top );
			resmid2 = TblPtr[ndx].fhead - hi + top + resmid;
			petit = small;
			bottom = __FADD( resmid2,  petit );
			if (__FABS(resmid2) > __FABS(petit))
				residual = resmid2-bottom+petit+smallres+argl2+TblPtr[ndx].ftail;
			else
				residual = petit-bottom+resmid2+smallres+argl2+TblPtr[ndx].ftail;
			top = hi;
		}
	}
	else {
		if (__FABS(x) > __FABS(w))
			enlog2low = x - enlog2mid + w + c;
		else
			enlog2low = w - enlog2mid + x + c;
		small = __FADD( argl, suml );
		petit = TblPtr[ndx].ftail + enlog2low + argl2 + small;
		smallres = argl - small + suml;
		summid = __FADD( sum, enlog2mid );
		if (__FABS(sum) > __FABS(enlog2mid)) 
			resmid = sum - summid + enlog2mid;
		else                                   
			resmid = enlog2mid - summid + sum;
		summid1 = __FADD( arg, summid );
		resmid1 = arg - summid1 + summid;
		summid2 = __FADD( summid1, TblPtr[ndx].fhead );
		resmid2 = TblPtr[ndx].fhead - summid2 + summid1 + resmid1;
		petit = petit + resmid + smallres;
		top = __FADD( enlog2hi, summid2 );
		resmid3 = enlog2hi - top + summid2;
		resmid4 = __FADD( resmid3, resmid2 );
		bottom = __FADD( petit, resmid4 );
		resmid5 = resmid3 - resmid4 + resmid2;
		residual = resmid4 - bottom + petit + resmid5;
	}

	result = __FADD( top, bottom );

	if (logtype != 1) 
		resmid = top - result + bottom + residual;
	else {  											// logtype = 1
		resmidtemp = top - result + bottom;
		resmid = __FADD( resmidtemp,  residual );
		*extra = resmidtemp - resmid + residual;
	}

	out.d[0] = result;
	out.d[1] = resmid;
	return out.f;
}
#endif
