//!@file rts-lapack.c
//!@author J. Marcel van der Veer
//
// !@section Copyright
//
// This file is part of VIF - vintage FORTRAN compiler.
// Copyright 2020-2025 J. Marcel van der Veer <algol68g@xs4all.nl>.
//
//! @section License
//
// This program 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 3 of the License, or 
// (at your option) any later version.
//
// This program 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 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 <http://www.gnu.org/licenses/>.

//! @section Synopsis
//!
//! Runtime support for LAPACK subprograms.

// Auxilliary routines for LAPACK.

#include "vif.h"

logical_4 _lsame (char _p_, char _p_);
logical_4 _lsamen (int_4 *, char _p_, char _p_);
real_8 _dlamch (char _p_ cmach);
int_4 _dlamc1 (int_4 *, int_4 *, logical_4 *, logical_4 *);
int_4 _dlamc2 (int_4 *, int_4 *, logical_4 *, real_8 *, int_4 *, real_8 *, int_4 *, real_8 *);
real_8 _dlamc3 (real_8 *, real_8 *);
int_4 _dlamc4 (int_4 *, real_8 *, int_4 *);
int_4 _dlamc5 (int_4 *, int_4 *, int_4 *, logical_4 *, int_4 *, real_8 *);

static real_8 zero_arg = 0.0;

logical_4 _lsame (char _p_ ca, char _p_ cb)
{
//  -- LAPACK auxiliary routine (version 3.2) --
//   Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd..
//   November 2006
//
//   .. Scalar Arguments ..
//   ..
//
//  Purpose
//  =======
//
//  LSAME returns .TRUE. if CA is the same letter as CB regardless of
//  case.
//
//  Arguments
//  =========
//
//  CA    (input) CHARACTER*1
//  CB    (input) CHARACTER*1
//      CA and CB specify the single characters to be compared.
//
// This is the VIF version of the LAPACK routine.
//
  if (ca != NO_TEXT && cb != NO_TEXT) {
    return tolower (*ca) == tolower (*cb);
  } else {
    return FALSE;
  }
}

logical_4 _lsamen (int_4 *n, char _p_ ca, char _p_ cb)
{
//
//  -- LAPACK auxiliary routine (version 2.0) --
//     Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
//     Courant Institute, Argonne National Lab, and Rice University
//     September 30, 1994
//
//  Purpose
//  =======
//
//  LSAMEN  tests if the first N letters of CA are the same as the
//  first N letters of CB, regardless of case.
//  LSAMEN returns .TRUE. if CA and CB are equivalent except for case
//  and .FALSE. otherwise.  LSAMEN also returns .FALSE. if LEN( CA )
//  or LEN( CB ) is less than N.
//
//  Arguments
//  =========
//
//  N       (input) INTEGER
//          The number of characters in CA and CB to be compared.
//
//  CA      (input) CHARACTER*(*)
//  CB      (input) CHARACTER*(*)
//          CA and CB specify two character strings of length at least N.
//          Only the first N characters of each string will be accessed.
//
// This is the VIF version of the LAPACK routine.
//
  if (ca != NO_TEXT && cb != NO_TEXT) {
    return strncasecmp (ca, cb, (size_t) *n) == 0;
  } else {
    return FALSE;
  }
}

