/*
This file contains proc SMSTRG, which performs a
linear regression with symmetric stable disturbances,
preceded by a short driver program to demonstrate its
use.  See the initial comments in SMSTRG for details.

   Written and submitted by J. Huston McCulloch for public, non-
   commercial use, August 16, 1997.
   There are no performance guarantees. Feedback is welcomed.
   Contact author at                mcculloch.2@osu.edu
		Economics Dept.             (614) 292-0382
		Ohio State Univ.            FAX   292-3906
		1945 N. High St.
		Columbus, OH 43210
		http://www.econ.ohio-state.edu/jhm/jhm.html

Included in this file are all procs called by SMSTRG,
namely SRLIKC, SYMSTB1, POLY, DGAMMA, STAB, FRCTL and MYOLS.
Also included is RNDSSTA, which is used by the demonstration program.
Numerous globals are generated, all of which are "uglified"
by the addtion of a terminal underscore (_) to reduce
the likelihood of a conflict.
*/

/* demonstrates smstrg */
new;
n = 1000;
alpha = 1.5;
x = seqa(0,1,n)/n;
y = 10 + 3 * x + 2 * rndssta(alpha, n);
{alphahat, chat, betahat, cov, its, maxlik, lrstat} = smstrg(y,x,1,.0001,2,1);
end;

proc(7) = smstrg(y,x,const,tol, vu, printout);
/* Performs linear regression y = X * beta + epsilon with
   symmetric stable residuals by quasi-Newton method, using
   OPG matrix.
   Requires procs symstb1, myols, stab, srlikc.
   Returns {alpha, c, beta, cov, its, maxlik, lrstat}.
   Converges when d log L (adjusted for splits) < tol .
   const = 1 to add a constant, which will be beta[1]
   vu = 0 for nothing on screen
      = 1 for results only on screen
      = 2 for progress on screen
      = 3 for above with pauses
   printout = 0 for nothing
            = 1 for printed results
   cov = cov(alpha, ln(c), beta'), as computed from numerical Hessian.
   If alpha is at (or within numerical increment of) .84
   or 2.00, alpha is treated as restricted, and cov[.,1] =
   cov[1,.] is returned as 0.  */
format /rdn 10,4;
local n, xx, k, beta, se, s2, dw, cln, thetaols, dth, view, theta;
local maxlik, its, alpha, c, glik, lrstat, tstat;
local h, dt, i, j, cov, hessian, var;
local e, n1, n2, ytrim, xtrim, thetatrm, trimlik, dt1, dt2;
local grad, g, hstar, d, newth, lambda, dlik, newlik;
local alfmin, alfmax, wald;
local delta, zeta, betas;
local f1, f2, aye, i1, lik;
local dmax, sqn;
declare xxglob_, yglob_;
declare lnden_;
alfmin = 0.84;
alfmax = 2.00;
n = rows(y);
xx = x;
if const == 1;
  xx = ones(n,1)~xx;
  endif;
k = cols (xx);
yglob_ = y;
xxglob_ = xx;
if printout;
  lprint "SYMMETRIC STABLE REGRESSION (SMSTRG)";
  lprint;
  lprint "OLS preliminary estimates";
  endif;
{beta, se, s2, dw} = myols(y,xx,0,printout);
var = s2*(n-k)/n;
cln = ln(sqrt(var/2));
@ theta is a column vector in smstrg@
thetaols = 2|cln|beta;
glik = -(n/2)*(ln(2*pi*var)+1);
theta = thetaols;
maxlik = glik;
if printout;
  lprint "  log L = " glik;
  endif;
sqn = sqrt(n);
dth = 1/sqn|1/sqn|se; @ (used for Hessian) @
e = y - xx * beta;
e = e~y~xx;
e = sortc(e,1);
n1 = round(n/4);
n2 = n + 1 - n1;
if n2-n1+1 < k+2; goto start; endif;
e = e[n1:n2,.];
ytrim = e[.,2];
xtrim = e[.,3:2+k];
if printout > 1;
  lprint "Trimmed OLS preliminary estimates";
  endif;
{beta, se, s2, dw} = myols(ytrim,xtrim,0,printout > 1);
e = y - xx * beta;
{alpha, betas, c, delta, zeta} = stab(e,0);
cln = ln(c);
thetatrm = alpha|cln|beta;
trimlik = srlikc(thetatrm);
if printout;
  lprint;
  lprint "Quantile preliminary estimates";
  lprint "  alpha = " alpha;
  lprint "  c =     " c;
  lprint "  log L = " trimlik;
  endif;
