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) == FP_NAN;
 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 || m == 1) {
 184      return m;
 185    } else if (m == -1) {
 186      return (EVEN (n) ? 1 : -1);
 187    }
 188  // General case with overflow check.
 189    UNSIGNED_T bit = 1;
 190    INT_T M = m, P = 1;
 191    do {
 192      if (n & bit) {
 193        P = a68g_mul_int (P, M);
 194      }
 195      bit <<= 1;
 196      if (bit <= n) {
 197        M = a68g_mul_int (M, M);
 198      }
 199    } while (bit <= n);
 200    return P;
 201  }
 202  
 203  // OP ** = (REAL, INT) REAL 
 204  
 205  REAL_T a68g_x_up_n_real (REAL_T x, INT_T n)
 206  {
 207  // Only positive n.
 208    if (n < 0) {
 209      return 1 / a68g_x_up_n_real (x, -n);
 210    }
 211  // Special cases.
 212    if (x == 0 || x == 1) {
 213      return x;
 214    } else if (x == -1) {
 215      return (EVEN (n) ? 1 : -1);
 216    }
 217  // General case.
 218    UNSIGNED_T bit = 1;
 219    REAL_T M = x, P = 1;
 220    do {
 221      if (n & bit) {
 222        P *= M;
 223      }
 224      bit <<= 1;
 225      if (bit <= n) {
 226        M *= M;
 227      }
 228    } while (bit <= n);
 229    A68G_OVERFLOW (!a68g_finite_real (P));
 230    return P;
 231  }
 232  
 233  REAL_T a68g_div_int (INT_T j, INT_T k)
 234  {
 235    A68G_INVALID (k == 0);
 236    return (REAL_T) j / (REAL_T) k;
 237  }
 238  
 239  // Sqrt (x^2 + y^2) that does not needlessly overflow.
 240  
 241  REAL_T a68g_hypot_real (REAL_T x, REAL_T y)
 242  {
 243    REAL_T xabs = ABS (x), yabs = ABS (y), min, max;
 244    if (xabs < yabs) {
 245      min = xabs;
 246      max = yabs;
 247    } else {
 248      min = yabs;
 249      max = xabs;
 250    }
 251    if (min == 0) {
 252      return max;
 253    } else {
 254      REAL_T u = min / max;
 255      return max * sqrt (1 + u * u);
 256    }
 257  }
 258  
 259  //! @brief Compute Chebyshev series to requested accuracy.
 260  
 261  REAL_T a68g_chebyshev_real (REAL_T x, const REAL_T c[], REAL_T acc)
 262  {
 263  // Iteratively compute the recursive Chebyshev series.
 264  // c[1..N] are coefficients, c[0] is N, and acc is relative accuracy.
 265    acc *= A68G_REAL_EPS;
 266    if (acc < c[1]) {
 267      diagnostic (A68G_MATH_WARNING, A68G (f_entry), WARNING_MATH_ACCURACY, NULL);
 268    }
 269    INT_T N = a68g_round (c[0]);
 270    REAL_T err = 0, z = 2 * x, u = 0, v = 0, w = 0;
 271    for (int i = 1; i <= N; i++) {
 272      if (err > acc) {
 273        w = v;
 274        v = u;
 275        u = z * v - w + c[i];
 276      }
 277      err += a68g_abs_real (c[i]);
 278    }
 279    return 0.5 * (u - w);
 280  }
 281  
 282  // Compute ln (1 + x) accurately. 
 283  // Some C99 platforms implement this incorrectly.
 284  
 285  REAL_T a68g_ln1p_real (REAL_T x)
 286  {
 287  // Based on GNU GSL's gsl_sf_log_1plusx_e.
 288    A68G_INVALID (x <= -1);
 289    if (a68g_abs_real (x) < pow (A68G_REAL_EPS, 1 / 6.0)) {
 290      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;
 291      const REAL_T t = c5 + x * (c6 + x * (c7 + x * (c8 + x * c9)));
 292      return x * (1 + x * (c1 + x * (c2 + x * (c3 + x * (c4 + x * t)))));
 293    } else if (a68g_abs_real (x) < 0.5) {
 294      REAL_T t = (8 * x + 1) / (x + 2) / 2;
 295      return x * a68g_chebyshev_real (t, c_ln1p, 0.1);
 296    } else {
 297      return ln (1 + x);
 298    }
 299  }
 300  
 301  // Compute ln (x), if possible accurately when x ~ 1.
 302  
 303  REAL_T a68g_ln_real (REAL_T x)
 304  {
 305    A68G_INVALID (x <= 0);
 306  #if (A68G_LEVEL >= 3)
 307    if (a68g_abs_real (x - 1) < 0.375) {
 308  // Double precision x-1 mitigates cancellation error.
 309      return a68g_ln1p_real (DBLEQ (x) - 1.0q);
 310    } else {
 311      return ln (x);
 312    }
 313  #else
 314    return ln (x);
 315  #endif
 316  }
 317  
 318  // PROC (REAL) REAL exp
 319  
 320  REAL_T a68g_exp_real (REAL_T x)
 321  {
 322    return exp (x);
 323  }
 324  
 325  // OP ** = (REAL, REAL) REAL
 326  
 327  REAL_T a68g_x_up_y (REAL_T x, REAL_T y)
 328  {
 329    return a68g_exp_real (y * a68g_ln_real (x));
 330  }
 331  
 332  // PROC (REAL) REAL csc
 333  
 334  REAL_T a68g_csc_real (REAL_T x)
 335  {
 336    REAL_T z = sin (x);
 337    A68G_OVERFLOW (z == 0);
 338    return 1 / z;
 339  }
 340  
 341  // PROC (REAL) REAL acsc
 342  
 343  REAL_T a68g_acsc_real (REAL_T x)
 344  {
 345    A68G_OVERFLOW (x == 0);
 346    return asin (1 / x);
 347  }
 348  
 349  // PROC (REAL) REAL sec
 350  
 351  REAL_T a68g_sec_real (REAL_T x)
 352  {
 353    REAL_T z = cos (x);
 354    A68G_OVERFLOW (z == 0);
 355    return 1 / z;
 356  }
 357  
 358  // PROC (REAL) REAL asec
 359  
 360  REAL_T a68g_asec_real (REAL_T x)
 361  {
 362    A68G_OVERFLOW (x == 0);
 363    return acos (1 / x);
 364  }
 365  
 366  // PROC (REAL) REAL cot
 367  
 368  REAL_T a68g_cot_real (REAL_T x)
 369  {
 370    REAL_T z = sin (x);
 371    A68G_OVERFLOW (z == 0);
 372    return cos (x) / z;
 373  }
 374  
 375  // PROC (REAL) REAL acot
 376  
 377  REAL_T a68g_acot_real (REAL_T x)
 378  {
 379    A68G_OVERFLOW (x == 0);
 380    return atan (1 / x);
 381  }
 382  
 383  // PROC atan2 (REAL, REAL) REAL
 384  
 385  REAL_T a68g_atan2_real (REAL_T x, REAL_T y)
 386  {
 387    if (x == 0) {
 388      A68G_INVALID (y == 0);
 389      return (y > 0 ? CONST_PI_2 : -CONST_PI_2);
 390    } else {
 391      REAL_T z = atan (ABS (y / x));
 392      if (x < 0) {
 393        z = CONST_PI - z;
 394      }
 395      return (y >= 0 ? z : -z);
 396    }
 397  }
 398  
 399  //! brief PROC (REAL) REAL cas
 400  
 401  REAL_T a68g_cas_real (REAL_T x)
 402  {
 403  // Hartley kernel, which Hartley named cas (cosine and sine).
 404    return cos (x) + sin (x);
 405  }
 406  
 407  //! brief PROC (REAL) REAL sindg
 408  
 409  REAL_T a68g_sindg_real (REAL_T x)
 410  {
 411    return sin (x * CONST_PI_OVER_180);
 412  }
 413  
 414  //! brief PROC (REAL) REAL cosdg
 415  
 416  REAL_T a68g_cosdg_real (REAL_T x)
 417  {
 418    return cos (x * CONST_PI_OVER_180);
 419  }
 420  
 421  //! brief PROC (REAL) REAL tandg
 422  
 423  REAL_T a68g_tandg_real (REAL_T x)
 424  {
 425    return tan (x * CONST_PI_OVER_180);
 426  }
 427  
 428  //! brief PROC (REAL) REAL asindg
 429  
 430  REAL_T a68g_asindg_real (REAL_T x)
 431  {
 432    return asin (x) * CONST_180_OVER_PI;
 433  }
 434  
 435  //! brief PROC (REAL) REAL acosdg
 436  
 437  REAL_T a68g_acosdg_real (REAL_T x)
 438  {
 439    return acos (x) * CONST_180_OVER_PI;
 440  }
 441  
 442  //! brief PROC (REAL) REAL atandg
 443  
 444  REAL_T a68g_atandg_real (REAL_T x)
 445  {
 446    return atan (x) * CONST_180_OVER_PI;
 447  }
 448  
 449  // PROC (REAL) REAL cscd
 450  
 451  REAL_T a68g_cscdg_real (REAL_T x)
 452  {
 453    REAL_T z = a68g_sindg_real (x);
 454    A68G_OVERFLOW (z == 0);
 455    return 1 / z;
 456  }
 457  
 458  // PROC (REAL) REAL acscdg
 459  
 460  REAL_T a68g_acscdg_real (REAL_T x)
 461  {
 462    A68G_OVERFLOW (x == 0);
 463    return a68g_asindg_real (1 / x);
 464  }
 465  
 466  // PROC (REAL) REAL secdg
 467  
 468  REAL_T a68g_secdg_real (REAL_T x)
 469  {
 470    REAL_T z = a68g_cosdg_real (x);
 471    A68G_OVERFLOW (z == 0);
 472    return 1 / z;
 473  }
 474  
 475  // PROC (REAL) REAL asecdg
 476  
 477  REAL_T a68g_asecdg_real (REAL_T x)
 478  {
 479    A68G_OVERFLOW (x == 0);
 480    return a68g_acosdg_real (1 / x);
 481  }
 482  
 483  // PROC (REAL) REAL cotdg
 484  
 485  REAL_T a68g_cot_realdg_real (REAL_T x)
 486  {
 487    REAL_T z = a68g_sindg_real (x);
 488    A68G_OVERFLOW (z == 0);
 489    return a68g_cosdg_real (x) / z;
 490  }
 491  
 492  // PROC (REAL) REAL acotdg
 493  
 494  REAL_T a68g_acotdg_real (REAL_T z)
 495  {
 496    A68G_OVERFLOW (z == 0);
 497    return a68g_atandg_real (1 / z);
 498  }
 499  
 500  // @brief PROC (REAL) REAL sinpi
 501  
 502  REAL_T a68g_sinpi_real (REAL_T x)
 503  {
 504    x = fmod (x, 2);
 505    if (x <= -1) {
 506      x += 2;
 507    } else if (x > 1) {
 508      x -= 2;
 509    }
 510  // x in <-1, 1].
 511    if (x == 0 || x == 1) {
 512      return 0;
 513    } else if (x == 0.5) {
 514      return 1;
 515    }
 516    if (x == -0.5) {
 517      return -1;
 518    } else {
 519      return sin (CONST_PI * x);
 520    }
 521  }
 522  
 523  // @brief PROC (REAL) REAL cospi
 524  
 525  REAL_T a68g_cospi_real (REAL_T x)
 526  {
 527    x = fmod (fabs (x), 2);
 528  // x in [0, 2>.
 529    if (x == 0.5 || x == 1.5) {
 530      return 0;
 531    } else if (x == 0) {
 532      return 1;
 533    } else if (x == 1) {
 534      return -1;
 535    } else {
 536      return cos (CONST_PI * x);
 537    }
 538  }
 539  
 540  // @brief PROC (REAL) REAL tanpi
 541  
 542  REAL_T a68g_tanpi_real (REAL_T x)
 543  {
 544    x = fmod (x, 1);
 545    if (x <= -0.5) {
 546      x += 1;
 547    } else if (x > 0.5) {
 548      x -= 1;
 549    }
 550  // x in <-1/2, 1/2].
 551    A68G_OVERFLOW (x == 0.5);
 552    if (x == -0.25) {
 553      return -1;
 554    } else if (x == 0) {
 555      return 0;
 556    } else if (x == 0.25) {
 557      return 1;
 558    } else {
 559      return a68g_sinpi_real (x) / a68g_cospi_real (x);
 560    }
 561  }
 562  
 563  // @brief PROC (REAL) REAL cotpi
 564  
 565  REAL_T a68g_cot_realpi (REAL_T x)
 566  {
 567    x = fmod (x, 1);
 568    if (x <= -0.5) {
 569      x += 1;
 570    } else if (x > 0.5) {
 571      x -= 1;
 572    }
 573  // x in <-1/2, 1/2].
 574    A68G_OVERFLOW (x == 0);
 575    if (x == -0.25) {
 576      return -1;
 577    } else if (x == 0.25) {
 578      return 1;
 579    } else if (x == 0.5) {
 580      return 0;
 581    } else {
 582      return a68g_cospi_real (x) / a68g_sinpi_real (x);
 583    }
 584  }
 585  
 586  // @brief PROC (REAL) REAL asinh
 587  
 588  REAL_T a68g_asinh_real (REAL_T x)
 589  {
 590    REAL_T a = ABS (x), s = (x < 0 ? -1.0 : 1);
 591    if (a > 1 / sqrt (A68G_REAL_EPS)) {
 592      return (s * (a68g_ln_real (a) + a68g_ln_real (2)));
 593    } else if (a > 2) {
 594      return (s * a68g_ln_real (2 * a + 1 / (a + sqrt (a * a + 1))));
 595    } else if (a > sqrt (A68G_REAL_EPS)) {
 596      REAL_T a2 = a * a;
 597      return (s * a68g_ln1p_real (a + a2 / (1 + sqrt (1 + a2))));
 598    } else {
 599      return (x);
 600    }
 601  }
 602  
 603  // @brief PROC (REAL) REAL acosh
 604  
 605  REAL_T a68g_acosh_real (REAL_T x)
 606  {
 607    if (x > 1 / sqrt (A68G_REAL_EPS)) {
 608      return (a68g_ln_real (x) + a68g_ln_real (2));
 609    } else if (x > 2) {
 610      return (a68g_ln_real (2 * x - 1 / (sqrt (x * x - 1) + x)));
 611    } else if (x > 1) {
 612      REAL_T t = x - 1;
 613      return (a68g_ln1p_real (t + sqrt (2 * t + t * t)));
 614    } else if (x == 1) {
 615      return (0);
 616    } else {
 617      A68G_INVALID (A68G_TRUE);
 618    }
 619  }
 620  
 621  // @brief PROC (REAL) REAL atanh
 622  
 623  REAL_T a68g_atanh_real (REAL_T x)
 624  {
 625    REAL_T a = ABS (x);
 626    A68G_INVALID (a >= 1);
 627    REAL_T s = (x < 0 ? -1 : 1);
 628    if (a >= 0.5) {
 629      return (s * 0.5 * a68g_ln1p_real (2 * a / (1 - a)));
 630    } else if (a > A68G_REAL_EPS) {
 631      return (s * 0.5 * a68g_ln1p_real (2 * a + 2 * a * a / (1 - a)));
 632    } else {
 633      return (x);
 634    }
 635  }
 636  
 637  //! @brief Inverse complementary error function.
 638  
 639  REAL_T a68g_inverfc_real (REAL_T y)
 640  {
 641    A68G_INVALID (y < 0 || y > 2);
 642    if (y == 0) {
 643      return A68G_REAL_MAX;
 644    } else if (y == 1) {
 645      return 0;
 646    } else if (y == 2) {
 647      return -A68G_REAL_MAX;
 648    } else {
 649  // Next is based on code that originally contained following statement:
 650  //   Copyright (c) 1996 Takuya Ooura. You may use, copy, modify this 
 651  //   code for any purpose and without fee.
 652      REAL_T s, t, u, v, x, z;
 653      z = (y <= 1 ? y : 2 - y);
 654      v = c_inverfc[0] - a68g_ln_real (z);
 655      u = sqrt (v);
 656      s = (a68g_ln_real (u) + c_inverfc[1]) / v;
 657      t = 1 / (u + c_inverfc[2]);
 658      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;
 659      t = c_inverfc[9] / (x + c_inverfc[9]);
 660      u = t - 0.5;
 661      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];
 662      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]);
 663      x += s * (x * s + 1);
 664      return (y <= 1 ? x : -x);
 665    }
 666  }
 667  
 668  //! @brief Inverse error function.
 669  
 670  REAL_T a68g_inverf_real (REAL_T y)
 671  {
 672    return a68g_inverfc_real (1 - y);
 673  }
 674  
 675  //! @brief PROC (REAL, REAL) REAL ln beta
 676  
 677  REAL_T a68g_ln_beta_real (REAL_T a, REAL_T b)
 678  {
 679    return lgamma (a) + lgamma (b) - lgamma (a + b);
 680  }
 681  
 682  //! @brief PROC (REAL, REAL) REAL beta
 683  
 684  REAL_T a68g_beta_real (REAL_T a, REAL_T b)
 685  {
 686    return a68g_exp_real (a68g_ln_beta_real (a, b));
 687  }
 688  
 689  //! brief PROC (INT) REAL fact
 690  
 691  REAL_T a68g_fact_real (INT_T n)
 692  {
 693    A68G_INVALID (n < 0 || n > A68G_MAX_FAC);
 694    return factable[n];
 695  }
 696  
 697  //! brief PROC (INT) REAL ln fact
 698  
 699  REAL_T a68g_ln_fact_real (INT_T n)
 700  {
 701    A68G_INVALID (n < 0);
 702    if (n <= A68G_MAX_FAC) {
 703      return ln_factable[n];
 704    } else {
 705      return lgamma (n + 1);
 706    }
 707  }
 708  
 709  //! @brief PROC choose = (INT n, m) REAL
 710  
 711  REAL_T a68g_choose_real (INT_T n, INT_T m)
 712  {
 713    A68G_INVALID (n < m);
 714    return factable[n] / (factable[m] * factable[n - m]);
 715  }
 716  
 717  //! @brief PROC ln choose = (INT n, m) REAL
 718  
 719  REAL_T a68g_ln_choose_real (INT_T n, INT_T m)
 720  {
 721    A68G_INVALID (n < m);
 722    return a68g_ln_fact_real (n) - (a68g_ln_fact_real (m) + a68g_ln_fact_real (n - m));
 723  }
 724  
 725  REAL_T a68g_beta_inc_real (REAL_T s, REAL_T t, REAL_T x)
 726  {
 727  // Incomplete beta function I{x}(s, t).
 728  // Continued fraction, see dlmf.nist.gov/8.17; Lentz's algorithm.
 729    if (x < 0 || x > 1) {
 730      errno = ERANGE;
 731      return -1;
 732    } else {
 733      const INT_T lim = 16 * sizeof (REAL_T);
 734      BOOL_T cont = A68G_TRUE;
 735  // Rapid convergence when x <= (s+1)/(s+t+2) or else recursion.
 736      if (x > (s + 1) / (s + t + 2)) {
 737  // B{x}(s, t) = 1 - B{1-x}(t, s)
 738        return 1 - a68g_beta_inc_real (s, t, 1 - x);
 739      }
 740  // Lentz's algorithm for continued fraction.
 741      REAL_T W = 1, F = 1, c = 1, d = 0;
 742      INT_T N, m;
 743      for (N = 0, m = 0; cont && N < lim; N++) {
 744        REAL_T T;
 745        if (N == 0) {
 746          T = 1;
 747        } else if (N % 2 == 0) {
 748  // d{2m} := x m(t-m)/((s+2m-1)(s+2m))
 749          T = x * m * (t - m) / (s + 2 * m - 1) / (s + 2 * m);
 750        } else {
 751  // d{2m+1} := -x (s+m)(s+t+m)/((s+2m+1)(s+2m))
 752          T = -x * (s + m) * (s + t + m) / (s + 2 * m + 1) / (s + 2 * m);
 753          m++;
 754        }
 755        d = 1 / (T * d + 1);
 756        c = T / c + 1;
 757        F *= c * d;
 758        if (F == W) {
 759          cont = A68G_FALSE;
 760        } else {
 761          W = F;
 762        }
 763      }
 764  // I{x}(s,t)=x^s(1-x)^t / s / B(s,t) F
 765      REAL_T beta = a68g_exp_real (lgamma (s) + lgamma (t) - lgamma (s + t));
 766      return a68g_x_up_y (x, s) * a68g_x_up_y (1 - x, t) / s / beta * (F - 1);
 767    }
 768  }
     


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