real_8 _dlamch (char _p_ cmach)
{
//
//  -- LAPACK auxiliary routine (version 3.2) --
//   Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd..
//   November 2006
//
//   .. Scalar Arguments ..
//   ..
//
//  Purpose
//  =======
//
//  DLAMCH determines double precision machine parameters.
//
//  Arguments
//  =========
//
//  CMACH   (input) CHARACTER*1
//      Specifies the value to be returned by DLAMCH:
//      = 'E' or 'e',   DLAMCH := eps
//      = 'S' or 's ,   DLAMCH := sfmin
//      = 'B' or 'b',   DLAMCH := base
//      = 'P' or 'p',   DLAMCH := eps*base
//      = 'N' or 'n',   DLAMCH := t
//      = 'R' or 'r',   DLAMCH := rnd
//      = 'M' or 'm',   DLAMCH := emin
//      = 'U' or 'u',   DLAMCH := rmin
//      = 'L' or 'l',   DLAMCH := emax
//      = 'O' or 'o',   DLAMCH := rmax
//
//      where
//
//      eps   = relative machine precision
//      sfmin = safe minimum, such that 1/sfmin does not overflow
//      base  = base of the machine
//      prec  = eps*base
//      t   = number of (base) digits in the mantissa
//      rnd   = 1.0 when rounding occurs in addition, 0.0 otherwise
//      emin  = minimum exponent before (gradual) underflow
//      rmin  = underflow threshold - base**(emin-1)
//      emax  = largest exponent before overflow
//      rmax  = overflow threshold  - (base**emax)*(1-eps)
//
  int_4 beta, imin, imax, it, i__1;
  logical_4 lrnd;
  real_8 rmach = 0.0, small;
  static logical_4 first = TRUE;
  static real_8 emin, prec, emax, rmin, rmax, rnd, eps, base, sfmin, t;
  if (first) {
    _dlamc2 (&beta, &it, &lrnd, &eps, &imin, &rmin, &imax, &rmax);
    base = (real_8) beta;
    t = (real_8) it;
    if (lrnd) {
      rnd = 1.0;
      i__1 = 1 - it;
      eps = _up_real_8 (base, i__1) / 2;
    } else {
      rnd = 0.0;
      i__1 = 1 - it;
      eps = _up_real_8 (base, i__1);
    }
    prec = eps * base;
    emin = (real_8) imin;
    emax = (real_8) imax;
    sfmin = rmin;
    small = 1.0 / rmax;
    if (small >= sfmin) {
// Use SMALL plus a bit, to avoid the possibility of rounding
// causing overflow when computing  1/sfmin.
      sfmin = small * (eps + 1.0);
    }
  }
//
  if (_lsame (cmach, "E")) {
    rmach = eps;
  } else if (_lsame (cmach, "S")) {
    rmach = sfmin;
  } else if (_lsame (cmach, "B")) {
    rmach = base;
  } else if (_lsame (cmach, "P")) {
    rmach = prec;
  } else if (_lsame (cmach, "N")) {
    rmach = t;
  } else if (_lsame (cmach, "R")) {
    rmach = rnd;
  } else if (_lsame (cmach, "M")) {
    rmach = emin;
  } else if (_lsame (cmach, "U")) {
    rmach = rmin;
  } else if (_lsame (cmach, "L")) {
    rmach = emax;
  } else if (_lsame (cmach, "O")) {
    rmach = rmax;
  }
//
  first = FALSE;
  return rmach;
}