if trimlik > glik;
  theta = thetatrm;
  maxlik = trimlik;
  endif;

start:
its = 1;
if vu > 1;
  print its~theta';
  if vu > 2;
    print "waiting "; wait;
    endif;
  endif;

newit:
h = .001;
dt1 = h * dth;
dt2 = - dt1;
alpha = theta[1];
if alpha + dt1[1] > alfmax; dt1[1] = alfmax - alpha; endif;
if alpha + dt2[1] < alfmin; dt2[1] = alfmin - alpha; endif;
dt = dt1-dt2;
dt1 = diagrv(zeros(k+2,k+2),dt1);
dt2 = diagrv(zeros(k+2,k+2),dt2);
grad = zeros(k+2,1);
g = zeros(n,k+2);
j = 1;
do until j > k+2;
grad[j] = srlikc(theta + dt1[.,j]);
g[.,j] = lnden_;
grad[j] = (grad[j] - srlikc(theta + dt2[.,j]))/dt[j];
g[.,j] = (g[.,j] - lnden_) / dt[j];
j = j + 1;
endo;
hstar = g'*g;
if (theta[1] >= alfmax and grad[1] >= 0) or
   (theta[1] <= alfmin and grad[1] <= 0) ;
  d = 0|inv(hstar[2:k+2,2:k+2])*grad[2:k+2];
  if vu > 1; print "restricted d "; print d'; endif;
  else;
  d = inv(hstar)*grad;
  if vu > 1; print "regular d "; print d'; endif;
  endif;
dmax = maxc(abs(d)./dth);
newth = theta + d;
lambda = 1;
if newth[1] > alfmax;
  lambda = (alfmax-theta[1])/(newth[1]-theta[1]);
  if vu > 1; print "foreshortened d. lambda = "; print lambda; endif;
  newth = theta + lambda * d;
  elseif newth[1] < alfmin;
  lambda = (alfmin-theta[1])/(newth[1]-theta[1]);
  if vu > 1; print "foreshortened d (low alf) "; print lambda; endif;
  newth = theta + lambda * d;
  endif;

newlik:
its = its + 1;
newlik = srlikc(newth);
if vu > 1;
  print (its|theta|newlik)';
  if vu > 2; wait; endif;
  endif;
if newlik < maxlik and dmax > 1.e-6;
  if vu > 1; print "overshoot"; endif;
  lambda = lambda /2;
  newth = theta + lambda * d;
  goto newlik;
else;
  theta = newth;
  dlik = newlik - maxlik;
  maxlik = newlik;
  if dlik > tol * lambda;
     goto newit;
     endif;
  endif;

hessian:
alpha = theta[1];
cln = theta[2];
c = exp(cln);
beta = theta[3:k+2];
if vu; print "Computing Hessian"; endif;
h = .01;
dt = h * dth;
f1 = zeros(k+2,1);
f2 = f1;
hessian = zeros(k+2,k+2);
aye = eye(k+2);
i1 = 1;
if (alpha + abs(dt[1]) > alfmax or alpha - abs(dt[1]) < alfmin);
  i1 = 2;
  endif;
i = i1;
do until i > k+2;
f1[i] = srlikc(theta + dt[i] * aye[.,i]);
f2[i] = srlikc(theta - dt[i] * aye[.,i]);
i = i + 1;
endo;
i = i1;
do until i > k+2;
j = i1;
do until j > k+2;
@ if (i < 3 and j > 2) or (j < 3 and i > 2);
  /* for any symmetric distribution, information matrix is
  block diagonal */
  goto nextj;
else@
if i == j;
  hessian[i,j] = (2*maxlik - f1[i] - f2[i])/(-dt[i]^2);
else;
  lik = srlikc(theta + dt[i] * aye[.,i] - dt[j] * aye[.,j]);
  hessian[i,j] = (lik - f1[i] -f2[j] + maxlik) / (-dt[i]*dt[j]);
