single-math.c

     
   1  //! @file single-math.c
   2  //! @author J. Marcel van der Veer
   3  
   4  //! @section Copyright
   5  //!
   6  //! This file is part of Algol68G - an Algol 68 compiler-interpreter.
   7  //! Copyright 2001-2025 J. Marcel van der Veer [algol68g@xs4all.nl].
   8  
   9  //! @section License
  10  //!
  11  //! This program is free software; you can redistribute it and/or modify it 
  12  //! under the terms of the GNU General Public License as published by the 
  13  //! Free Software Foundation; either version 3 of the License, or 
  14  //! (at your option) any later version.
  15  //!
  16  //! This program is distributed in the hope that it will be useful, but 
  17  //! WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
  18  //! or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
  19  //! more details. You should have received a copy of the GNU General Public 
  20  //! License along with this program. If not, see [http://www.gnu.org/licenses/].
  21  
  22  //! @section Synopsis
  23  //!
  24  //! REAL math routines supplementing libc.
  25  
  26  // References:
  27  //   M. Abramowitz and I. Stegun, Handbook of Mathematical Functions,
  28  //   Dover Publications, New York [1970]
  29  //   https://en.wikipedia.org/wiki/Abramowitz_and_Stegun
  30  
  31  #include "a68g.h"
  32  #include "a68g-genie.h"
  33  #include "a68g-prelude.h"
  34  #include "a68g-double.h"
  35  #include "a68g-numbers.h"
  36  #include "a68g-math.h"
  37  
  38  inline REAL_T a68g_max_real (REAL_T x, REAL_T y)
  39  {
  40    return (x > y ? x : y);
  41  }
  42  
  43  inline REAL_T a68g_min_real (REAL_T x, REAL_T y)
  44  {
  45    return (x < y ? x : y);
  46  }
  47  
  48  inline REAL_T a68g_sign_real (REAL_T x)
  49  {
  50    return (x == 0 ? 0 : (x > 0 ? 1 : -1));
  51  }
  52  
  53  inline REAL_T a68g_int_real (REAL_T x)
  54  {
  55    return (x >= 0 ? (INT_T) x : -(INT_T) - x);
  56  }
  57  
  58  inline INT_T a68g_round (REAL_T x)
  59  {
  60    return (INT_T) (x >= 0 ? x + 0.5 : x - 0.5);
  61  }
  62  
  63  #define IS_INTEGER(n) (n == a68g_int_real (n))
  64  
  65  inline REAL_T a68g_abs_real (REAL_T x)
  66  {
  67    return (x >= 0 ? x : -x);
  68  }
  69  
  70  REAL_T a68g_fdiv_real (REAL_T x, REAL_T y)
  71  {
  72  // This is for generating +-INF.
  73    return x / y;
  74  }
  75  
  76  REAL_T a68g_nan_real (void)
  77  {
  78    return a68g_fdiv_real (0.0, 0.0);
  79  }
  80  
  81  REAL_T a68g_posinf_real (void)
  82  {
  83    return a68g_fdiv_real (+1.0, 0.0);
  84  }
  85  
  86  REAL_T a68g_mininf_real (void)
  87  {
  88    return a68g_fdiv_real (-1.0, 0.0);
  89  }
  90  
  91  // Range check.
  92  
  93  BOOL_T a68g_finite_real (REAL_T x)
  94  {
  95    #if defined (HAVE_ISFINITE)
  96      return isfinite (x);
  97    #elif defined (HAVE_FINITE)
  98      return finite (x);
  99    #else
 100      (void) x;
 101      return A68G_TRUE;
 102    #endif
 103  }
 104  
 105  // Range check.
 106  
 107  BOOL_T a68g_isnan_real (REAL_T x)
 108  {
 109    #if defined (HAVE_ISNAN)
 110      return isnan (x) != 0;
 111    #elif defined (HAVE_IEEE_COMPARISONS)
 112      int status = (x != x);
 113      return status;
 114    #else
 115      (void) x;
 116      return A68G_FALSE;
 117    #endif
 118  }
 119  
 120  // Range check.
 121  
 122  BOOL_T a68g_isinf_real (REAL_T x)
 123  {
 124    #if defined (HAVE_ISINF)
 125      return isinf (x);
 126    #else
 127      if (!a68g_finite_real (x) && !a68g_isnan_real (x)) {
 128        return A68G_TRUE
 129      } else {
 130        return A68G_FALSE
 131      }
 132    #endif
 133  }
 134  
 135  // INT operators
 136  
 137  INT_T a68g_add_int (INT_T j, INT_T k)
 138  {
 139    if (j >= 0) {
 140      A68G_OVERFLOW (A68G_MAX_INT - j < k);
 141    } else {
 142      A68G_OVERFLOW (k < (-A68G_MAX_INT) - j);
 143    }
 144    return j + k;
 145  }
 146  
 147  INT_T a68g_sub_int (INT_T j, INT_T k)
 148  {
 149    return a68g_add_int (j, -k);
 150  }
 151  
 152  INT_T a68g_mul_int (INT_T j, INT_T k)
 153  {
 154    if (j == 0 || k == 0) {
 155      return 0;
 156    } else {
 157      INT_T u = (j > 0 ? j : -j), v = (k > 0 ? k : -k);
 158      A68G_OVERFLOW (u > A68G_MAX_INT / v);
 159      return j * k;
 160    }
 161  }
 162  
 163  INT_T a68g_over_int (INT_T j, INT_T k)
 164  {
 165    A68G_INVALID (k == 0);
 166    return j / k;
 167  }
 168  
 169  INT_T a68g_mod_int (INT_T j, INT_T k)
 170  {
 171    A68G_INVALID (k == 0);
 172    INT_T r = j % k;
 173    return (r < 0 ? (k > 0 ? r + k : r - k) : r);
 174  }
 175  
 176  // OP ** = (INT, INT) INT 
 177  
 178  INT_T a68g_m_up_n (INT_T m, INT_T n)
 179  {
 180  // Only positive n.
 181    A68G_INVALID (n < 0);
 182  // Special cases.
 183    if (m == 0 && n == 0) {
 184      return 1;
 185    } else if (m == 0 || m == 1) {
 186      return m;
 187    } else if (m == -1) {
 188      return (EVEN (n) ? 1 : -1);
 189    }
 190  // General case with overflow check.
 191    UNSIGNED_T bit = 1;
 192    INT_T M = m, P = 1;
 193    do {
 194      if (n & bit) {
 195        P = a68g_mul_int (P, M);
 196      }
 197      bit <<= 1;
 198      if (bit <= n) {
 199        M = a68g_mul_int (M, M);
 200      }
 201    } while (bit <= n);
 202    return P;
 203  }
 204  
 205  // OP ** = (REAL, INT) REAL 
 206  
 207  REAL_T a68g_x_up_n_real (REAL_T x, INT_T n)
 208  {
 209  // Only positive n.
 210    if (n < 0) {
 211      return 1 / a68g_x_up_n_real (x, -n);
 212    }
 213  // Special cases.
 214    if (x == 0 && n == 0) {
 215      return 1;
 216    } else if (x == 0 || x == 1) {
 217      return x;
 218    } else if (x == -1) {
 219      return (EVEN (n) ? 1 : -1);
 220    }
 221  // General case.
 222    UNSIGNED_T bit = 1;
 223    REAL_T M = x, P = 1;
 224    do {
 225      if (n & bit) {
 226        P *= M;
 227      }
 228      bit <<= 1;
 229      if (bit <= n) {
 230        M *= M;
 231      }
 232    } while (bit <= n);
 233    A68G_OVERFLOW (!a68g_finite_real (P));
 234    return P;
 235  }
 236  
 237  REAL_T a68g_div_int (INT_T j, INT_T k)
 238  {
 239    A68G_INVALID (k == 0);
 240    return (REAL_T) j / (REAL_T) k;
 241  }
 242  
 243  // Sqrt (x^2 + y^2) that does not needlessly overflow.
 244  
 245  REAL_T a68g_hypot_real (REAL_T x, REAL_T y)
 246  {
 247    REAL_T xabs = ABS (x), yabs = ABS (y), min, max;
 248    if (xabs < yabs) {
 249      min = xabs;
 250      max = yabs;
 251    } else {
 252      min = yabs;
 253      max = xabs;
 254    }
 255    if (min == 0) {
 256      return max;
 257    } else {
 258      REAL_T u = min / max;
 259      return max * sqrt (1 + u * u);
 260    }
 261  }
 262  
 263  //! @brief Compute Chebyshev series to requested accuracy.
 264  
 265  REAL_T a68g_chebyshev_real (REAL_T x, const REAL_T c[], REAL_T acc)
 266  {
 267  // Iteratively compute the recursive Chebyshev series.
 268  // c[1..N] are coefficients, c[0] is N, and acc is relative accuracy.
 269    acc *= A68G_REAL_EPS;
 270    if (acc < c[1]) {
 271      diagnostic (A68G_MATH_WARNING, A68G (f_entry), WARNING_MATH_ACCURACY, NULL);
 272    }
 273    INT_T N = a68g_round (c[0]);
 274    REAL_T err = 0, z = 2 * x, u = 0, v = 0, w = 0;
 275    for (int i = 1; i <= N; i++) {
 276      if (err > acc) {
 277        w = v;
 278        v = u;
 279        u = z * v - w + c[i];
 280      }
 281      err += a68g_abs_real (c[i]);
 282    }
 283    return 0.5 * (u - w);
 284  }
 285  
 286  // Compute ln (1 + x) accurately. 
 287  // Some C99 platforms implement this incorrectly.
 288  
 289  REAL_T a68g_ln1p_real (REAL_T x)
 290  {
 291  // Based on GNU GSL's gsl_sf_log_1plusx_e.
 292    A68G_INVALID (x <= -1);
 293    if (a68g_abs_real (x) < pow (A68G_REAL_EPS, 1 / 6.0)) {
 294      const REAL_T c1 = -0.5, c2 = 1 / 3.0, c3 = -1 / 4.0, c4 = 1 / 5.0, c5 = -1 / 6.0, c6 = 1 / 7.0, c7 = -1 / 8.0, c8 = 1 / 9.0, c9 = -1 / 10.0;
 295      const REAL_T t = c5 + x * (c6 + x * (c7 + x * (c8 + x * c9)));
 296      return x * (1 + x * (c1 + x * (c2 + x * (c3 + x * (c4 + x * t)))));
 297    } else if (a68g_abs_real (x) < 0.5) {
 298      REAL_T t = (8 * x + 1) / (x + 2) / 2;
 299      return x * a68g_chebyshev_real (t, c_ln1p, 0.1);
 300    } else {
 301      return ln (1 + x);
 302    }
 303  }
 304  
 305  // Compute ln (x), if possible accurately when x ~ 1.
 306  
 307  REAL_T a68g_ln_real (REAL_T x)
 308  {
 309    A68G_INVALID (x <= 0);
 310    #if (A68G_LEVEL >= 3)
 311      if (a68g_abs_real (x - 1) < 0.375) {
 312  // Double precision x-1 mitigates cancellation error.
 313        return a68g_ln1p_real (DBLEQ (x) - 1.0q);
 314      } else {
 315        return ln (x);
 316      }
 317    #else
 318      return ln (x);
 319    #endif
 320  }
 321  
 322  // PROC (REAL) REAL exp
 323  
 324  REAL_T a68g_exp_real (REAL_T x)
 325  {
 326    return exp (x);
 327  }
 328  
 329  // OP ** = (REAL, REAL) REAL
 330  
 331  REAL_T a68g_x_up_y (REAL_T x, REAL_T y)
 332  {
 333    if (y == 0.0) {
 334      return 1;
 335    } else {
 336      return a68g_exp_real (y * a68g_ln_real (x));
 337    }
 338  }
 339  
 340  // PROC (REAL) REAL csc
 341  
 342  REAL_T a68g_csc_real (REAL_T x)
 343  {
 344    REAL_T z = sin (x);
 345    A68G_OVERFLOW (z == 0);
 346    return 1 / z;
 347  }
 348  
 349  // PROC (REAL) REAL acsc
 350  
 351  REAL_T a68g_acsc_real (REAL_T x)
 352  {
 353    A68G_OVERFLOW (x == 0);
 354    return asin (1 / x);
 355  }
 356  
 357  // PROC (REAL) REAL sec
 358  
 359  REAL_T a68g_sec_real (REAL_T x)
 360  {
 361    REAL_T z = cos (x);
 362    A68G_OVERFLOW (z == 0);
 363    return 1 / z;
 364  }
 365  
 366  // PROC (REAL) REAL asec
 367  
 368  REAL_T a68g_asec_real (REAL_T x)
 369  {
 370    A68G_OVERFLOW (x == 0);
 371    return acos (1 / x);
 372  }
 373  
 374  // PROC (REAL) REAL cot
 375  
 376  REAL_T a68g_cot_real (REAL_T x)
 377  {
 378    REAL_T z = sin (x);
 379    A68G_OVERFLOW (z == 0);
 380    return cos (x) / z;
 381  }
 382  
 383  // PROC (REAL) REAL acot
 384  
 385  REAL_T a68g_acot_real (REAL_T x)
 386  {
 387    A68G_OVERFLOW (x == 0);
 388    return atan (1 / x);
 389  }
 390  
 391  // PROC atan2 (REAL, REAL) REAL
 392  
 393  REAL_T a68g_atan2_real (REAL_T x, REAL_T y)
 394  {
 395    if (x == 0) {
 396      A68G_INVALID (y == 0);
 397      return (y > 0 ? CONST_PI_2 : -CONST_PI_2);
 398    } else {
 399      REAL_T z = atan (ABS (y / x));
 400      if (x < 0) {
 401        z = CONST_PI - z;
 402      }
 403      return (y >= 0 ? z : -z);
 404    }
 405  }
 406  
 407  //! brief PROC (REAL) REAL cas
 408  
 409  REAL_T a68g_cas_real (REAL_T x)
 410  {
 411  // Hartley kernel, which Hartley named cas (cosine and sine).
 412    return cos (x) + sin (x);
 413  }
 414  
 415  //! brief PROC (REAL) REAL sindg
 416  
 417  REAL_T a68g_sindg_real (REAL_T x)
 418  {
 419    return sin (x * CONST_PI_OVER_180);
 420  }
 421  
 422  //! brief PROC (REAL) REAL cosdg
 423  
 424  REAL_T a68g_cosdg_real (REAL_T x)
 425  {
 426    return cos (x * CONST_PI_OVER_180);
 427  }
 428  
 429  //! brief PROC (REAL) REAL tandg
 430  
 431  REAL_T a68g_tandg_real (REAL_T x)
 432  {
 433    return tan (x * CONST_PI_OVER_180);
 434  }
 435  
 436  //! brief PROC (REAL) REAL asindg
 437  
 438  REAL_T a68g_asindg_real (REAL_T x)
 439  {
 440    return asin (x) * CONST_180_OVER_PI;
 441  }
 442  
 443  //! brief PROC (REAL) REAL acosdg
 444  
 445  REAL_T a68g_acosdg_real (REAL_T x)
 446  {
 447    return acos (x) * CONST_180_OVER_PI;
 448  }
 449  
 450  //! brief PROC (REAL) REAL atandg
 451  
 452  REAL_T a68g_atandg_real (REAL_T x)
 453  {
 454    return atan (x) * CONST_180_OVER_PI;
 455  }
 456  
 457  // PROC (REAL) REAL cscd
 458  
 459  REAL_T a68g_cscdg_real (REAL_T x)
 460  {
 461    REAL_T z = a68g_sindg_real (x);
 462    A68G_OVERFLOW (z == 0);
 463    return 1 / z;
 464  }
 465  
 466  // PROC (REAL) REAL acscdg
 467  
 468  REAL_T a68g_acscdg_real (REAL_T x)
 469  {
 470    A68G_OVERFLOW (x == 0);
 471    return a68g_asindg_real (1 / x);
 472  }
 473  
 474  // PROC (REAL) REAL secdg
 475  
 476  REAL_T a68g_secdg_real (REAL_T x)
 477  {
 478    REAL_T z = a68g_cosdg_real (x);
 479    A68G_OVERFLOW (z == 0);
 480    return 1 / z;
 481  }
 482  
 483  // PROC (REAL) REAL asecdg
 484  
 485  REAL_T a68g_asecdg_real (REAL_T x)
 486  {
 487    A68G_OVERFLOW (x == 0);
 488    return a68g_acosdg_real (1 / x);
 489  }
 490  
 491  // PROC (REAL) REAL cotdg
 492  
 493  REAL_T a68g_cot_realdg_real (REAL_T x)
 494  {
 495    REAL_T z = a68g_sindg_real (x);
 496    A68G_OVERFLOW (z == 0);
 497    return a68g_cosdg_real (x) / z;
 498  }
 499  
 500  // PROC (REAL) REAL acotdg
 501  
 502  REAL_T a68g_acotdg_real (REAL_T z)
 503  {
 504    A68G_OVERFLOW (z == 0);
 505    return a68g_atandg_real (1 / z);
 506  }
 507  
 508  // @brief PROC (REAL) REAL sinpi
 509  
 510  REAL_T a68g_sinpi_real (REAL_T x)
 511  {
 512    x = fmod (x, 2);
 513    if (x <= -1) {
 514      x += 2;
 515    } else if (x > 1) {
 516      x -= 2;
 517    }
 518  // x in <-1, 1].
 519    if (x == 0 || x == 1) {
 520      return 0;
 521    } else if (x == 0.5) {
 522      return 1;
 523    }
 524    if (x == -0.5) {
 525      return -1;
 526    } else {
 527      return sin (CONST_PI * x);
 528    }
 529  }
 530  
 531  // @brief PROC (REAL) REAL cospi
 532  
 533  REAL_T a68g_cospi_real (REAL_T x)
 534  {
 535    x = fmod (fabs (x), 2);
 536  // x in [0, 2>.
 537    if (x == 0.5 || x == 1.5) {
 538      return 0;
 539    } else if (x == 0) {
 540      return 1;
 541    } else if (x == 1) {
 542      return -1;
 543    } else {
 544      return cos (CONST_PI * x);
 545    }
 546  }
 547  
 548  // @brief PROC (REAL) REAL tanpi
 549  
 550  REAL_T a68g_tanpi_real (REAL_T x)
 551  {
 552    x = fmod (x, 1);
 553    if (x <= -0.5) {
 554      x += 1;
 555    } else if (x > 0.5) {
 556      x -= 1;
 557    }
 558  // x in <-1/2, 1/2].
 559    A68G_OVERFLOW (x == 0.5);
 560    if (x == -0.25) {
 561      return -1;
 562    } else if (x == 0) {
 563      return 0;
 564    } else if (x == 0.25) {
 565      return 1;
 566    } else {
 567      return a68g_sinpi_real (x) / a68g_cospi_real (x);
 568    }
 569  }
 570  
 571  // @brief PROC (REAL) REAL cotpi
 572  
 573  REAL_T a68g_cot_realpi (REAL_T x)
 574  {
 575    x = fmod (x, 1);
 576    if (x <= -0.5) {
 577      x += 1;
 578    } else if (x > 0.5) {
 579      x -= 1;
 580    }
 581  // x in <-1/2, 1/2].
 582    A68G_OVERFLOW (x == 0);
 583    if (x == -0.25) {
 584      return -1;
 585    } else if (x == 0.25) {
 586      return 1;
 587    } else if (x == 0.5) {
 588      return 0;
 589    } else {
 590      return a68g_cospi_real (x) / a68g_sinpi_real (x);
 591    }
 592  }
 593  
 594  // @brief PROC (REAL) REAL asinh
 595  
 596  REAL_T a68g_asinh_real (REAL_T x)
 597  {
 598    REAL_T a = ABS (x), s = (x < 0 ? -1.0 : 1);
 599    if (a > 1 / sqrt (A68G_REAL_EPS)) {
 600      return (s * (a68g_ln_real (a) + a68g_ln_real (2)));
 601    } else if (a > 2) {
 602      return (s * a68g_ln_real (2 * a + 1 / (a + sqrt (a * a + 1))));
 603    } else if (a > sqrt (A68G_REAL_EPS)) {
 604      REAL_T a2 = a * a;
 605      return (s * a68g_ln1p_real (a + a2 / (1 + sqrt (1 + a2))));
 606    } else {
 607      return (x);
 608    }
 609  }
 610  
 611  // @brief PROC (REAL) REAL acosh
 612  
 613  REAL_T a68g_acosh_real (REAL_T x)
 614  {
 615    if (x > 1 / sqrt (A68G_REAL_EPS)) {
 616      return (a68g_ln_real (x) + a68g_ln_real (2));
 617    } else if (x > 2) {
 618      return (a68g_ln_real (2 * x - 1 / (sqrt (x * x - 1) + x)));
 619    } else if (x > 1) {
 620      REAL_T t = x - 1;
 621      return (a68g_ln1p_real (t + sqrt (2 * t + t * t)));
 622    } else if (x == 1) {
 623      return (0);
 624    } else {
 625      A68G_INVALID (A68G_TRUE);
 626    }
 627  }
 628  
 629  // @brief PROC (REAL) REAL atanh
 630  
 631  REAL_T a68g_atanh_real (REAL_T x)
 632  {
 633    REAL_T a = ABS (x);
 634    A68G_INVALID (a >= 1);
 635    REAL_T s = (x < 0 ? -1 : 1);
 636    if (a >= 0.5) {
 637      return (s * 0.5 * a68g_ln1p_real (2 * a / (1 - a)));
 638    } else if (a > A68G_REAL_EPS) {
 639      return (s * 0.5 * a68g_ln1p_real (2 * a + 2 * a * a / (1 - a)));
 640    } else {
 641      return (x);
 642    }
 643  }
 644  
 645  //! @brief Inverse complementary error function.
 646  
 647  REAL_T a68g_inverfc_real (REAL_T y)
 648  {
 649    A68G_INVALID (y < 0 || y > 2);
 650    if (y == 0) {
 651      return A68G_REAL_MAX;
 652    } else if (y == 1) {
 653      return 0;
 654    } else if (y == 2) {
 655      return -A68G_REAL_MAX;
 656    } else {
 657  // Next is based on code that originally contained following statement:
 658  //   Copyright (c) 1996 Takuya Ooura. You may use, copy, modify this 
 659  //   code for any purpose and without fee.
 660      REAL_T s, t, u, v, x, z;
 661      z = (y <= 1 ? y : 2 - y);
 662      v = c_inverfc[0] - a68g_ln_real (z);
 663      u = sqrt (v);
 664      s = (a68g_ln_real (u) + c_inverfc[1]) / v;
 665      t = 1 / (u + c_inverfc[2]);
 666      x = u * (1 - s * (s * c_inverfc[3] + 0.5)) - ((((c_inverfc[4] * t + c_inverfc[5]) * t + c_inverfc[6]) * t + c_inverfc[7]) * t + c_inverfc[8]) * t;
 667      t = c_inverfc[9] / (x + c_inverfc[9]);
 668      u = t - 0.5;
 669      s = (((((((((c_inverfc[10] * u + c_inverfc[11]) * u - c_inverfc[12]) * u - c_inverfc[13]) * u + c_inverfc[14]) * u + c_inverfc[15]) * u - c_inverfc[16]) * u - c_inverfc[17]) * u + c_inverfc[18]) * u + c_inverfc[19]) * u + c_inverfc[20];
 670      s = ((((((((((((s * u - c_inverfc[21]) * u - c_inverfc[22]) * u + c_inverfc[23]) * u + c_inverfc[24]) * u + c_inverfc[25]) * u + c_inverfc[26]) * u + c_inverfc[27]) * u + c_inverfc[28]) * u + c_inverfc[29]) * u + c_inverfc[30]) * u + c_inverfc[31]) * u + c_inverfc[32]) * t - z * a68g_exp_real (x * x - c_inverfc[33]);
 671      x += s * (x * s + 1);
 672      return (y <= 1 ? x : -x);
 673    }
 674  }
 675  
 676  //! @brief Inverse error function.
 677  
 678  REAL_T a68g_inverf_real (REAL_T y)
 679  {
 680    return a68g_inverfc_real (1 - y);
 681  }
 682  
 683  //! @brief PROC (REAL, REAL) REAL ln beta
 684  
 685  REAL_T a68g_ln_beta_real (REAL_T a, REAL_T b)
 686  {
 687    return lgamma (a) + lgamma (b) - lgamma (a + b);
 688  }
 689  
 690  //! @brief PROC (REAL, REAL) REAL beta
 691  
 692  REAL_T a68g_beta_real (REAL_T a, REAL_T b)
 693  {
 694    return a68g_exp_real (a68g_ln_beta_real (a, b));
 695  }
 696  
 697  //! brief PROC (INT) REAL fact
 698  
 699  REAL_T a68g_fact_real (INT_T n)
 700  {
 701    A68G_INVALID (n < 0 || n > A68G_MAX_FAC);
 702    return factable[n];
 703  }
 704  
 705  //! brief PROC (INT) REAL ln fact
 706  
 707  REAL_T a68g_ln_fact_real (INT_T n)
 708  {
 709    A68G_INVALID (n < 0);
 710    if (n <= A68G_MAX_FAC) {
 711      return ln_factable[n];
 712    } else {
 713      return lgamma (n + 1);
 714    }
 715  }
 716  
 717  //! @brief PROC choose = (INT n, m) REAL
 718  
 719  REAL_T a68g_choose_real (INT_T n, INT_T m)
 720  {
 721    A68G_INVALID (n < m);
 722    return factable[n] / (factable[m] * factable[n - m]);
 723  }
 724  
 725  //! @brief PROC ln choose = (INT n, m) REAL
 726  
 727  REAL_T a68g_ln_choose_real (INT_T n, INT_T m)
 728  {
 729    A68G_INVALID (n < m);
 730    return a68g_ln_fact_real (n) - (a68g_ln_fact_real (m) + a68g_ln_fact_real (n - m));
 731  }
 732  
 733  REAL_T a68g_beta_inc_real (REAL_T s, REAL_T t, REAL_T x)
 734  {
 735  // Incomplete beta function I{x}(s, t).
 736  // Continued fraction, see dlmf.nist.gov/8.17; Lentz's algorithm.
 737    if (x < 0 || x > 1) {
 738      errno = ERANGE;
 739      return -1;
 740    } else {
 741      const INT_T lim = 16 * sizeof (REAL_T);
 742      BOOL_T cont = A68G_TRUE;
 743  // Rapid convergence when x <= (s+1)/(s+t+2) or else recursion.
 744      if (x > (s + 1) / (s + t + 2)) {
 745  // B{x}(s, t) = 1 - B{1-x}(t, s)
 746        return 1 - a68g_beta_inc_real (s, t, 1 - x);
 747      }
 748  // Lentz's algorithm for continued fraction.
 749      REAL_T W = 1, F = 1, c = 1, d = 0;
 750      INT_T N, m;
 751      for (N = 0, m = 0; cont && N < lim; N++) {
 752        REAL_T T;
 753        if (N == 0) {
 754          T = 1;
 755        } else if (N % 2 == 0) {
 756  // d{2m} := x m(t-m)/((s+2m-1)(s+2m))
 757          T = x * m * (t - m) / (s + 2 * m - 1) / (s + 2 * m);
 758        } else {
 759  // d{2m+1} := -x (s+m)(s+t+m)/((s+2m+1)(s+2m))
 760          T = -x * (s + m) * (s + t + m) / (s + 2 * m + 1) / (s + 2 * m);
 761          m++;
 762        }
 763        d = 1 / (T * d + 1);
 764        c = T / c + 1;
 765        F *= c * d;
 766        if (F == W) {
 767          cont = A68G_FALSE;
 768        } else {
 769          W = F;
 770        }
 771      }
 772  // I{x}(s,t)=x^s(1-x)^t / s / B(s,t) F
 773      REAL_T beta = a68g_exp_real (lgamma (s) + lgamma (t) - lgamma (s + t));
 774      return a68g_x_up_y (x, s) * a68g_x_up_y (1 - x, t) / s / beta * (F - 1);
 775    }
 776  }
     


© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)