int_4 _dlamc1 (int_4 _p_ beta, int_4 _p_ t, logical_4 _p_ rnd, logical_4 _p_ ieee1)
{
//
//
//  -- LAPACK auxiliary routine (version 3.2) --
//   Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd..
//   November 2006
//
//   .. Scalar Arguments ..
//   ..
//
//  Purpose
//  =======
//
//  DLAMC1 determines the machine parameters given by BETA, T, RND, and
//  IEEE1.
//
//  Arguments
//  =========
//
//  BETA  (output) INTEGER
//      The base of the machine.
//
//  T     (output) INTEGER
//      The number of ( BETA ) digits in the mantissa.
//
//  RND   (output) LOGICAL
//      Specifies whether proper rounding  ( RND = .TRUE. )  or
//      chopping  ( RND = .FALSE. )  occurs in addition. This may not
//      be a reliable guide to the way in which the machine performs
//      its arithmetic.
//
//  IEEE1   (output) LOGICAL
//      Specifies whether rounding appears to be done in the IEEE
//      'round to nearest' style.
//
//  Further Details
//  ===============
//
//  The routine is based on the routine  ENVRON  by Malcolm and
//  incorporates suggestions by Gentleman and Marovich. See
//
//   Malcolm M. A. (1972) Algorithms to reveal properties of
//    floating-point arithmetic. Comms. of the ACM, 15, 949-951.
//
//   Gentleman W. M. and Marovich S. B. (1974) More on algorithms
//    that reveal properties of floating point arithmetic units.
//    Comms. of the ACM, 17, 276-277.
//
  real_8 a, b, c__, f, t1, t2, d__1, d__2, one, qtr, savec;
  static int_4 lbeta, lt;
  static logical_4 first = TRUE, lieee1, lrnd;
  if (first) {
    one = 1.0;
// LBETA,  LIEEE1,  LT and  LRND  are the  local values  of  BETA,
// IEEE1, T and RND.
//
// Throughout this routine  we use the function  DLAMC3  to ensure
// that relevant values are  stored and not held in registers,  or
// are not affected by optimizers.
//
// Compute  a = 2.0**m  with the  smallest positive int_4 m such
// that
//
// fl( a + 1.0 ) = a.
//
    a = 1.0;
    c__ = 1.0;
// +     WHILE( C.EQ.ONE )LOOP
    while (c__ == one) {
      a *= 2;
      c__ = _dlamc3 (&a, &one);
      d__1 = -a;
      c__ = _dlamc3 (&c__, &d__1);
    }
// +     END WHILE
//
// Now compute  b = 2.0**m  with the smallest positive int_4 m
// such that
//
// fl( a + b ) .gt. a.
    b = 1.0;
    c__ = _dlamc3 (&a, &b);
// +     WHILE( C.EQ.A )LOOP
    while (c__ == a) {
      b *= 2;
      c__ = _dlamc3 (&a, &b);
    }
// +     END WHILE
//
// Now compute the base.  a and c  are neighbouring floating point
// numbers  in the  interval  ( beta**t, beta**( t + 1 ) )  and so
// their difference is beta. Adding 0.25 to c is to ensure that it
// is truncated to beta and not ( beta - 1 ).
    qtr = one / 4;
    savec = c__;
    d__1 = -a;
    c__ = _dlamc3 (&c__, &d__1);
    lbeta = (int_4) (c__ + qtr);
// Now determine whether rounding or chopping occurs,  by adding a
// bit  less  than  beta/2  and a  bit  more  than  beta/2  to  a.
    b = (real_8) lbeta;
    d__1 = b / 2;
    d__2 = -b / 100;
    f = _dlamc3 (&d__1, &d__2);
    c__ = _dlamc3 (&f, &a);
    if (c__ == a) {
      lrnd = TRUE;
    } else {
      lrnd = FALSE;
    }
    d__1 = b / 2;
    d__2 = b / 100;
    f = _dlamc3 (&d__1, &d__2);
    c__ = _dlamc3 (&f, &a);
    if (lrnd && c__ == a) {
      lrnd = FALSE;
    }
// Try and decide whether rounding is done in the  IEEE  'round to
// nearest' style. B/2 is half a unit in the last place of the two
// numbers A and SAVEC. Furthermore, A is even, i.e. has last  bit
// zero, and SAVEC is odd. Thus adding B/2 to A should not  change
// A, but adding B/2 to SAVEC should change SAVEC.
    d__1 = b / 2;
    t1 = _dlamc3 (&d__1, &a);
    d__1 = b / 2;
    t2 = _dlamc3 (&d__1, &savec);
    lieee1 = t1 == a && t2 > savec && lrnd;
// Now find  the  mantissa, t.  It should  be the  int_4 part of
// log to the base beta of a,  however it is safer to determine  t
// by powering.  So we find t as the smallest positive int_4 for
// which
//
//    fl( beta**t + 1.0 ) = 1.0.
    lt = 0;
    a = 1.0;
    c__ = 1.0;
// +     WHILE( C.EQ.ONE )LOOP
    while (c__ == one) {
      ++lt;
      a *= lbeta;
      c__ = _dlamc3 (&a, &one);
      d__1 = -a;
      c__ = _dlamc3 (&c__, &d__1);
    }
// +     END WHILE
  }
  *beta = lbeta;
  *t = lt;
  *rnd = lrnd;
  *ieee1 = lieee1;
  first = FALSE;
  return 0;
}

