www.pudn.com > gandalf.1.zip > numerics.c
/** * File: $RCSfile: numerics.c,v $ * Module: Numerical functions * Part of: Gandalf Library * * Revision: $Revision: 1.20 $ * Last edited: $Date: 2005/10/18 22:01:50 $ * Author: $Author: pm $ * Copyright: (c) 2000 Imagineer Software Limited */ /* This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include#include #include #include #include /** * \addtogroup Common * \{ */ /** * \defgroup CommonNumerics Numerical Routines * \{ */ /** * \brief Real square function. * \param x Input real value * \return The square of \a x * * Real square function. * * \sa gan_sqr_i(), gan_cbrt(). */ double gan_sqr_d ( double x ) { return gan_sqr(x); } /** * \brief Real square function (single precision). * \param x Input real value * \return The square of \a x * * Real square function * * \sa gan_sqr_d(), gan_cbrt(). */ float gan_sqr_f ( float x ) { return gan_sqr(x); } /** * \brief Integer square function. * \param x Input integer value * \return The square of \a x * * Integer square function * * \sa gan_sqr_d(), gan_cbrt(). */ int gan_sqr_i ( int x ) { return gan_sqr(x); } /** * \brief Unsigned integer square function. * \param x Input unsigned integer value * \return The square of \a x * * Unsigned integer square function. * * \sa gan_sqr_d(), gan_cbrt(). */ unsigned gan_sqr_ui ( unsigned x ) { return gan_sqr(x); } /** * \brief Long integer square function. * \param x Input long integer value * \return The square of \a x * * Long integer square function * * \sa gan_sqr_i(), gan_cbrt(). */ long gan_sqr_l ( long x ) { return gan_sqr(x); } /** * \brief Real cube root function. * \param x Input real value * \return The cube root of \a x. * * Real cube root function. */ double gan_cbrt ( double x ) { return ( (x>0.0) ? exp(log(x)/3.0) : ((x<0.0) ? -exp(log(-x)/3.0) : 0.0) ); } /** * \brief Returns a sample of a normally distributed random variable. * \param mu The mean of the normal distribution * \param sigma The standard deviation of the Gaussian distribution * \return The sample taken from the normal distribution. * * Returns a sample from a normal distribution. Two values are computed, * but only one is returned. */ double gan_normal_sample ( double mu, double sigma ) { double r, x, y; do { /* get a random point inside the unit circle */ x = 2.0*gan_random_01() - 1.0; y = 2.0*gan_random_01() - 1.0; r = x*x + y*y; } while ( r > 1.0 ); /* discarded sample value is mu + y*sqrt(-2.0*log(r)/r)*sigma */ return ( mu + x*sqrt(-2.0*log(r)/r)*sigma ); } /** * \brief Find roots of a quadratic equation with real coefficients. * \param a The coeffient in \f$ x^2 \f$ * \param b The coeffient in \f$ x \f$ * \param c The scalar coeffient * \param x The roots of the equation * \return The number of roots, or -1 on error. * * Solve quadratic equation * \f[ * a\:x^2 + b\:x + c = 0 * \f] * using method given in Numerical Recipes. */ int gan_solve_quadratic ( double a, double b, double c, Gan_Complex x[2] ) { double d; if ( a == 0.0 ) { /* linear equation */ if ( b == 0.0 ) { if ( c == 0.0 ) { /* equation is 0 == 0 */ gan_err_flush_trace(); gan_err_register ( "gan_solve_quadratic", GAN_ERROR_FAILURE, "" ); return -1; } /* equation is c == 0 for non-zero c */ return 0; } x[0].r = -c/b; x[0].i = 0.0; return 1; } if ( b == 0.0 ) { /* simple case */ if ( c <= 0.0 ) { x[0].r = sqrt(-c/a); x[1].r = -x[0].r; x[0].i = x[1].i = 0.0; } else { x[0].r = x[1].r = 0.0; x[0].i = sqrt(c/a); x[1].i = -x[0].i; } return 2; } /* compute discriminant */ d = b*b - 4.0*a*c; if ( d > 0.0 ) { double q; /* positive discriminant: two real roots */ q = (b > 0.0) ? -0.5*(b + sqrt(d)) : -0.5*(b - sqrt(d)); x[0].r = q/a; x[1].r = c/q; x[0].i = x[1].i = 0.0; } else { double qr, qi, q2; /* negative discriminant: two complex roots */ qr = -0.5*b; if ( b > 0.0 ) qi = -0.5*sqrt(-d); else qi = 0.5*sqrt(-d); x[0].r = qr/a; x[0].i = qi/a; q2 = qr*qr + qi*qi; x[1].r = c*qr/q2; x[1].i = -c*qi/q2; } return 2; } /** * \brief Find roots of a cubic equation with real coefficients. * \param a The coeffient in \f$ x^3 \f$ * \param b The coeffient in \f$ x^2 \f$ * \param c The coeffient in \f$ x \f$ * \param d The scalar coeffient * \param x The roots of the equation * \return The number of roots, or -1 on error. * * Solve cubic equation * \f[ * a\:x^3 + b\:x^2 + c\:x + d = 0 * \f] * using method given in Numerical Recipes. */ int gan_solve_cubic ( double a, double b, double c, double d, Gan_Complex x[3] ) { double Q, Rp, Q3; if ( a == 0.0 ) { Gan_Complex xq[2]; int qn, i; /* solve quadratic */ qn = gan_solve_quadratic ( b, c, d, xq ); for ( i = qn-1; i >= 0; i-- ) { x[i].r = xq[i].r; x[i].i = xq[i].i; } return qn; } if ( a != 1.0 ) { b /= a; c /= a; d /= a; } Q = (b*b - 3.0*c)/9.0; Rp = (2.0*b*b*b - 9.0*b*c + 27.0*d)/54.0; Q3 = Q*Q*Q; if ( Rp*Rp < Q3 ) { double t, Qr2m, b3 = b/3.0; /* real roots */ t = acos(Rp/sqrt(Q3)); Qr2m = -2.0*sqrt(Q); x[0].r = Qr2m*cos(t/3.0) - b3; x[1].r = Qr2m*cos((t + 2.0*M_PI)/3.0) - b3; x[2].r = Qr2m*cos((t - 2.0*M_PI)/3.0) - b3; /* set imaginary components to zero */ x[0].i = x[1].i = x[2].i = 0.0; } else { /* one real and two complex roots */ double A, B; A = (Rp > 0) ? -gan_cbrt(fabs(Rp) + sqrt(Rp*Rp - Q3)) : gan_cbrt(fabs(Rp) + sqrt(Rp*Rp - Q3)); B = (A == 0.0) ? 0.0 : Q/A; x[0].r = (A + B) - b/3.0; x[0].i = 0.0; x[1].r = -0.5*(A+B) - b/3.0; x[1].i = 0.5*M_SQRT3*(A - B); x[2].r = x[1].r; x[2].i = -x[1].i; } return 3; } #define ADDRESS(i) ((void *)CHADDR(i)) #define CHADDR(i) ((void *)(&(((char *)base)[(i)*size]))) #define SWAP(i,j) {memcpy(tmpbuf,CHADDR(i),size); memcpy(CHADDR(i),CHADDR(j),size); memcpy(CHADDR(j),tmpbuf,size);} /** * \brief Return k'th highest element of an array */ void * gan_kth_highest ( void *base, size_t nmemb, size_t size, unsigned int k, int (*compar)(const void *, const void *) ) { unsigned low = 0, high = nmemb-1, middle, x, y; void *tmpbuf; if ( k >= nmemb || nmemb == 0 || size == 0 ) { gan_err_flush_trace(); gan_err_register ( "gan_kth_highest", GAN_ERROR_ILLEGAL_ARGUMENT, "" ); return NULL; } /* allocate buffer for swapping */ tmpbuf = (void *)gan_malloc_array(char,size); if ( tmpbuf == NULL ) { gan_err_flush_trace(); gan_err_register_with_number ( "gan_kth_highest", GAN_ERROR_MALLOC_FAILED, "", size*sizeof(char) ); return NULL; } for(;;) { /* if low and high element are the same, we're done */ if ( low == high ) { assert(low == k); free(tmpbuf); return ADDRESS(low); } /* if low and high are consecutive, choose one of them */ if ( low == high-1 ) { if ( compar(ADDRESS(low), ADDRESS(high)) > 0 ) SWAP(low,high) assert(low == k || high == k); free(tmpbuf); return ADDRESS(k); } /* choose a comparison element as median of low, high and middle element of current sub-array */ middle = low + ((high - low)/2); /* move median element to position low+1, and swap around low and high elements if necessary to maintain their order */ if ( compar(ADDRESS(low), ADDRESS(high)) < 0 ) { if ( compar(ADDRESS(high), ADDRESS(middle)) < 0 ) /* high is the median of the three; swap with middle element */ SWAP(high,middle) else if ( compar(ADDRESS(low), ADDRESS(middle)) > 0 ) /* low is the median of the three */ SWAP(low,middle) } else { /* first swap low and high to put them in order */ SWAP(low,high) if ( compar(ADDRESS(high), ADDRESS(middle)) < 0 ) /* high is the median of the three; swap with middle element */ SWAP(high,middle) else if ( compar(ADDRESS(low), ADDRESS(middle)) > 0 ) /* low is the median of the three */ SWAP(low,middle) } /* move median element into position low+1 */ if ( middle != low+1 ) { SWAP(low+1,middle) middle = low+1; } /* initialise indices of lower and upper elements */ x = middle; y = high; for(;;) { do x++; while ( compar(ADDRESS(x), ADDRESS(middle)) < 0 ); do y--; while ( compar(ADDRESS(y), ADDRESS(middle)) > 0 ); if (x>y) break; /* terminate when indices cross */ SWAP(x,y) } /* place partitioning element into position */ if ( y != middle ) SWAP(middle, y) if (y == k) { free(tmpbuf); return ADDRESS(k); } if (y > k) high=y-1; if (y < k) low = (x==y+2) ? x-1 : x; assert(low <= k && high >= k); } /* shouldn't get here */ free(tmpbuf); gan_err_flush_trace(); gan_err_register ( "gan_kth_highest", GAN_ERROR_FAILURE, "" ); return NULL; } #undef CHADDR #undef ADDRESS #undef SWAP /** * \} */ /** * \} */