/* * SLV: Ascend Numeric Solver * by Karl Michael Westerberg * Created: 2/6/90 * Version: $Revision: 1.16 $ * Version control file: $RCSfile: safe.c,v $ * Date last modified: $Date: 1998/06/11 15:28:35 $ * Last modified by: $Author: ballan $ * * This file is part of the SLV solver. * * Copyright (C) 1990 Karl Michael Westerberg * Copyright (C) 1993 Joseph Zaher * Copyright (C) 1994 Joseph Zaher, Benjamin Andrew Allan * Copyright (C) 1996 Benjamin Andrew Allan, Kenneth Tyner * * The SLV solver is free software; you can redistribute * it and/or modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * The SLV solver is distributed in hope that it will be * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "safe.h" #include #include #include #include #include #include "func.h" #define BIGNUM 1.0e8 //#define SAFE_DEBUG #ifdef SAFE_DEBUG # define MSG CONSOLE_DEBUG #else # define MSG(ARGS...) ((void)0) #endif /*boolean safe_ok = TRUE;*/ static int safe_print_errors = FALSE; void safe_error_to_stderr(enum safe_err *not_safe){ if (!safe_print_errors) { return; } switch(*not_safe) { case safe_problem: /* error msg should have already been printed */ case safe_ok: return; case safe_div_by_zero: ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"Division by zero"); case safe_complex_result: ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"Complex result"); break; case safe_overflow: ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"Overflow"); break; case safe_underflow: ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"Underflow"); break; case safe_range_error: ERROR_REPORTER_NOLINE(ASC_PROG_WARNING,"Function range error"); break; } } static double rec_1_p_sqr_Dn(double x,int n, enum safe_err *safe) /** *** Calculates n-th derivative of 1/(1+x^2). **/ { /* g[n](x) = -1/(1+x^2) * (n*(n-1)*g[n-2](x) + 2*n*x*g[n-1](x)) where g[n] := n-th derivative of 1/(1+x^2). */ int k; double prev[3],val; /* prev[j] := g[k-j](x), val = g[0](x) */ prev[2] = val = 1.0/(1.0 + safe_sqr_D0(x,safe)); if( n==0 ) return(val); prev[1] = -2.0*x*val*val; /* first derivative */ for( k=2 ; k<=n ; ++k ) { prev[0] = -val*(safe_mul_D0((double)(k*(k-1)),prev[2],safe) + safe_mul_D0(2*k*x,prev[1],safe)); prev[2] = prev[1]; prev[1] = prev[0]; } return(prev[1]); } static double *alloc_poly(int order) /** *** Allocates a polynominal of given order and returns it. The *** polynominal need not be freed, but this function should not be *** called again until the old polynominal is not needed anymore. **/ { static double *poly = NULL; static int poly_cap = 0; if( order + 1 > poly_cap ) { poly_cap = order+1; if( poly != NULL ) { ascfree( poly ); } poly = ASC_NEW_ARRAY(double,poly_cap ); } return(poly); } static double safe_poly(double *poly, int order, double x, double x2, enum safe_err *safe) /* x2 = x^2 */ /** *** Calculates the value of the given polynomial, where only the *** (order%1 ? odd : even) degree terms are used. **/ { double val; (void)safe; for( val=poly[order] ; (order -= 2) >= 0 ; ) { val = safe_mul_D0(val,x2,safe) + poly[order]; } return( order==-1 ? safe_mul_D0(val,x,safe) : val ); } #ifdef HAVE_ERF static double exp_msqr_Dn(double x,int n,enum safe_err *safe) /** *** Computes n-th derivative of exp(-x^2). **/ { /** *** n-th derivative of exp(-x^2) = f[n](x)*exp(-x^2), where f[n] is an *** n-th degree polynominal of definite parity satisfying f[0](x) = 1 *** & f[n+1](x) = -2x*f[n](x) + f[n]'(x). **/ double x2 = safe_sqr_D0(x,safe); int k,r; double *poly; poly = alloc_poly(n); poly[0] = exp(-x2); for( k=0 ; k= 1 ; r -= 2 ) poly[r-1] = (double)r * poly[r]; for( r=k ; r >= 0 ; r -= 2 ) poly[r+1] += -2.0*poly[r]; } return( safe_poly(poly,n,x,x2,safe) ); } #endif static double sqrt_rec_1_m_sqr_Dn(double x,int n,enum safe_err *safe) /** *** Calculates n-th derivative of (1-x^2)^-.5 **/ { /** *** n-th derivative of (1-x^2)^-.5 = f[n](x) * (1-x^2)^-(n+.5), where *** f[n] is an n-degree polynominal of definite parity satisfying *** f[0] = 1 and f[n+1](x) = f[n]'(x)*(1+x^2) + (2n+1)*x*f[n](x). **/ int k,r; double x2; double *poly; x2 = safe_sqr_D0(x,safe); poly = alloc_poly(n); (void)safe; poly[0] = safe_rec(safe_upower(safe_sqrt_D0(1.0-x2,safe),2*n+1,safe),safe); for( k=0 ; k= 1 ; r -= 2 ) poly[r-1] = (double)r * poly[r]; for( r=k ; r >= 0 ; r -= 2 ) poly[r+1] += (double)(r+2*k+1) * poly[r]; } return( safe_poly(poly,n,x,x2,safe) ); } double safe_upower(double x, unsigned n, enum safe_err *safe){ double y = 1.0; (void)safe; for( ; n-- > 0 ; y = safe_mul_D0(y,x,safe) ); return y; } double safe_factorial(unsigned n, enum safe_err *safe){ double x,y; (void)safe; for( x = (double)n , y = 1.0 ; x >= 0.5 ; y = safe_mul_D0(y,x--,safe) ); return y; } double safe_rec(double x,enum safe_err *safe){ if(x == 0.0) { double bogus = BIGNUM; if(safe_print_errors) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Divide by zero in 1/x expr: returning %g",bogus); } *safe = safe_div_by_zero; return( bogus ); } return 1.0/x; } #ifndef INV_CUBRTHUGE #define INV_CUBRTHUGE 1.7718548704178434e-103 /* smallest cubeable number in an 8 byte ieee double */ #endif #ifndef CUBRTHUGE #define CUBRTHUGE 5.6438030941223618e102 /* largest cubeable number in an 8 byte ieee double */ #endif double safe_cube(register double x,enum safe_err *safe) { if( fabs(x) > CUBRTHUGE || fabs(x) < INV_CUBRTHUGE ) { double bogus; if ( fabs(x) > CUBRTHUGE) { bogus=BIGNUM; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Overflow in x^3 expr: returning %g.",bogus); } } else { bogus=0.0; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Underflow in x^3 expr: returning %g.",bogus); } } *safe = safe_underflow; return( bogus ); } return ( x*x*x ); } double safe_Dn_rec(double x,int n,enum safe_err *safe) { return( -safe_upower(-safe_rec(x,safe),n+1,safe) * safe_factorial(n,safe) ); } #ifdef HAVE_ERF double safe_erf_inv(double x,enum safe_err *safe) { #define CONV (1e-7) double y,dy,sign; sign = (x<0.0) ? -1.0 : 1.0; x *= sign; if( x >= 1.0 ) { double bogus = sign*BIGNUM; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"inv_erf undefined at %g: returning %g",x,bogus); } *safe = safe_range_error; return(bogus); } y = 0.0; do { dy = (x-safe_erf_D0(y,safe))* safe_exp_D0(safe_mul_D0(y,y,safe),safe)/safe_ERF_COEF; y += dy; /** *** Since erf is concave and x >= erf(y0), dy should *** always be positive (?). **/ if( dy < 0.0 && safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Found negative slope of %g on erf_inv.",dy); } } while( safe_ok && dy > CONV ); return( y * sign ); #undef CONV } #endif /* HAVE_ERF */ double safe_lnm_inv(double x,enum safe_err *safe) { register double eps=FuncGetLnmEpsilon(); if( x > (DBL_MAX > 1.0e308 ? 709.196209 : log(DBL_MAX)) ) { double bogus = BIGNUM; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"Argument %g too large in lnm_inv: returning %g.",x,bogus); } *safe = safe_range_error; return(bogus); } /* since lnm(eps)= log(eps), use cheaper one. eps known >0 */ return( (x >= log(eps)) ? safe_exp_D0(x,safe) : eps*(x+1.0-log(eps)) ); } int safe_is_int(double x,enum safe_err *safe) { #define TOLERANCE (1e-4) unsigned n; (void)safe; if( x<0.0 ) { x = -x; } /** *** Theorem: Define P(n) :<==> x-TOLERANCE <= n <= x+TOLERANCE. Then *** P(n) for some integer n <==> P(floor(x+TOLERANCE)) *** <==> x-TOLERANCE <= floor(x+TOLERANCE). *** *** Proof: Since floor(x+TOLERANCE) <= x+TOLERANCE, the second *** equivalence holds. The backward direction of the first *** equivalence is a tautology. If P(n) holds for some n, then *** n <= floor(x+TOLERANCE) <= x+TOLERANCE, the second inequality *** follows since floor(z) <= z, and the first inequality follows *** since floor(z) is the largest integer with that property. **/ if( x-TOLERANCE <= (double)(n = (unsigned)(x+TOLERANCE)) ) { return( n&01 ); } else { return( -1 ); } #undef TOLERANCE } double safe_sin_D0(double x,enum safe_err *safe) { (void)safe; return(sin(x)); } double safe_sinh_D0(double x,enum safe_err *safe) { (void)safe; return(sinh(x)); } double safe_cos_D0(double x,enum safe_err *safe) { (void)safe; return(cos(x)); } double safe_cosh_D0(double x,enum safe_err *safe) { (void)safe; return(cosh(x)); } double safe_tan_D0(double x,enum safe_err *safe) { return( safe_div_D0( sin(x) , cos(x) , safe) ); } double safe_tanh_D0(double x,enum safe_err *safe) { (void)safe; return( tanh(x) ); } double safe_arcsin_D0(double x,enum safe_err *safe) { if( x < -1.0 || 1.0 < x ) { double bogus = 0.0; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"arcsin_D0: arcsin undefined at %g: Returning %g.",x,bogus); } *safe = safe_range_error; return(bogus); } return( asin(x) ); } double safe_arcsinh_D0(double x,enum safe_err *safe) { (void)safe; return( arcsinh(x) ); } double safe_arccos_D0(double x,enum safe_err *safe) { if( x < -1.0 || 1.0 < x ) { double bogus = 0.0; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"arccos_D0: arccos undefined at %g: returning %g.",x,bogus); } *safe = safe_range_error; return(bogus); } return( acos(x) ); } double safe_arccosh_D0(double x,enum safe_err *safe) { if( x < 1.0 ) { double bogus = 0.0; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"arccosh_D0: undefined at %g: returning %g.",x,bogus); } *safe = safe_range_error; return(bogus); } return( arccosh(x) ); } double safe_arctan_D0(double x,enum safe_err *safe) { (void)safe; return( atan(x) ); } double safe_arctanh_D0(double x,enum safe_err *safe) { if( x < -1.0 || 1.0 < x ) { double bogus = 0.0; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"arctanh_D0: undefined at %g: returning %g.",x,bogus); } *safe = safe_range_error; return(bogus); } return( arctanh(x) ); } #ifdef HAVE_ERF double safe_erf_D0(double x,enum safe_err *safe) { (void)safe; return( erf(x) ); } #endif /* HAVE_ERF */ double safe_exp_D0(double x,enum safe_err *safe) { if( x > (DBL_MAX > 1.0e308 ? 709.196209 : log(DBL_MAX)) ) { double bogus = BIGNUM; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"exp_D0: Argument %g too large: returning %g.",x,bogus); } *safe = safe_range_error; return(bogus); } return( exp(x) ); } double safe_ln_D0(double x,enum safe_err *safe) { if( x <= 0.0 ) { double bogus = -BIGNUM; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"ln_D0: undefined at %g: Returning %g.",x,bogus); } *safe = safe_range_error; return(bogus); } return( log(x) ); } double safe_lnm_D0(double x,enum safe_err *safe) { (void)safe; return( lnm(x) ); } double safe_log10_D0(double x,enum safe_err *safe) { return( safe_LOG10_COEF * safe_ln_D0(x,safe) ); } double safe_sqr_D0(double x,enum safe_err *safe) { (void)safe; return( sqr(x) ); } double safe_sqrt_D0(double x,enum safe_err *safe) { if( x < 0.0 ) { double bogus; bogus = -sqrt(-x); if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"sqrt_D0: undefined at %g: returning %g.",x,bogus); } *safe = safe_complex_result; return(bogus); } return(sqrt(x)); } double safe_cube_D0(double x,enum safe_err *safe) { (void)safe; return( cube(x) ); } double safe_cbrt_D0(double x,enum safe_err *safe) { (void)safe; return( cbrt(x) ); } double safe_fabs_D0(double x,enum safe_err *safe) { (void)safe; return( fabs(x) ); } double safe_hold_D0(double x,enum safe_err *safe) { (void)safe; return x; } double safe_sin_D1(double x,enum safe_err *safe) { (void)safe; return( cos(x) ); } double safe_sinh_D1(double x,enum safe_err *safe) { (void)safe; return( cosh(x) ); } double safe_cos_D1(double x,enum safe_err *safe) { (void)safe; return( sin(x) ); } double safe_cosh_D1(double x,enum safe_err *safe) { (void)safe; return( sinh(x) ); } double safe_tan_D1(double x,enum safe_err *safe) { x = cos(x); return( safe_rec(safe_sqr_D0(x,safe),safe) ); } double safe_tanh_D1(double x,enum safe_err *safe) { (void)safe; if (x < -200 || x > 200) { return 0.0; } return( dtanh(x) ); } double safe_arcsin_D1(double x,enum safe_err *safe) { return( safe_rec(safe_sqrt_D0(1.0 - safe_sqr_D0(x,safe),safe),safe) ); } double safe_arcsinh_D1(double x,enum safe_err *safe) { (void)safe; return( darcsinh(x) ); } double safe_arccos_D1(double x,enum safe_err *safe) { return( -safe_arcsin_D1(x,safe) ); } double safe_arccosh_D1(double x,enum safe_err *safe) { return( safe_rec(safe_sqrt_D0(safe_sqr_D0(x,safe) - 1.0,safe),safe) ); } double safe_arctan_D1(double x,enum safe_err *safe) { (void)safe; return( datan(x) ); } double safe_arctanh_D1(double x,enum safe_err *safe) { return( safe_rec(1.0 - safe_sqr_D0(x,safe),safe) ); } #ifdef HAVE_ERF double safe_erf_D1(double x,enum safe_err *safe) { return( safe_ERF_COEF * safe_exp_D0(-safe_sqr_D0(x,safe),safe) ); } #endif /* HAVE_ERF */ double safe_exp_D1(double x,enum safe_err *safe) { return( safe_exp_D0(x,safe) ); } double safe_ln_D1(double x,enum safe_err *safe) { return( safe_rec(x,safe) ); } double safe_lnm_D1(double x,enum safe_err *safe) { (void)safe; return( dlnm(x) ); } double safe_log10_D1(double x,enum safe_err *safe) { return( safe_LOG10_COEF * safe_ln_D1(x,safe) ); } double safe_sqr_D1(double x,enum safe_err *safe) { (void)safe; return( dsqr(x) ); } double safe_sqrt_D1(double x,enum safe_err *safe) { return( 0.5 * safe_rec(safe_sqrt_D0(x,safe),safe) ); } double safe_cube_D1(double x,enum safe_err *safe) { return( 3 * safe_sqr_D0(x,safe) ); } double safe_cbrt_D1(double x,enum safe_err *safe) { return( 0.3333333333333333* safe_rec(safe_sqr_D0(safe_cbrt_D0(x,safe),safe),safe) ); } double safe_fabs_D1(double x,enum safe_err *safe) { (void)safe; (void)x; /* stop gcc whine about unused parameter */ return( 1.0 ); } double safe_hold_D1(double x,enum safe_err *safe) { (void)x; /* stop gcc whine about unused parameter */ (void)safe; return( 0.0 ); } double safe_sin_D2(double x,enum safe_err *safe) { (void)safe; return( dcos(x) ); } double safe_sinh_D2(double x,enum safe_err *safe) { (void)safe; return( sinh(x) ); } double safe_cos_D2(double x,enum safe_err *safe) { (void)safe; return( dcos2(x) ); } double safe_cosh_D2(double x,enum safe_err *safe) { (void)safe; return( cosh(x) ); } double safe_tan_D2(double x,enum safe_err *safe) { if( fabs(cos(x)) == 0.0 ) { double bogus = BIGNUM; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"tan_D2: Infinite at %g: returning %g.",x,bogus); } *safe = safe_range_error; return(bogus); } return dtan2(x); } double safe_tanh_D2(double x,enum safe_err *safe) { (void)safe; return( dtanh2(x) ); } double safe_arcsin_D2(double x,enum safe_err *safe) { register double t; t = safe_rec(1.0 - safe_sqr_D0(x,safe),safe); return( safe_mul_D0( safe_mul_D0(x,t,safe) , safe_mul_D0(t,t,safe) ,safe) ); } double safe_arcsinh_D2(double x,enum safe_err *safe) { (void)safe; return( darcsinh2(x) ); } double safe_arccos_D2(double x,enum safe_err *safe) { return( -safe_arcsin_D2(x,safe) ); } double safe_arccosh_D2(double x,enum safe_err *safe) { if( fabs(x) <= 1.0 ) { double bogus = -BIGNUM; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"arccosh_D2: Undefined at %g: returning %g",x,bogus); } *safe = safe_range_error; return(bogus); } return darccosh2(x); } double safe_arctan_D2(double x,enum safe_err *safe) { (void)safe; return( datan2(x) ); } double safe_arctanh_D2(double x,enum safe_err *safe) { if( fabs(x) == 1.0 ) { double bogus = BIGNUM; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"arctanh_D2: undefined at %g: returning %g.",x,bogus); } *safe = safe_range_error; return(bogus); } return( darctanh2(x) ); } #ifdef HAVE_ERF double safe_erf_D2(double x,enum safe_err *safe) { return( -ldexp(safe_ERF_COEF * safe_mul_D0(x,safe_exp_D0(-safe_sqr_D0(x,safe),safe),safe),1) ); } #endif /* HAVE_ERF */ double safe_exp_D2(double x,enum safe_err *safe) { return( safe_exp_D0(x,safe) ); } double safe_ln_D2(double x,enum safe_err *safe) { return( -safe_rec(safe_sqr_D0(x,safe),safe) ); } double safe_lnm_D2(double x,enum safe_err *safe) { (void)safe; return( dlnm2(x) ); } double safe_log10_D2(double x,enum safe_err *safe) { return( safe_LOG10_COEF * safe_ln_D2(x,safe) ); } double safe_sqr_D2(double x,enum safe_err *safe) { (void)safe; return( dsqr2(x) ); } double safe_sqrt_D2(double x,enum safe_err *safe) { return(-ldexp(safe_rec(safe_mul_D0(x,safe_sqrt_D0( x,safe),safe),safe),-2)); } double safe_cube_D2(double x,enum safe_err *safe) { (void)safe; return( safe_mul_D0(6,x,safe) ); } double safe_cbrt_D2(double x,enum safe_err *safe) { if( fabs(x) == 0.0 ) { double bogus = BIGNUM; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"cbrt_D2: undefined at %g: returning %g.",x,bogus); } *safe = safe_range_error; return(bogus); } return dcbrt2(x); } double safe_fabs_D2(double x,enum safe_err *safe) { (void)x; /* stop gcc whine about unused parameter */ (void)safe; return( 0.0 ); } double safe_arcsin_Dn(double x,int n,enum safe_err *safe) { return( n==0 ? safe_arcsin_D0(x,safe) : sqrt_rec_1_m_sqr_Dn(x,n-1,safe) ); } double safe_arccos_Dn(double x,int n,enum safe_err *safe) { return( n==0 ? safe_arccos_D0(x,safe) : -sqrt_rec_1_m_sqr_Dn(x,n-1,safe) ); } double safe_arctan_Dn(double x,int n,enum safe_err *safe) { return( n==0 ? safe_arctan_D0(x,safe) : rec_1_p_sqr_Dn(x,n-1,safe) ); } double safe_cos_Dn(double x,int n,enum safe_err *safe) { switch( n%4 ) { case 0: return( cos(x) ); case 1: return( -sin(x) ); case 2: return( -cos(x) ); case 3: return( sin(x) ); } if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"cos_D: Unreachable point reached?!?"); } *safe = safe_range_error; return 0.0; } double safe_sin_Dn(double x,int n,enum safe_err *safe) { return( safe_cos_Dn(x,n+3,safe) ); } #ifdef HAVE_ERF double safe_erf_Dn(double x,int n,enum safe_err *safe) { return( (n==0) ? (safe_erf_D0(x,safe)) : (safe_ERF_COEF*exp_msqr_Dn(x,n-1,safe)) ); } #endif /* HAVE_ERF */ double safe_exp_Dn(double x,int n,enum safe_err *safe) { (void)n; /* stop gcc whine about unused parameter */ return( safe_exp_D0(x,safe) ); } double safe_ln_Dn(double x,int n,enum safe_err *safe) { return( n==0 ? safe_ln_D0(x,safe) : safe_Dn_rec(x,n-1,safe) ); } double safe_log10_Dn(double x,int n,enum safe_err *safe) { return( safe_LOG10_COEF * safe_ln_Dn(x,n,safe) ); } double safe_sqr_Dn(double x,int n,enum safe_err *safe) { switch(n) { case 0: return( safe_sqr_D0(x,safe) ); case 1: return( 2.0*x ); case 2: return(2.0); default: return(0.0); } } double safe_sqrt_Dn(double x,int n,enum safe_err *safe) { double a,b; for( b = 1.0 , a = 1.5-n ; a < 1.0 ; ++a ) b *= a; return( b * safe_sqrt_D0(x,safe) * safe_rec(safe_upower(x,n,safe),safe) ); } static double tan_k_Dn(double u,int k,int n,enum safe_err *safe) /* k >= 0 */ /** *** nth derivative of tan^k evaluated at x, where u=tan(x) **/ { /* (tan^k)' = k * (tan^(k+1) + tan^(k-1)) */ if( n == 0 ) return( safe_upower(u,k,safe) ); else if( k == 0 ) return( 0.0 ); else return( k * (tan_k_Dn(u,k+1,n-1,safe) + tan_k_Dn(u,k-1,n-1,safe)) ); } double safe_tan_Dn(double x,int n,enum safe_err *safe) { return( tan_k_Dn(safe_tan_D0(x,safe),1,n,safe) ); } static double cheap_pow(double x,double y,enum safe_err *safe) /** *** Computes x^y, x>0 **/ { return( safe_exp_D0(safe_mul_D0(y,safe_ln_D0(x,safe),safe),safe) ); } double safe_pow_D0(double x,double y,enum safe_err *safe) { if( x > 0.0 ) return( cheap_pow(x,y,safe) ); else if( x == 0.0 ) { if( y <= 0.0 ) { double bogus; bogus = y < 0.0 ? BIGNUM : 0.0; if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"pow_D0: %g raised to power %g undefined: returning %g",x,y,bogus); } *safe = safe_range_error; return(bogus); } return( 0.0 ); } /* x < 0.0 */ switch( safe_is_int(y,safe) ) { case 0: return( cheap_pow(-x,y,safe) ); case 1: return( -cheap_pow(-x,y,safe) ); default: { double bogus = cheap_pow(-x,y,safe); if( safe_print_errors ) { ERROR_REPORTER_NOLINE(ASC_USER_ERROR,"pow_D0: %g raised to power %g undefined: returning %g",x,y,bogus); } *safe = safe_range_error; return(bogus); } } } double safe_div_D1(double x,double y,int wrt,enum safe_err *safe) { y = safe_rec(y,safe); switch( wrt ) { case 0: return(y); case 1: return( -safe_mul_D0(safe_sqr_D0(y,safe),x,safe) ); default: ASC_PANIC("'wrt' out of range!"); } } double safe_ipow_D1( double x,double y, int wrt,enum safe_err *safe) { (void)safe; switch( wrt ) { case 0: return( safe_mul_D0(y,safe_ipow_D0(x,y-1,safe),safe) ); case 1: return(0.0); /* d(x^i)/di where i is integer is 0.0 since we * assume integers are constant */ default: ASC_PANIC("'wrt' out of range!"); } } double safe_pow_D1(double x,double y,int wrt,enum safe_err *safe) { switch( wrt ) { case 0: return( safe_mul_D0(y,safe_pow_D0(x,y-1.0,safe),safe) ); case 1: return( safe_mul_D0( safe_ln_D0(x,safe) , safe_pow_D0(x,y,safe) ,safe) ); default: ASC_PANIC("'wrt' out of range!"); } } double safe_div_D2(double x,double y,int wrt1,int wrt2,enum safe_err *safe) { (void)x; /* stop gcc whine about unused parameter */ switch( wrt1+wrt2 ) { case 0: return(0.0); case 1: return( -safe_rec(safe_sqr_D0(y,safe),safe) ); case 2: return( safe_rec(0.5*safe_mul_D0(y,safe_sqr_D0(y,safe),safe),safe) ); default: ASC_PANIC("'wrt' out of range!"); } } double safe_ipow_D2( double x,double y, int wrt1,int wrt2,enum safe_err *safe) { double lnx; int yint; yint = ascnint(y); switch( wrt1+wrt2 ) { case 0: return yint*(yint-1)*safe_ipow_D0(x,y-2,safe); case 1: lnx = safe_ln_D0(x,safe); return( safe_mul_D0(safe_rec(x,safe)+ safe_sqr_D0(lnx,safe),safe_ipow_D0(x,y,safe),safe)); case 2: lnx = safe_ln_D0(x,safe); return( safe_mul_D0(safe_sqr_D0(lnx,safe) , safe_ipow_D0(x,y,safe),safe) ); default: ASC_PANIC("'wrt' out of range!"); } } double safe_pow_D2(double x,double y,int wrt1,int wrt2,enum safe_err *safe) { double lnx; switch( wrt1+wrt2 ) { case 0: return( safe_mul_D0( safe_mul_D0(y,y-1.0,safe), safe_pow_D0(x,y-2.0,safe),safe ) ); case 1: lnx = safe_ln_D0(x,safe); return( safe_mul_D0(safe_rec(x,safe)+safe_sqr_D0(lnx,safe), safe_pow_D0(x,y,safe),safe) ); case 2: lnx = safe_ln_D0(x,safe); return( safe_mul_D0(safe_sqr_D0(lnx,safe) , safe_pow_D0(x,y,safe),safe) ); default: ASC_PANIC("'wrt' out of range!"); } } double safe_add_Dn(double x,double y,int nwrt0,int nwrt1,enum safe_err *safe) { (void)safe; switch( nwrt0+nwrt1 ) { case 0: return( x+y ); case 1: return( 1.0 ); default: return( 0.0 ); } } double safe_sub_Dn(double x,double y,int nwrt0,int nwrt1,enum safe_err *safe) { (void)safe; switch( nwrt0+nwrt1 ) { case 0: return( x-y ); case 1: return( nwrt1==0 ? 1.0 : -1.0 ); default: return( 0.0 ); } } double safe_mul_Dn(double x,double y,int nwrt0,int nwrt1,enum safe_err *safe) { (void)safe; switch( nwrt0 ) { case 0: break; case 1: x = 1.0; break; default: return(0.0); } switch( nwrt1 ) { case 0: return( safe_mul_D0(x,y,safe) ); case 1: return(x); default: return(0.0); } } double safe_div_Dn(double x,double y,int nwrt0,int nwrt1,enum safe_err *safe) { switch( nwrt0 ) { case 1: x = 1.0; case 0: /* FALL THROUGH */ return( safe_mul_D0(x,safe_Dn_rec(y,nwrt1,safe),safe) ); default: return(0.0); } } double safe_pow_Dn(double x,double y,int nwrt0,int nwrt1,enum safe_err *safe) /** *** The n-th derivative wrt x of (lnx)^m * x^y (the m-th derivative wrt y *** of x^y) = x^(y-n) * P[n](lnx), where P[0](z) = z^m and *** P[n+1](z) = (y-n)*P[n](z) + P[n]'(z). (n==nwrt0,m==nwrt1) **/ { double *poly; double lnx,lnx2; /* To be calculated later if necessary */ int k,r; poly = alloc_poly(nwrt1); /* mem_zero_byte_cast(poly,0,nwrt1*sizeof(double));*/ ascbzero((void *)poly,nwrt1*sizeof(double)); poly[nwrt1] = safe_pow_D0(x,y-(double)nwrt0,safe); for( k=0 ; k