int_4 _dlamc2 (int_4 _p_ beta, int_4 _p_ t, logical_4 _p_ rnd, real_8 _p_ eps, int_4 _p_ emin, 
                   real_8 _p_ rmin, int_4 _p_ emax, real_8 _p_ rmax)
{
//
//  -- LAPACK auxiliary routine (version 3.2) --
//   Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd..
//   November 2006
//
//   .. Scalar Arguments ..
//   ..
//
//  Purpose
//  =======
//
//  DLAMC2 determines the machine parameters specified in its argument
//  list.
//
//  Arguments
//  =========
//
//  BETA  (output) INTEGER
//      The base of the machine.
//
//  T     (output) INTEGER
//      The number of ( BETA ) digits in the mantissa.
//
//  RND   (output) LOGICAL
//      Specifies whether proper rounding  ( RND = .TRUE. )  or
//      chopping  ( RND = .FALSE. )  occurs in addition. This may not
//      be a reliable guide to the way in which the machine performs
//      its arithmetic.
//
//  EPS   (output) DOUBLE PRECISION
//      The smallest positive number such that
//
//       fl( 1.0 - EPS ) .LT. 1.0,
//
//      where fl denotes the computed value.
//
//  EMIN  (output) INTEGER
//      The minimum exponent before (gradual) underflow occurs.
//
//  RMIN  (output) DOUBLE PRECISION
//      The smallest normalized number for the machine, given by
//      BASE**( EMIN - 1 ), where  BASE  is the floating point value
//      of BETA.
//
//  EMAX  (output) INTEGER
//      The maximum exponent before overflow occurs.
//
//  RMAX  (output) DOUBLE PRECISION
//      The largest positive number for the machine, given by
//      BASE**EMAX * ( 1 - EPS ), where  BASE  is the floating point
//      value of BETA.
//
//  Further Details
//  ===============
//
//  The computation of  EPS  is based on a routine PARANOIA by
//  W. Kahan of the University of California at Berkeley.
//
  int_4 gnmin, gpmin, i__, i__1, ngnmin, ngpmin;
  logical_4 ieee, lieee1, lrnd = TRUE;
  real_8 a, b, c__, d__1, d__2, d__3, d__4, d__5;
  real_8 half, one, two, rbase, sixth, small, third, zero;
  static int_4 lbeta, lemin, lemax, lt;
  static logical_4 first = TRUE, iwarn = FALSE;
  static real_8 leps, lrmin, lrmax;
  if (first) {
    zero = 0.0;
    one = 1.0;
    two = 2.0;
// LBETA, LT, LRND, LEPS, LEMIN and LRMIN  are the local values of
// BETA, T, RND, EPS, EMIN and RMIN.
//
// Throughout this routine  we use the function  DLAMC3  to ensure
// that relevant values are stored  and not held in registers,  or
// are not affected by optimizers.
//
// DLAMC1 returns the parameters  LBETA, LT, LRND and LIEEE1.
    _dlamc1 (&lbeta, &lt, &lrnd, &lieee1);
// Start to find EPS.
    b = (real_8) lbeta;
    i__1 = -lt;
    a = _up_real_8 (b, i__1);
    leps = a;
// Try some tricks to see whether or not this is the correct  EPS.
    b = two / 3;
    half = one / 2;
    d__1 = -half;
    sixth = _dlamc3 (&b, &d__1);
    third = _dlamc3 (&sixth, &sixth);
    d__1 = -half;
    b = _dlamc3 (&third, &d__1);
    b = _dlamc3 (&b, &sixth);
    b = abs (b);
    if (b < leps) {
      b = leps;
    }
    leps = 1.0;
// +     WHILE( ( LEPS.GT.B ).AND.( B.GT.ZERO ) )LOOP
    while (leps > b && b > zero) {
      leps = b;
      d__1 = half * leps;
// Computing 5th power
      d__3 = two, d__4 = d__3, d__3 *= d__3;
// Computing 2nd power
      d__5 = leps;
      d__2 = d__4 * (d__3 * d__3) * (d__5 * d__5);
      c__ = _dlamc3 (&d__1, &d__2);
      d__1 = -c__;
      c__ = _dlamc3 (&half, &d__1);
      b = _dlamc3 (&half, &c__);
      d__1 = -b;
      c__ = _dlamc3 (&half, &d__1);
      b = _dlamc3 (&half, &c__);
    }
// +     END WHILE
    if (a < leps) {
      leps = a;
    }
// Computation of EPS complete.
//
// Now find  EMIN.  Let A = + or - 1, and + or - (1 + BASE**(-3)).
// Keep dividing  A by BETA until (gradual) underflow occurs. This
// is detected when we cannot recover the previous A.
    rbase = one / lbeta;
    small = one;
    for (i__ = 1; i__ <= 3; ++i__) {
      d__1 = small * rbase;
      small = _dlamc3 (&d__1, &zero);
    }
    a = _dlamc3 (&one, &small);
    _dlamc4 (&ngpmin, &one, &lbeta);
    d__1 = -one;
    _dlamc4 (&ngnmin, &d__1, &lbeta);
    _dlamc4 (&gpmin, &a, &lbeta);
    d__1 = -a;
    _dlamc4 (&gnmin, &d__1, &lbeta);
    ieee = FALSE;
    if (ngpmin == ngnmin && gpmin == gnmin) {
      if (ngpmin == gpmin) {
	lemin = ngpmin;
// Non twos-complement machines, no gradual underflow; e.g., VAX
      } else if (gpmin - ngpmin == 3) {
	lemin = ngpmin - 1 + lt;
	ieee = TRUE;
// Non twos-complement machines, with gradual underflow; e.g., IEEE standard followers
      } else {
	lemin = _min (ngpmin, gpmin);
// A guess; no known machine
	iwarn = TRUE;
      }
    } else if (ngpmin == gpmin && ngnmin == gnmin) {
      if ((i__1 = ngpmin - ngnmin, abs (i__1)) == 1) {
	lemin = _max (ngpmin, ngnmin);
// Twos-complement machines, no gradual underflow; e.g., CYBER 205
      } else {
	lemin = _min (ngpmin, ngnmin);
// A guess; no known machine
	iwarn = TRUE;
      }
    } else if ((i__1 = ngpmin - ngnmin, abs (i__1)) == 1 && gpmin == gnmin) {
      if (gpmin - _min (ngpmin, ngnmin) == 3) {
	lemin = _max (ngpmin, ngnmin) - 1 + lt;
// Twos-complement machines with gradual underflow; no known machine
      } else {
	lemin = _min (ngpmin, ngnmin);
// A guess; no known machine
	iwarn = TRUE;
      }
    } else {
// Computing MIN
      i__1 = _min (ngpmin, ngnmin), i__1 = _min (i__1, gpmin);
      lemin = _min (i__1, gnmin);
// A guess; no known machine
      iwarn = TRUE;
    }
    first = FALSE;
// **
// Comment out this if block if EMIN is ok
    if (iwarn) {
      fprintf (stderr, "WARNING. The value EMIN may be incorrect: EMIN = %d",
	       lemin);
      fprintf (stderr,
	       "If, after inspection, the value EMIN looks acceptable please");
      fprintf (stderr,
	       "comment out the IF block as marked within the code of routine,");
      fprintf (stderr, "DLAMC2, otherwise supply EMIN explicitly.");
      first = TRUE;
    }
// Assume IEEE arithmetic if we found denormalised  numbers above,
// or if arithmetic seems to round in the  IEEE style,  determined
// in routine DLAMC1. A true IEEE machine should have both  things
// true; however, faulty machines may have one or the other.
    ieee = ieee || lieee1;
// Compute  RMIN by successive division by  BETA. We could compute
// RMIN as BASE**( EMIN - 1 ),  but some machines underflow during
// this computation.
    lrmin = 1.0;
    i__1 = 1 - lemin;
    for (i__ = 1; i__ <= i__1; ++i__) {
      d__1 = lrmin * rbase;
      lrmin = _dlamc3 (&d__1, &zero);
    }
// Finally, call DLAMC5 to compute EMAX and RMAX.
    _dlamc5 (&lbeta, &lt, &lemin, &ieee, &lemax, &lrmax);
  }
  *beta = lbeta;
  *t = lt;
  *rnd = lrnd;
  *eps = leps;
  *emin = lemin;
  *rmin = lrmin;
  *emax = lemax;
  *rmax = lrmax;
  return 0;
}

