Main Page | Modules | Data Structures | File List | Data Fields | Globals | Related Pages

numerical.c

Go to the documentation of this file.
00001 /* ---------------------------------------------------------------------------- 00002 @COPYRIGHT : 00003 Copyright 1993,1994,1995 David MacDonald, 00004 McConnell Brain Imaging Centre, 00005 Montreal Neurological Institute, McGill University. 00006 Permission to use, copy, modify, and distribute this 00007 software and its documentation for any purpose and without 00008 fee is hereby granted, provided that the above copyright 00009 notice appear in all copies. The author and McGill University 00010 make no representations about the suitability of this 00011 software for any purpose. It is provided "as is" without 00012 express or implied warranty. 00013 ---------------------------------------------------------------------------- */ 00014 00015 #include <volume_io/internal_volume_io.h> 00016 #include <bicpl/numerical.h> 00017 #include <limits.h> 00018 00019 #ifndef lint 00020 static char rcsid[] = "$Header: /software/source//libraries/bicpl/Numerical/numerical.c,v 1.24 2000/02/06 15:30:41 stever Exp $"; 00021 #endif 00022 00023 /* ----------------------------- MNI Header ----------------------------------- 00024 @NAME : numerically_close 00025 @INPUT : n1 00026 n2 00027 threshold_ratio 00028 @OUTPUT : 00029 @RETURNS : TRUE if the numbers are within the threshold ratio 00030 @DESCRIPTION: Decides if two numbers are close to each other. 00031 @METHOD : 00032 @GLOBALS : 00033 @CALLS : 00034 @CREATED : 1993 David MacDonald 00035 @MODIFIED : 00036 ---------------------------------------------------------------------------- */ 00037 00038 #define SMALLEST 1.0e-20 00039 00040 public BOOLEAN numerically_close( 00041 Real n1, 00042 Real n2, 00043 Real threshold_ratio ) 00044 { 00045 double avg, diff, abs_n1, abs_n2; 00046 00047 diff = n1 - n2; 00048 if( diff < 0.0 ) diff = -diff; 00049 00050 abs_n1 = FABS( n1 ); 00051 abs_n2 = FABS( n2 ); 00052 00053 if( abs_n1 < SMALLEST || abs_n2 < SMALLEST ) 00054 return( abs_n1 < threshold_ratio && abs_n2 < threshold_ratio ); 00055 00056 avg = (n1 + n2) / 2.0; 00057 00058 if( avg == 0.0 ) 00059 return( diff <= (double) threshold_ratio ); 00060 00061 if( avg < 0.0 ) avg = -avg; 00062 00063 return( (diff / avg) <= (double) threshold_ratio ); 00064 } 00065 00066 /* ----------------------------- MNI Header ----------------------------------- 00067 @NAME : get_good_round_value 00068 @INPUT : value 00069 @OUTPUT : 00070 @RETURNS : a similar value 00071 @DESCRIPTION: Finds an approximation to the value that has fewer digits. 00072 @METHOD : Finds the closest power of 10 or 5 times a power of ten less than 00073 the value. 00074 @GLOBALS : 00075 @CALLS : 00076 @CREATED : 1993 David MacDonald 00077 @MODIFIED : 00078 ---------------------------------------------------------------------------- */ 00079 00080 public Real get_good_round_value( 00081 Real value ) 00082 { 00083 Real rounded, sign, power_of_ten, power_of_ten_times_5; 00084 00085 if( value < 0.0 ) 00086 { 00087 sign = -1.0; 00088 value = -value; 00089 } 00090 else 00091 sign = 1.0; 00092 00093 if( value == 0.0 ) 00094 rounded = 0.0; 00095 else 00096 { 00097 power_of_ten = pow( 10.0, (double) (int) log10( (double) value ) ); 00098 power_of_ten_times_5 = 5.0 * power_of_ten; 00099 if( power_of_ten_times_5 > value ) 00100 power_of_ten_times_5 /= 10.0; 00101 00102 rounded = MAX( power_of_ten, power_of_ten_times_5 ); 00103 } 00104 00105 return( sign * rounded ); 00106 } 00107 00108 /* ----------------------------- MNI Header ----------------------------------- 00109 @NAME : round_to_nearest_multiple 00110 @INPUT : value 00111 multiple_value 00112 @OUTPUT : 00113 @RETURNS : some multiple of multiple_value 00114 @DESCRIPTION: Returns the nearest integer multiple of multiple_value near value. 00115 @METHOD : 00116 @GLOBALS : 00117 @CALLS : 00118 @CREATED : 1993 David MacDonald 00119 @MODIFIED : 00120 ---------------------------------------------------------------------------- */ 00121 00122 public Real round_to_nearest_multiple( 00123 Real value, 00124 Real multiple_value ) 00125 { 00126 int i; 00127 Real factor, nearest; 00128 00129 multiple_value = FABS( multiple_value ); 00130 00131 factor = value / multiple_value; 00132 if( factor > (Real) INT_MAX || factor < (Real) INT_MIN ) 00133 nearest = value; 00134 else 00135 { 00136 i = ROUND( value / multiple_value ); 00137 nearest = (Real) i * multiple_value; 00138 } 00139 00140 return( nearest ); 00141 } 00142 00143 /* ----------------------------- MNI Header ----------------------------------- 00144 @NAME : solve_quadratic 00145 @INPUT : a 00146 b 00147 c 00148 @OUTPUT : solution1 00149 solution2 00150 @RETURNS : number of solutions 0, 1, or 2 00151 @DESCRIPTION: Solves a quadratic equation for its zeroes. 00152 @METHOD : 00153 @GLOBALS : 00154 @CALLS : 00155 @CREATED : 1993 David MacDonald 00156 @MODIFIED : 00157 ---------------------------------------------------------------------------- */ 00158 00159 public int solve_quadratic( 00160 Real a, 00161 Real b, 00162 Real c, 00163 Real *solution1, 00164 Real *solution2 ) 00165 { 00166 double inside_sqrt; 00167 int n_solutions; 00168 00169 if( a == 0.0 ) 00170 { 00171 if( b == 0.0 ) 00172 n_solutions = 0; 00173 else 00174 { 00175 n_solutions = 1; 00176 *solution1 = -c / b; 00177 } 00178 } 00179 else 00180 { 00181 inside_sqrt = (b * b - 4.0 * a * c); 00182 00183 if( inside_sqrt < 0.0 ) 00184 { 00185 n_solutions = 0; 00186 } 00187 else if( inside_sqrt == 0.0 ) 00188 { 00189 n_solutions = 1; 00190 *solution1 = - b / 2.0 / a; 00191 } 00192 else 00193 { 00194 n_solutions = 2; 00195 inside_sqrt = sqrt( (double) inside_sqrt ); 00196 *solution1 = (- b - inside_sqrt) / 2.0 / a; 00197 *solution2 = (- b + inside_sqrt) / 2.0 / a; 00198 } 00199 } 00200 00201 return( n_solutions ); 00202 } 00203 00204 /* 00205 * Roots3And4.c 00206 * 00207 * Utility functions to find cubic and quartic roots, 00208 * coefficients are passed like this: 00209 * 00210 * c[0] + c[1]*x + c[2]*x^2 + c[3]*x^3 + c[4]*x^4 = 0 00211 * 00212 * The functions return the number of non-complex roots and 00213 * put the values into the s array. 00214 * 00215 * Author: Jochen Schwarze (schwarze@isa.de) 00216 * 00217 * Jan 26, 1990 Version for Graphics Gems 00218 * Oct 11, 1990 Fixed sign problem for negative q's in SolveQuartic 00219 * (reported by Mark Podlipec), 00220 * Old-style function definitions, 00221 * IsZero() as a macro 00222 * Nov 23, 1990 Some systems do not declare acos() and cbrt() in 00223 * <math.h>, though the functions exist in the library. 00224 * If large coefficients are used, EQN_EPS should be 00225 * reduced considerably (e.g. to 1E-30), results will be 00226 * correct but multiple roots might be reported more 00227 * than once. 00228 */ 00229 00230 /* epsilon surrounding for near zero values */ 00231 00232 #define EQN_EPS 1e-9 00233 #define IsZero(x) ((x) > -EQN_EPS && (x) < EQN_EPS) 00234 00235 #ifdef NO_CBRT 00236 #define cbrt(x) ((x) > 0.0 ? pow((double)(x), 1.0/3.0) : \ 00237 ((x) < 0.0 ? -pow((double)-(x), 1.0/3.0) : 0.0)) 00238 #endif 00239 00240 #define COS_60 0.5 00241 #define SIN_60 0.86602540378443864676 00242 00243 00244 public int solve_cubic( 00245 Real a, 00246 Real b, 00247 Real c, 00248 Real d, 00249 Real s[ 3 ] ) 00250 { 00251 int num; 00252 double A, B, C, a3, cs, sn; 00253 double sq_A3, p, q; 00254 double cb_p, D, phi, t, sr_p, tc; 00255 00256 if( IsZero(a) ) 00257 { 00258 return( solve_quadratic( b, c, d, &s[0], &s[1] ) ); 00259 } 00260 00261 /* normal form: x^3 + Ax^2 + Bx + C = 0 */ 00262 00263 A = b / a; 00264 B = c / a; 00265 C = d / a; 00266 00267 /* substitute x = y - A/3 to eliminate quadric term: 00268 x^3 +px + q = 0 */ 00269 00270 a3 = A / 3.0; 00271 sq_A3 = a3 * a3; 00272 p = B / 3.0 - sq_A3 ; 00273 q = a3 * sq_A3 + (C - a3 * B) / 2.0; 00274 00275 /* use Cardano's formula */ 00276 00277 cb_p = p * p * p; 00278 D = q * q + cb_p; 00279 00280 if (IsZero(D)) 00281 { 00282 if (IsZero(q)) /* one triple solution */ 00283 { 00284 s[ 0 ] = -a3; 00285 num = 1; 00286 } 00287 else /* one single and one double solution */ 00288 { 00289 double u = cbrt(-q); 00290 s[ 0 ] = 2.0 * u - a3; 00291 s[ 1 ] = - u - a3; 00292 num = 2; 00293 } 00294 } 00295 else if (D < 0.0) /* Casus irreducibilis: three real solutions */ 00296 { 00297 sr_p = sqrt( -p ); 00298 phi = acos(-q / (sr_p*sr_p*sr_p) ) / 3.0; 00299 t = 2.0 * sr_p; 00300 00301 tc = t * cos( phi ); 00302 cs = - tc * COS_60 - a3; 00303 sn = t * SIN_60 * sin( phi ); 00304 00305 s[ 0 ] = tc - a3; 00306 s[ 1 ] = cs + sn; 00307 s[ 2 ] = cs - sn; 00308 num = 3; 00309 } 00310 else /* one real solution */ 00311 { 00312 double sqrt_D = sqrt(D); 00313 double u = cbrt(sqrt_D - q); 00314 double v = - cbrt(sqrt_D + q); 00315 00316 s[ 0 ] = u + v - a3; 00317 num = 1; 00318 } 00319 00320 return num; 00321 } 00322 00323 /* ----------------------------- MNI Header ----------------------------------- 00324 @NAME : get_roots_of_cubic 00325 @INPUT : n - degrees (4 for cubic, 3 for quadratic, 2 for linear) 00326 poly - coefficients of polynomial 00327 u_min 00328 u_max 00329 @OUTPUT : roots 00330 @RETURNS : number of roots 00331 @DESCRIPTION: Finds the roots of the up-to-cubic polynomial within the 00332 range [u_min,u_max] 00333 @METHOD : 00334 @GLOBALS : 00335 @CALLS : 00336 @CREATED : 1993 David MacDonald 00337 @MODIFIED : 00338 ---------------------------------------------------------------------------- */ 00339 00340 private int get_roots_of_cubic( 00341 int n, 00342 Real poly[], 00343 Real u_min, 00344 Real u_max, 00345 Real roots[] ) 00346 { 00347 int n_roots, i, j, k, n_inside; 00348 Real cubic[4], tmp; 00349 00350 for_less( i, 0, 4 ) 00351 { 00352 if( i >= n ) 00353 cubic[i] = 0.0; 00354 else 00355 cubic[i] = poly[i]; 00356 } 00357 00358 if( n < 4 || cubic[3] == 0.0 ) 00359 { 00360 n_roots = solve_quadratic( cubic[2], cubic[1], cubic[0], 00361 &roots[0], &roots[1] ); 00362 } 00363 else 00364 n_roots = solve_cubic( cubic[3], cubic[2], cubic[1], cubic[0], roots ); 00365 00366 n_inside = 0; 00367 for_less( i, 0, n_roots ) 00368 { 00369 if( u_min > u_max || (u_min <= roots[i] && roots[i] <= u_max) ) 00370 { 00371 roots[n_inside] = roots[i]; 00372 ++n_inside; 00373 } 00374 } 00375 00376 for_less( i, 0, n_inside-1 ) 00377 { 00378 k = i; 00379 for_less( j, i+1, n_inside ) 00380 { 00381 if( roots[j] < roots[k] ) 00382 k = j; 00383 } 00384 00385 if( k != i ) 00386 { 00387 tmp = roots[i]; 00388 roots[i] = roots[k]; 00389 roots[k] = tmp; 00390 } 00391 } 00392 00393 return( n_inside ); 00394 } 00395 00396 /* ----------------------------- MNI Header ----------------------------------- 00397 @NAME : evaluate_polynomial 00398 @INPUT : n - n coefficients 00399 poly - coefficients 00400 u 00401 @OUTPUT : 00402 @RETURNS : value 00403 @DESCRIPTION: Evaluates the n-th order polynomial at u. 00404 @METHOD : 00405 @GLOBALS : 00406 @CALLS : 00407 @CREATED : 1993 David MacDonald 00408 @MODIFIED : 00409 ---------------------------------------------------------------------------- */ 00410 00411 public Real evaluate_polynomial( 00412 int n, 00413 Real poly[], 00414 Real u ) 00415 { 00416 int i; 00417 Real val; 00418 00419 val = 0.0; 00420 00421 for( i = n-1; i >= 0; --i ) 00422 val = val * u + poly[i]; 00423 00424 return( val ); 00425 } 00426 00427 /* ----------------------------- MNI Header ----------------------------------- 00428 @NAME : interval_value_range 00429 @INPUT : n 00430 poly 00431 u_min 00432 u_max 00433 @OUTPUT : min_val_ptr 00434 max_val_ptr 00435 @RETURNS : 00436 @DESCRIPTION: Finds the min and max value of the given interval. Actually 00437 the range may be a subset of the returned values, but the 00438 main thing is that all values of the polynomial within u_min 00439 and u_max are guaranteed to be within the values 00440 min_val_ptr and max_val_ptr. 00441 @METHOD : 00442 @GLOBALS : 00443 @CALLS : 00444 @CREATED : 1993 David MacDonald 00445 @MODIFIED : 00446 ---------------------------------------------------------------------------- */ 00447 00448 private void interval_value_range( 00449 int n, 00450 Real poly[], 00451 Real u_min, 00452 Real u_max, 00453 Real *min_val_ptr, 00454 Real *max_val_ptr ) 00455 { 00456 int i; 00457 Real min_val, max_val, t1, t2, t3, t4; 00458 00459 min_val = 0.0; 00460 max_val = 0.0; 00461 00462 if( u_min >= 0.0 ) 00463 { 00464 for( i = n-1; i >= 0; --i ) 00465 { 00466 if( i != n-1 ) 00467 { 00468 if( min_val < 0.0 ) 00469 { 00470 min_val *= u_max; 00471 if( max_val < 0.0 ) 00472 max_val *= u_min; 00473 else 00474 max_val *= u_max; 00475 } 00476 else 00477 { 00478 min_val *= u_min; 00479 max_val *= u_max; 00480 } 00481 00482 min_val += poly[i]; 00483 max_val += poly[i]; 00484 } 00485 else 00486 { 00487 min_val = poly[i]; 00488 max_val = poly[i]; 00489 } 00490 } 00491 } 00492 else 00493 { 00494 for( i = n-1; i >= 0; --i ) 00495 { 00496 t1 = min_val * u_min; 00497 t2 = max_val * u_min; 00498 t3 = min_val * u_max; 00499 t4 = max_val * u_max; 00500 min_val = t1; 00501 max_val = t1; 00502 if( t2 < min_val ) 00503 min_val = t2; 00504 else if( t2 > max_val ) 00505 max_val = t2; 00506 if( t3 < min_val ) 00507 min_val = t3; 00508 else if( t3 > max_val ) 00509 max_val = t3; 00510 if( t4 < min_val ) 00511 min_val = t4; 00512 else if( t4 > max_val ) 00513 max_val = t4; 00514 min_val += poly[i]; 00515 max_val += poly[i]; 00516 } 00517 } 00518 00519 *min_val_ptr = min_val; 00520 *max_val_ptr = max_val; 00521 } 00522 00523 /* ----------------------------- MNI Header ----------------------------------- 00524 @NAME : interval_may_contain_zero 00525 @INPUT : n 00526 poly 00527 u_min 00528 u_max 00529 @OUTPUT : 00530 @RETURNS : TRUE if the interval may contain zero 00531 @DESCRIPTION: Checks if the polynomial may have a zero value within the 00532 given u range. 00533 @METHOD : 00534 @GLOBALS : 00535 @CALLS : 00536 @CREATED : 1993 David MacDonald 00537 @MODIFIED : 00538 ---------------------------------------------------------------------------- */ 00539 00540 private BOOLEAN interval_may_contain_zero( 00541 int n, 00542 Real poly[], 00543 Real u_min, 00544 Real u_max ) 00545 { 00546 Real min_val, max_val; 00547 00548 interval_value_range( n, poly, u_min, u_max, &min_val, &max_val ); 00549 00550 return( min_val <= 0.0 && max_val >= 0.0 ); 00551 } 00552 00553 /* ----------------------------- MNI Header ----------------------------------- 00554 @NAME : check_interval 00555 @INPUT : n 00556 poly 00557 u_min 00558 u_max 00559 accuracy 00560 @OUTPUT : n_roots 00561 roots 00562 @RETURNS : 00563 @DESCRIPTION: Recursively checks intervals on the polynomial for zeros. 00564 @METHOD : 00565 @GLOBALS : 00566 @CALLS : 00567 @CREATED : 1993 David MacDonald 00568 @MODIFIED : 00569 ---------------------------------------------------------------------------- */ 00570 00571 private void check_interval( 00572 int n, 00573 Real poly[], 00574 Real u_min, 00575 Real u_max, 00576 Real accuracy, 00577 int *n_roots, 00578 Real roots[] ) 00579 { 00580 int i, which; 00581 Real u_mid, min_diff, diff; 00582 00583 if( interval_may_contain_zero( n, poly, u_min, u_max ) ) 00584 { 00585 u_mid = (u_max + u_min) / 2.0; 00586 if( u_max - u_min > accuracy ) 00587 { 00588 check_interval( n, poly, u_min, u_mid, accuracy, n_roots, roots ); 00589 check_interval( n, poly, u_mid, u_max, accuracy, n_roots, roots ); 00590 } 00591 else 00592 { 00593 if( *n_roots == n-1 ) 00594 { 00595 which = 0; 00596 min_diff = 0.0; 00597 for_less( i, 0, *n_roots-1 ) 00598 { 00599 diff = roots[i+1] - roots[i]; 00600 if( which == 0 || diff < min_diff ) 00601 { 00602 min_diff = diff; 00603 which = i; 00604 } 00605 } 00606 00607 for_less( i, which+1, *n_roots-1 ) 00608 roots[i] = roots[i+1]; 00609 --(*n_roots); 00610 } 00611 00612 roots[*n_roots] = u_mid; 00613 ++(*n_roots); 00614 } 00615 } 00616 } 00617 00618 /* ----------------------------- MNI Header ----------------------------------- 00619 @NAME : get_roots_of_polynomial 00620 @INPUT : n 00621 poly 00622 u_min 00623 u_max 00624 accuracy 00625 @OUTPUT : roots 00626 @RETURNS : # roots 00627 @DESCRIPTION: Finds the roots of the polynomial on the given range. 00628 @METHOD : 00629 @GLOBALS : 00630 @CALLS : 00631 @CREATED : 1993 David MacDonald 00632 @MODIFIED : 00633 ---------------------------------------------------------------------------- */ 00634 00635 public int get_roots_of_polynomial( 00636 int n, 00637 Real poly[], 00638 Real u_min, 00639 Real u_max, 00640 Real accuracy, 00641 Real roots[] ) 00642 { 00643 int n_roots; 00644 00645 if( n <= 4 ) 00646 return( get_roots_of_cubic( n, poly, u_min, u_max, roots ) ); 00647 00648 n_roots = 0; 00649 00650 check_interval( n, poly, u_min, u_max, accuracy, &n_roots, roots ); 00651 00652 return( n_roots ); 00653 } 00654 00655 /* ----------------------------- MNI Header ----------------------------------- 00656 @NAME : polynomial_may_include_range 00657 @INPUT : n 00658 poly 00659 u_min 00660 u_max 00661 min_val 00662 max_val 00663 @OUTPUT : 00664 @RETURNS : TRUE if polynomial may contain range 00665 @DESCRIPTION: Checks if the polynomial may evaluate to a value in the given 00666 range in the given parameter interval. 00667 @METHOD : 00668 @GLOBALS : 00669 @CALLS : 00670 @CREATED : 1993 David MacDonald 00671 @MODIFIED : 00672 ---------------------------------------------------------------------------- */ 00673 00674 private BOOLEAN polynomial_may_include_range( 00675 int n, 00676 Real poly[], 00677 Real u_min, 00678 Real u_max, 00679 Real min_val, 00680 Real max_val ) 00681 { 00682 Real min_range, max_range; 00683 00684 interval_value_range( n, poly, u_min, u_max, &min_range, &max_range ); 00685 00686 return( max_val >= min_range && min_val <= max_range ); 00687 } 00688 00689 private BOOLEAN check_range( 00690 int n, 00691 Real poly[], 00692 Real u_min, 00693 Real u_max, 00694 Real min_val, 00695 Real max_val, 00696 Real accuracy, 00697 Real *u_min_range, 00698 Real *u_max_range ) 00699 { 00700 Real u_mid; 00701 BOOLEAN left_has_value, right_has_value; 00702 00703 if( u_max - u_min <= accuracy ) 00704 { 00705 *u_min_range = u_min; 00706 *u_max_range = u_max; 00707 return( TRUE ); 00708 } 00709 00710 u_mid = (u_min + u_max) / 2.0; 00711 00712 left_has_value = polynomial_may_include_range( n, poly, u_min, u_mid, 00713 min_val, max_val ); 00714 right_has_value = polynomial_may_include_range( n, poly, u_mid, u_max, 00715 min_val, max_val ); 00716 00717 if( left_has_value && right_has_value ) 00718 { 00719 *u_min_range = u_min; 00720 *u_max_range = u_max; 00721 return( TRUE ); 00722 } 00723 else if( left_has_value && !right_has_value ) 00724 { 00725 return( check_range( n, poly, u_min, u_mid, min_val, max_val, 00726 accuracy, u_min_range, u_max_range ) ); 00727 } 00728 else if( !left_has_value && right_has_value ) 00729 { 00730 return( check_range( n, poly, u_mid, u_max, min_val, max_val, 00731 accuracy, u_min_range, u_max_range ) ); 00732 } 00733 else 00734 return( FALSE ); 00735 } 00736 00737 public BOOLEAN get_range_of_polynomial( 00738 int n, 00739 Real poly[], 00740 Real u_min, 00741 Real u_max, 00742 Real min_val, 00743 Real max_val, 00744 Real accuracy, 00745 Real *u_min_range, 00746 Real *u_max_range ) 00747 { 00748 BOOLEAN found; 00749 00750 if( polynomial_may_include_range( n, poly, u_min, u_max, min_val, max_val )) 00751 { 00752 found = check_range( n, poly, u_min, u_max, min_val, max_val, accuracy, 00753 u_min_range, u_max_range ); 00754 } 00755 else 00756 found = FALSE; 00757 00758 return( found ); 00759 }

Generated on Wed Jul 28 09:10:57 2004 for BICPL by doxygen 1.3.7