/***
*sqrt.c - square root
*
*	Copyright (c) 1991-1991, Microsoft Corporation.	All rights reserved.
*
*Purpose:
*
*Revision History:
*   8-15-91	GDP	written
*   1-29-91	GDP	Kahan's algorithm for final rounding
*   3-11-92	GDP	new interval and initial approximation
*
*******************************************************************************/
#ifndef R4000

#include <math.h>
#include <trans.h>

//
// Coefficients for initial approximation (Hart & al)
//

static double p00 =  .2592768763e+0;
static double p01 =  .1052021187e+1;
static double p02 = -.3163221431e+0;


/***
*double sqrt(double x) - square root
*
*Purpose:
*   Compute the square root of a number.
*   This function should be provided by the underlying
*   hardware (IEEE spec).
*Entry:
*
*Exit:
*
*Exceptions:
*  I P
*******************************************************************************/
double sqrt(double x)
{
    unsigned int savedcw, sw;
    double result,t;
    unsigned int stat,rc;

    savedcw = _ctrlfp(ICW, IMCW);

    if (IS_D_SPECIAL(x)){
	switch (_sptype(x)) {
	case T_PINF:
	    RETURN(savedcw, x);
	case T_QNAN:
	    return _handle_qnan1(OP_SQRT, x, savedcw);
	case T_SNAN:
	    return _except1(FP_I,OP_SQRT,x,QNAN_SQRT,savedcw);
	}
	/* -INF will be handled in the x<0 case */
    }
    if (x < 0.0) {
	return _except1(FP_I, OP_SQRT, x, QNAN_SQRT,savedcw);
    }

    if (x == 0.0) {
	RETURN (savedcw, x);
    }


    result = _fsqrt(x);

    _ctrlfp(IRC_DOWN, IMCW_RC);


    //
    // Kahan's algorithm
    //

    sw = _clrfp();
    t = x / result;
    stat = _statfp();
    if (! (stat & ISW_INEXACT))	{
	// exact
	if (t == result) {
	    _set_statfp(sw);		// restore status word
	    RETURN(savedcw, result);
	}
	else {
	    // t = t-1
	    if (*D_LO(t) == 0) {
		(*D_HI(t)) --;
	    }
	    (*D_LO(t)) --;
	}

    }

    rc = savedcw & IMCW_RC;
    if (rc == IRC_UP  || rc == IRC_NEAR) {
	// t = t+1
	(*D_LO(t)) ++;
	if (*D_LO(t) == 0) {
	    (*D_HI(t)) ++;
	}
	if (rc == IRC_UP) {
	    // y = y+1
	    (*D_LO(t)) ++;
	    if (*D_LO(t) == 0) {
		(*D_HI(t)) ++;
	    }
	}
    }

    result = 0.5 * (t + result);

    _set_statfp(sw | ISW_INEXACT);	// update status word
    RETURN_INEXACT1(OP_SQRT, x, result, savedcw);
}



/***
* _fsqrt - non IEEE conforming square root
*
*Purpose:
*   compute a square root of a normal number without performing
*   IEEE rounding. The argument is a finite number (no NaN or INF)
*
*Entry:
*
*Exit:
*
*Exceptions:
*
*******************************************************************************/

double _fsqrt(double x)
{
    double f,y,result;
    int n;

    f = _decomp(x,&n);

    if (n & 0x1) {
	// n is odd
	n++;
	f = _add_exp(f, -1);
    }

    //
    // approximation for sqrt in the interval [.25, 1]
    // (Computer Approximationsn, Hart & al.)
    // gives more than 7 bits of accuracy
    //

    y =  p00 + f * (p01	+ f *  p02);

    y += f / y;
    y = _add_exp(y, -1);

    y += f / y;
    y = _add_exp(y, -1);

    y += f / y;
    y = _add_exp(y, -1);

    n >>= 1;
    result = _add_exp(y,n);

    return result;
}



#endif