real_8 _dlamc3 (real_8 _p_ a, real_8 _p_ b)
{
//
//  -- LAPACK auxiliary routine (version 3.2) --
//   Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd..
//   November 2006
//
//   .. Scalar Arguments ..
//   ..
//
//  Purpose
//  =======
//
//  DLAMC3  is intended to force  A  and  B  to be stored prior to doing
//  the addition of  A  and  B ,  for use in situations where optimizers
//  might hold one of these in a register.
//
//  Arguments
//  =========
//
//  A     (input) DOUBLE PRECISION
//  B     (input) DOUBLE PRECISION
//      The values A and B.
//
  return (*a) + (*b);
}

int_4 _dlamc4 (int_4 _p_ emin, real_8 _p_ start, int_4 _p_ base)
{
//
//  -- LAPACK auxiliary routine (version 3.2) --
//   Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd..
//   November 2006
//
//   .. Scalar Arguments ..
//   ..
//
//  Purpose
//  =======
//
//  DLAMC4 is a service routine for DLAMC2.
//
//  Arguments
//  =========
//
//  EMIN  (output) INTEGER
//      The minimum exponent before (gradual) underflow, computed by
//      setting A = START and dividing by BASE until the previous A
//      can not be recovered.
//
//  START   (input) DOUBLE PRECISION
//      The starting point for determining EMIN.
//
//  BASE  (input) INTEGER
//      The base of the machine.
//
  int_4 i__, i__1;
  real_8 a, b1, b2, c1, c2, d__1, d1, d2, one, zero, rbase;
  a = *start;
  one = 1.0;
  rbase = one / *base;
  zero = 0.0;
  *emin = 1;
  d__1 = a * rbase;
  b1 = _dlamc3 (&d__1, &zero);
  c1 = a;
  c2 = a;
  d1 = a;
  d2 = a;
// +  WHILE( ( C1.EQ.A ).AND.( C2.EQ.A ).AND.
//  $     ( D1.EQ.A ).AND.( D2.EQ.A )    )LOOP
  while (c1 == a && c2 == a && d1 == a && d2 == a) {
    --(*emin);
    a = b1;
    d__1 = a / *base;
    b1 = _dlamc3 (&d__1, &zero);
    d__1 = b1 * *base;
    c1 = _dlamc3 (&d__1, &zero);
    d1 = zero;
    i__1 = *base;
    for (i__ = 1; i__ <= i__1; ++i__) {
      d1 += b1;
    }
    d__1 = a * rbase;
    b2 = _dlamc3 (&d__1, &zero);
    d__1 = b2 / rbase;
    c2 = _dlamc3 (&d__1, &zero);
    d2 = zero;
    i__1 = *base;
    for (i__ = 1; i__ <= i__1; ++i__) {
      d2 += b2;
    }
  }
// +  END WHILE
  return 0;
}