endif;
nextj:
j = j + 1;
endo;
i = i + 1;
endo;
@ This numerical Hessian may not be precisely symmetrical @
hessian = (hessian + hessian')/2;

if i1 == 1;
  cov = inv(-hessian);
else;
  cov = inv(-hessian[2:k+2,2:k+2]);
  cov = zeros(k+2,1)~(zeros(1,k+1)|cov);
endif;
se = sqrt(diag(cov));
wald = beta./se[3:k+2];

if printout;
  lprint;
  lprint "Symmetric Stable ML Estimates";
  lprint "  alpha = " alpha " se " se[1];
  lprint "  log c = " cln " se " se[2];
  lprint "  c =     " c;
  lprint "  log L = " maxlik;
  lprint "  LR stat " 2*(maxlik-glik);
  if const;
    lprint "  beta[1] is intercept";
    endif;
  lprint;
  lprint "      j      beta[j]      se     Wald ratio";
  lprint seqa(1,1,k)~beta~se[3:k+2]~wald;
  lprint;
  endif;
if vu;
  print;
  print "Symmetric Stable ML Estimates";
  print "  alpha = " alpha " se " se[1];
  print "  c =     " c;
  print "  log c = " cln " se " se[2];
  print "  log L = " maxlik;
  print "  LR stat " 2*(maxlik-glik);
  if const;
    print "  beta[1] is intercept";
    endif;
  print;
  print "      j      beta[j]      se     Wald ratio";;
  print seqa(1,1,k)~beta~se[3:k+2]~wald;
  endif;
if vu == 3;
  wait;
  endif;
retp(alpha, c, beta, cov, its,maxlik,2*(maxlik-glik));
endp;

proc srlikc(theta);
/* theta = alpha|cln|beta
   receives y as yglob_, XX as XXglob_ (globals)
   passes likelihood contributions back as global lnden_
   theta is a column vector in this "c" version of srlik */
local alpha, c, beta, e;
alpha = theta[1];
c = exp(theta[2]);
beta = theta[3:rows(theta)];
e = yglob_ - xxglob_ * beta;
lnden_ = ln(symstb1(e/c, alpha)/c);
retp(sumc(lnden_));
endp;

proc symstb1(x,alpha);
@ June 1993: GAUSS version @
@ If you use this program, please cite @
@  J. Huston McCulloch, "Numerical Approximation of the Symmetric @
@  Stable Distribution and Density," OSU Econ Dept., Oct. 1994.  @
@ x may be a column vector; alpha is a scalar @
@ returns sden = symmetric stable density @
@ Program symstb3 gives distribution fn as well @
@ Program symstb1 gives only density @
declare s_, sqpi_, a2_, cpxp0_, gpxp0_, cpxpp0_, gpxpp0_, cppp_, gppp_, c5i_;
declare a_, alf2_, alf1_, pd_, p_, pdd_;
declare first_ = 1;
declare oldalf_ = -999.99;
local r, q, znot, zn4, zn5;
local i, i1, j, j1;
local pialf, sp0, sppp0, xp0, xpp0, xppp0, spzp1;
local rp0, rpp0, rppp0, rp1, b, c;
local xa1, xa1a, z, x1, x2, zp, x1p, x2p, rpz, sden;
local x11, x2a1, x2pp, cd, gd;
fn afun(alpha) = 2^(1/alpha) - 1;
fn cden(x) = 1/(pi*(1+x.*x));
fn gden(x) = exp(maxc(-700*ones(1,rows(x)) | -(x.*x)'/4)) / (2*sqpi_);
fn zfun(x, alpha, a) = 1 - (1 + a*x) ^ (-alpha);
fn zpfun(x, alpha, a) = alpha * a * (1+a*x) ^ (-alpha -1);
fn xfun(z, alpha, a) = ((1-z)^(-1/alpha) - 1) / a;

if(not first_); goto starta; endif;
@ this s is the gij of the writeup @
s_ =    {1.8514190959D2,   -4.6769332663D2,
         4.8424720302D2,   -1.7639153404D2,
        -3.0236552164D2,    7.6351931975D2,
        -7.8560342101D2,    2.8426313374D2,
         4.4078923600D2,   -1.1181138121D3,
         1.1548311335D3,   -4.1969666223D2,
        -5.2448142165D2,    1.3224487717D3,
        -1.3555648053D3,    4.8834079950D2,
         5.3530435018D2,   -1.3374570340D3,
         1.3660140118D3,   -4.9286099583D2,
        -4.8988957866D2,    1.2091418165D3,
        -1.2285872257D3,    4.4063174114D2,
         3.2905528742D2,   -7.3211767697D2,
         6.8183641829D2,   -2.2824291084D2,
        -2.1495402244D2,    3.9694906604D2,
        -3.3695710692D2,    1.0905855709D2,
         2.1112581866D2,   -2.7921107017D2,
         1.1717966020D2,    3.4394664342D0,
        -2.6486798043D2,    1.1999093707D2,
         2.1044841328D2,   -1.5110881541D2,
         9.4105784123D2,   -1.7221988478D3,
         1.4087544698D3,   -4.2472511892D2,
        -2.1990475933D3,    4.2637720422D3,
        -3.4723981786D3,    1.0174373627D3,
         3.1047490290D3,   -5.4204210990D3,
         4.2221052925D3,   -1.2345971177D3,
        -5.1408260668D3,    1.1090264364D4,
        -1.0270337246D4,    3.4243449595D3,
         1.1215157876D4,   -2.4243529825D4,
         2.1536057267D4,   -6.8490996103D3,
        -1.8120631586D4,    3.1430132257D4,
        -2.4164285641D4,    6.9126862826D3,
         1.7388413126D4,   -2.2108397686D4,
         1.3397999271D4,   -3.1246611987D3,
        -7.2435775303D3,    4.3545399418D3,
         2.3616155949D2,   -7.6571653073D2,
        -8.7376725439D3,    1.5510852129D4,
        -1.3789764138D4,    4.6387417712D3};
s_ = reshape (s_,19, 4);
sqpi_ = sqrt(pi);
a2_ = afun(2);
cpxp0_ = 1/pi;
gpxp0_ = 1/(4*sqpi_*a2_);
cpxpp0_ = 2/pi;
gpxpp0_ = gpxp0_ * 1.5;
cppp_ = 4/pi;
gppp_ = gpxpp0_ * 2.5 - 1/(32*sqpi_*a2_^3);
c5i_ = {1, 5, 10, 10, 5, 1};
first_ = 0;

starta:
if alpha > 2.000001;
  format /rdn 20,15;
  print "alpha too big: " alpha;
  end;
  endif;
if alpha >= 2;
  sden = gden(x);
  retp(sden);
  endif;
if alpha == oldalf_; goto startx; endif;
znot = seqa(1,1,19) /20;
zn4 = (1-znot)^4;
zn5 = zn4 .* (1-znot);
a_ = afun(alpha);
alf2_ = 2 - alpha;
alf1_ = alpha - 1;
pialf = pi * alpha;
sp0 = dgamma(1/alpha) / pialf;
sppp0 = -dgamma(3/alpha) / pialf;
xp0 = 1/(alpha * a_);
xpp0 = xp0 * (1+alpha) / alpha;
xppp0 = xpp0 * (1+2*alpha)/alpha;
rp0 = -sp0 * xp0 + alf2_ * cpxp0_ + alf1_ * gpxp0_;
rpp0 = -sp0 * xpp0 + alf2_ * cpxpp0_ + alf1_ * gpxpp0_;
rppp0 = -sp0 * xppp0 - sppp0 * xp0^3 + alf2_ * cppp_ + alf1_ * gppp_;
spzp1 = (a_^alpha) * dgamma(alpha) * sin(pialf / 2) / pi;
rp1 = -spzp1 + alf2_ / pi;
q = zeros(6,1);
q[2] = rp0;
q[3] = rpp0/2;
q[4] = rppp0/6;
r = alf2_ * (s_ * (alf2_^seqa(1,1,4) - 1));
b = -sumc(q[2:4]) - sumc(r.*zn5);
c = rp1 - q[2:4]' * seqa(1,1,3) - 5 * sumc(r.*zn4);
q[5] = 5*b-c;
q[6] = c-4*b;
p_ = zeros(6,20);
i = 0;
do until i > 5;
i1 = i + 1;
j = 1;
do until j > 19;
j1 = j + 1;
p_[i1, j1] = p_[i1, j] + r[j] * (-znot[j])^(5-i);
j = j1;
endo;
i = i1;
endo;
p_ = q + c5i_ .* p_;
pd_ = p_[2:6,.] .* seqa (1,1,5);
oldalf_ = alpha;

startx:
xa1 = 1 + a_ * abs(x);
xa1a = xa1 ^ (-alpha);
z = 1 - xa1a;
zp = alpha * a_ * xa1a ./ xa1;
x11 = 1/xa1a;
x1 = x11 - 1;
x1p = x11 .* x11;
x2a1 = 1/sqrt(xa1a);
x2 = (x2a1 -1) /a2_;
x2p = x2a1 ./ (2*a2_ * xa1a);
cd = cden (x1);
gd = gden(x2);
j1 = trunc(20 * z) + 1;
rpz = poly(pd_[.,j1], z, 4);
sden = (alf2_ * cd .* x1p + alf1_ * gd .* x2p - rpz) .* zp;
retp (sden);
endp;

proc poly(a, x, k);
local sum, j;
sum = a[k+1,.]';
j = k;
do until j ==0;
sum = sum .* x + a[j,.]';
j = j - 1;
endo;
retp(sum);
endp;

proc dgamma(x);
@ Note -- GAUSS 3.2+ has adopted an improved gamma function similar
to this one, making this proc redundant. @
local t, ser, c, stp;
c = {76.18009172947146, -86.50532032941677, 24.01409824083091,
     -1.231739572450155, .001208650973866179, -.5395239384953d-5};
stp = 2.5066282746310005;
t = x + 5.5;
t = t^(x+.5)/exp(t);
ser = 1.000000000190015 + sumc(c./(x+seqa(1,1,6)));
retp(t * stp * ser / x);
endp;

proc rndssta(alpha, r);
/* Returns rX1 vector of iid standard symmetric stable pseudo-random
   numbers with characteristic exponent alpha in [.1,2], and skewness
   parameter beta = 0, using method of Chambers, Mallows and Stuck
   (JASA 1976, 340-4).
   Encoded in GAUSS by J. Huston McCulloch, Ohio State University Econ Dept.
   (mcculloch.2@osu.edu), 12/95, directly from the CMS equation (4.1).
   Each r.v. has log characteristic function
        log E exp(ixt) = -abs(t)^alpha
   When alpha = 2, the distribution is Gaussian, with variance 2.
   When alpha = 1, the distribution is standard Cauchy.
   If alpha > 1, Ex = 0.  In all cases, the median is 0.
   This proc uses 2r uniform pseudo-random numbers from RNDU.  */

local phi, w;
if (alpha < .1) or (alpha > 2);
  format /rdn 10,4;
  print "Alpha must be in [.1,2] for proc RNDSSTA." ;
  print "Actual value is " alpha;
  if alpha < .1 and alpha > 0;
    print "If alpha < .1, probability of overflow becomes non-negligible.";
    endif;
  stop;
  endif;
w = - ln(rndu(r,1));
phi = (rndu(r,1) - .5) * pi;
if feq(alpha,2);
  retp(2 * sqrt(w) .* sin(phi));
elseif feq(alpha,1);
  retp(tan(phi));
endif;
retp((cos((1-alpha) * phi) ./ w) ^ (1/alpha - 1) .* sin(alpha * phi)
             ./ cos(phi) ^ (1/alpha));
endp;

proc(5) = stab(x,symm);
/* McCulloch 12/95 GAUSS version (converted from FORTRAN)
  TAKES A STAB AT STABLE DISTRIBUTION PARAMETERS USING J.H. MCCULLOCH,
  "SIMPLE CONSISTENT ESTIMATORS OF STABLE DISTRIBUTION PARAMETERS."
  (COMMUNICATIONS IN STATISTICS:  SIMULATION & COMPUTATION 15#4,
  1109-1136 (1986))
  INPUT VARIABLES FOR STAB:
    x is a column vector of iid stable random variables with characteristic
      exponent alpha in [.5, 2].
    symm = 0 if beta is to be estimated.
           1 if only the symmetric case (beta = 0) is to be considered.
  OUTPUT VARIABLES:
     alpha = characteristic exponent.
     beta = skewness parameter (= 0 if symmetrical)
     c = scale parameter
     delta = location parameter
     zeta = shifted location parameter (= delta if beta = 0)
          = delta + beta*c*tan(pi*alpha/2), for alpha NE 1,
          = delta, for alpha = 1;
  PARAMETERIZATION:
     The distribution of x has the log characteristic function
        ln Eexp(i*x*t) = i*delta*t + psi(c*t),
     where the standard (c = 1, delta = 0) log c.f. is
        psi(t) = -abs(t)^alpha*[1-i*b*sign(t)*tan(pi*alpha/2)],
                    for alpha NE 1,
               = -abs(t)*[1+i*beta*(2/pi)*sign(t)*ln(abs(t)),
                    for alpha = 1.
     (See McCulloch, "On the Parameterization of the Afocal Stable
     Distribution," _Bulletin of the London Mathematical Society_,
     forthcoming 1996.  (1.1) in McCulloch (1986) should have a c before
     the t inside the log in the last line for internal consistency.)
     With this parameterization, if X has parameters (alpha, beta,
     c, delta), then (X-delta)/c has parameters (alpha, beta, 1, 0),
     for all alpha.

   Alpha is constrained to lie between 0.5 and 2.0.
     If the input data is inconsistent with any alpha value in this
     range, alpha will be returned either as 0.5 or as 2.0.  The former
     should be interpreted as meaning that alpha is indeterminately less
     than 0.5.  The latter indicates that the
     data is either Gaussian or too platykurtic (that's the
     opposite of leptokurtic) to be consistent with any stable distribution.
   Beta is constrained to lie between -1.0 and +1.0.  Note that if alpha
     is 2.0, beta has no effect on the distribution.  In this case 0.0
     is returned for beta regardless of the data.
   C is constrained to be positive.  If the interquartile range is
     0, the apparent scale is 0 and (-1,-1,-1,-1,-1) is returned.
     This is an error condition.                  */

local ena, enb, enc, za, ah, bh, q05, q25, q50, q75, q95, xx;
local d, cn, cnu, an, bn, sign, alpha, beta, c, delta, zeta;
local i, j, ii, jj, aa, b, bb, t1, t2, s1, s2, dt, ds, s, t;

@  ENA IS THE INDEX NU SUB ALPHA DESCRIBED IN MCCULLOCH (1986).  @
let ena[16,5] = {
           2.4388     2.4388     2.4388     2.4388     2.4388,
           2.5120     2.5117     2.5125     2.5129     2.5148,
           2.6080     2.6093     2.6101     2.6131     2.6174,
           2.7369     2.7376     2.7387     2.7420     2.7464,
           2.9115     2.9090     2.9037     2.8998     2.9016,
           3.1480     3.1363     3.1119     3.0919     3.0888,
           3.4635     3.4361     3.3778     3.3306     3.3161,
           3.8824     3.8337     3.7199     3.6257     3.5997,
           4.4468     4.3651     4.1713     4.0052     3.9635,
           5.2172     5.0840     4.7778     4.5122     4.4506,
           6.3140     6.0978     5.6241     5.2195     5.1256,
           7.9098     7.5900     6.8606     6.2598     6.1239,
          10.4480     9.9336     8.7790     7.9005     7.6874,
          14.8378    13.9540    12.0419    10.7219    10.3704,
          23.4831    21.7682    18.3320    16.2163    15.5841,
          44.2813    40.1367    33.0018    29.1399    27.7822 };
@  ENB IS THE INDEX NU SUB BETA. @
let enb[16,5] = {
           0.0000     0.0000     0.0000     0.0000     0.0000,
           0.0000     0.0179     0.0357     0.0533     0.0710,
           0.0000     0.0389     0.0765     0.1133     0.1480,
           0.0000     0.0626     0.1226     0.1784     0.2281,
           0.0000     0.0895     0.1736     0.2478     0.3090,
           0.0000     0.1183     0.2282     0.3199     0.3895,
           0.0000     0.1478     0.2849     0.3942     0.4686,
           0.0000     0.1769     0.3422     0.4703     0.5458,
           0.0000     0.2062     0.3993     0.5473     0.6210,
           0.0000     0.2362     0.4561     0.6240     0.6934,
           0.0000     0.2681     0.5134     0.6993     0.7616,
           0.0000     0.3026     0.5726     0.7700     0.8248,
           0.0000     0.3415     0.6343     0.8339     0.8805,
           0.0000     0.3865     0.6994     0.8900     0.9269,
           0.0000     0.4408     0.7678     0.9362     0.9620,
           0.0000     0.5095     0.8381     0.9700     0.9847 };
@  ENC IS THE INDEX NU SUB C  @
let enc[16,5] = {
           1.9078     1.9078     1.9078     1.9078     1.9078,
           1.9140     1.9150     1.9160     1.9185     1.9210,
           1.9210     1.9220     1.9275     1.9360     1.9470,
           1.9270     1.9305     1.9425     1.9610     1.9870,
           1.9330     1.9405     1.9620     1.9970     2.0430,
           1.9390     1.9520     1.9885     2.0450     2.1160,
           1.9460     1.9665     2.0220     2.1065     2.2110,
           1.9550     1.9845     2.0670     2.1880     2.3330,
           1.9650     2.0075     2.1255     2.2945     2.4910,
           1.9800     2.0405     2.2050     2.4345     2.6965,
           2.0000     2.0850     2.3115     2.6240     2.9735,
           2.0400     2.1490     2.4610     2.8865     3.3565,
           2.0980     2.2445     2.6765     3.2650     3.9125,
           2.1890     2.3920     3.0040     3.8440     4.7755,
           2.3370     2.6355     3.5425     4.8085     6.2465,
           2.5880     3.0735     4.5340     6.6365     9.1440 };
@  ZA IS THE INDEX NU SUB ZETA.
   DELTA IS ESTIMATED FROM ZETA, SO NU SUB DELTA IS NOT USED. @
let za[16,5] = {
           0.0000     0.0000     0.0000     0.0000     0.0000,
           0.0000    -0.0166    -0.0322    -0.0488    -0.0644,
           0.0000    -0.0302    -0.0615    -0.0917    -0.1229,
           0.0000    -0.0434    -0.0878    -0.1321    -0.1785,
           0.0000    -0.0556    -0.1113    -0.1699    -0.2315,
           0.0000    -0.0660    -0.1340    -0.2060    -0.2830,
           0.0000    -0.0751    -0.1542    -0.2413    -0.3354,
           0.0000    -0.0837    -0.1733    -0.2760    -0.3896,
           0.0000    -0.0904    -0.1919    -0.3103    -0.4467,
           0.0000    -0.0955    -0.2080    -0.3465    -0.5080,
           0.0000    -0.0980    -0.2230    -0.3830    -0.5760,
           0.0000    -0.0986    -0.2372    -0.4239    -0.6525,
           0.0000    -0.0956    -0.2502    -0.4688    -0.7424,
           0.0000    -0.0894    -0.2617    -0.5201    -0.8534,
           0.0000    -0.0779    -0.2718    -0.5807    -0.9966,
           0.0000    -0.0610    -0.2790    -0.6590    -1.1980 };
@ The above are used in transposed form ([16,5]) @
ena = ena';
enb = enb';
enc = enc';
za = za';

@  CN, AN, AND BN ARE THE SAMPLE VALUES OF THE INDICES NU SUB C,
   ALPHA, AND BETA, RESPECTIVELY.      @
xx = sortc(x,1);
q05 = frctl(xx,.05);
q25 = frctl(xx,.25);
q50 = frctl(xx,.50);
q75 = frctl(xx,.75);
q95 = frctl(xx,.95);
d = q95 - q05;
cn = q75 - q25;
if cn == 0;
  print "Warning -- interquartile range is 0";
  print "Press any key to continue"; wait;
  retp(-1,-1,-1,-1,-1);
endif;
an = d/cn;
if an < 2.4388;
  retp(2, 0, cn/1.9078, q50, q50);
endif;
if symm == 1; goto sstab; endif;

bn = (q05 + q95 - 2 * q50)/d;
@ The sign of beta is saved and restored at the end. @
sign = 1.0;
if bn < 0; sign = -1; endif;
if bn == 0; sign = 0; endif;
bn = abs(bn);
@   SUBSCRIPT J (OR JJ) RUNS FROM 1 TO 5 AS BETA RUNS FROM 0.0,
    0.25, ... 1.0.
    SUBSCRIPT I RUNS FROM 1 TO 16 AS ALPHA RUNS FROM 2.0, 1.9,
    ... 0.5.           @
ah = zeros(5,1);
j = 1; do until j > 5;
i = 2; do until i == 16;
if an <= ena[j,i]; break; endif;
i = i + 1; endo;
t = (an - ena[j,i-1])/(ena[j,i]-ena[j,i-1]);
ah[j] = 2 - (i-2+t)*.1;
j = j + 1; endo;

bh = zeros(16,1);
i = 2; do until i > 16;
j = 2; do until j == 5;
if bn <= enb[j,i]; break; endif;
j = j + 1; endo;
t = (bn - enb[j-1,i])/(enb[j,i] - enb[j-1,i]);
bh[i] = (j - 2 + t) * .25;
i = i + 1; endo;
bh[1] = 2 * bh[2] - bh[3];

j = 2; do until j > 5;
jj = j;
i = trunc((2 - ah[j]) * 10 + 2);
i = maxc(i|2);
i = minc(i|16);
aa = 2 - .1*(i-2);
s2 = -(ah[j] - aa) * 10;
b = (1 - s2) * bh[i-1] + s2 * bh[i];
if b < (j-1)/4; break; endif;
j = j + 1; endo;
j = jj;

bb = .25 * (j-2);
t1 = (bh[i-1] - bb) / .25;
t2 = (bh[i] - bb) / .25;
s1 = - (ah[j-1] - aa) * 10;
dt = t2 - t1;
ds = s2 - s1;
s = (s1 + t1 * ds) / (1 - ds * dt);
t = t1 + s * dt;
alpha = aa - s * .1;
if alpha < .5; alpha = .5; endif;
beta = bb + t * .25;
if beta > 1;
  beta = 1;
endif;
beta = beta * sign;
c = enc[j-1,i-1] * (1-s) * (1-t) + enc[j-1,i] * s * (1-t)
    + enc[j,i-1] * t * (1-s) + enc[j,i] * t * s;
c = cn/c;
zeta = za[j-1,i-1] * (1-s) * (1-t) + za[j-1,i] * s * (1-t)
       + za[j,i-1] * t * (1-s) + za[j,i] * t * s;
zeta = q50 + c * sign * zeta;
if alpha == 1;
  delta = zeta;
else;
  delta = zeta - beta * c * tan(pi*alpha / 2);
endif;
retp(alpha, beta, c, delta, zeta);

sstab:
i = 2; do until i == 16;
ii = i;
if an <= ena[1,i]; break; endif;
i = i + 1; endo;
i = ii;
t = (an - ena[1, i-1]) / (ena[1, i] - ena[1, i-1]);
alpha = (22 - i - t) / 10;
if alpha < .5; alpha = .5; endif;
cnu = enc[1, i-1] * (1-t) + enc[1,i] * t;
c = cn/cnu;
retp(alpha, 0, c, q50, q50);
endp;

proc frctl(x,p);
@ This proc obtains selected quantiles off the ordered vector x @
local zi, i, th;
zi = p*rows(x) + .5;
i = trunc(zi);
i = maxc(i|1);
i = minc(i|rows(x)-1);
th = zi - i;
retp((1-th)*x[i] + th * x[i+1]);
endp;

proc(4) = myols(y,x,const,out);
@ I had trouble getting proc OLS to work, and
  so wrote this. @
@ returns beta, se, s2, dw @
@ const = 1 for constant @;
@ out = 1 for printed output @;
local xx, n, k, xpxi, beta, yhat, e, ee, s2, se, dw, t, de;
n = rows(y);
if rows(x) /= n;
  print "rows(y) /= rows(x) " n rows(x);
  stop;
  endif;
xx = x;
if const;
  xx = ones(n,1)~x;
  endif;
k = cols(xx);
xpxi = inv(xx'*xx);
beta = xpxi * xx' * y;
yhat = xx * beta;
e = y - yhat;
ee = e'*e;
s2 = ee / (n-k);
se = sqrt(s2 * diag(xpxi));
de = e[1:n-1] - e[2:n];
dw = de'de / ee;
if out;
  format /rdn 5,0;
  print "  n " n;
  print "  k " k;
  format /rdn 10,4;
  t = beta./se;
  print "      j      beta[j]      se        t";
  print seqa(1,1,k)~beta~se~t;
  print;
  print "  dw " dw;
  format /rdn 5,0;
  lprint "  n " n;
  lprint "  k " k;
  format /rdn 10,4;
  lprint;
  lprint "      j      beta[j]      se        t";
  lprint seqa(1,1,k)~beta~se~t;
  lprint;
  lprint "  dw " dw;
  lprint;
endif;
retp(beta, se, s2, dw);
endp;

/* End of file smstrg */