int_4 _dlamc5 (int_4 _p_ beta, int_4 _p_ p, int_4 _p_ emin, logical_4 _p_ ieee, int_4 _p_ emax, real_8 _p_ rmax)
{
//
//  -- LAPACK auxiliary routine (version 3.2) --
//   Univ. of Tennessee, Univ. of California Berkeley and NAG Ltd..
//   November 2006
//
//   .. Scalar Arguments ..
//   ..
//
//  Purpose
//  =======
//
//  DLAMC5 attempts to compute RMAX, the largest machine floating-point
//  number, without overflow.  It assumes that EMAX + abs(EMIN) sum
//  approximately to a power of 2.  It will fail on machines where this
//  assumption does not hold, for example, the Cyber 205 (EMIN = -28625,
//  EMAX = 28718).  It will also fail if the value supplied for EMIN is
//  too large (i.e. too close to zero), probably with overflow.
//
//  Arguments
//  =========
//
//  BETA  (input) INTEGER
//      The base of floating-point arithmetic.
//
//  P     (input) INTEGER
//      The number of base BETA digits in the mantissa of a
//      floating-point value.
//
//  EMIN  (input) INTEGER
//      The minimum exponent before (gradual) underflow.
//
//  IEEE  (input) LOGICAL
//      A logical flag specifying whether or not the arithmetic
//      system is thought to comply with the IEEE standard.
//
//  EMAX  (output) INTEGER
//      The largest exponent before overflow
//
//  RMAX  (output) DOUBLE PRECISION
//      The largest machine floating-point number.
//
//   First compute LEXP and UEXP, two powers of 2 that bound
//   abs(EMIN). We then assume that EMAX + abs(EMIN) will sum
//   approximately to the bound that is closest to abs(EMIN).
//   (EMAX is the exponent of the required number RMAX).
//
  int_4 exbits, expsum, i__, i__1, try, lexp, uexp, nbits;
  real_8 d__1, oldy, recbas;
  lexp = 1;
  exbits = 1;
  logical_4 cont = TRUE;
  while (cont) {
    try = lexp << 1;
    if (try <= -(*emin)) {
      lexp = try;
      ++exbits;
    } else {
      cont = FALSE;
    }
  }
  if (lexp == -(*emin)) {
    uexp = lexp;
  } else {
    uexp = try;
    ++exbits;
  }
// Now -LEXP is less than or equal to EMIN, and -UEXP is greater
// than or equal to EMIN. EXBITS is the number of bits needed to
// store the exponent.
  if (uexp + *emin > -lexp - *emin) {
    expsum = lexp << 1;
  } else {
    expsum = uexp << 1;
  }
// EXPSUM is the exponent range, approximately equal to
// EMAX - EMIN + 1 .
  *emax = expsum + *emin - 1;
  nbits = exbits + 1 + *p;
// NBITS is the total number of bits needed to store a
// floating-point number.
  if (nbits % 2 == 1 && *beta == 2) {
// Either there are an odd number of bits used to store a
// floating-point number, which is unlikely, or some bits are
// not used in the representation of numbers, which is possible,
// (e.g. Cray machines) or the mantissa has an implicit bit,
// (e.g. IEEE machines, Dec Vax machines), which is perhaps the
// most likely. We have to assume the last alternative.
// If this is true, then we need to reduce EMAX by one because
// there must be some way of representing zero in an implicit-bit
// system. On machines like Cray, we are reducing EMAX by one
// unnecessarily.
    --(*emax);
  }
  if (*ieee) {
// Assume we are on an IEEE machine which reserves one exponent
// for infinity and NaN.
    --(*emax);
  }
// Now create RMAX, the largest machine number, which should
// be equal to (1.0 - BETA**(-P)) * BETA**EMAX .
//
// First compute 1.0 - BETA**(-P), being careful that the
// result is less than 1.0 .
  recbas = 1.0 / *beta;
  real_8 z__ = *beta - 1.0;
  real_8 y = 0.0;
  i__1 = *p;
  for (i__ = 1; i__ <= i__1; ++i__) {
    z__ *= recbas;
    if (y < 1.0) {
      oldy = y;
    }
    y = _dlamc3 (&y, &z__);
  }
  if (y >= 1.0) {
    y = oldy;
  }
// Now multiply by BETA**EMAX to get RMAX.
  i__1 = *emax;
  for (i__ = 1; i__ <= i__1; ++i__) {
    d__1 = y * (*beta);
    y = _dlamc3 (&d__1, &zero_arg);
  }
  *rmax = y;
  return 0;
}
