/* stats.c

   written by Don Robert Maszle
   24 August 1992
   
   Copyright (c) 1993.  Don Maszle, Frederic Bois.  All rights reserved.

   -- Revisions -----
     Logfile:  SCCS/s.stats.c
    Revision:  1.1
        Date:  7/14/93
     Modtime:  19:14:57
      Author:  @a
   -- SCCS  ---------

   Statistical routines.

   In the header file, the macro IS_MISSING(x) can be redefined for
   the particular convention in use for representing missing data.
   This is set up as a macro, instead of merely a value, to accomodate
   conventions such as NaN which cannot be used in comparisons.
*/


#include <stdio.h>
#include <math.h>
#include "stats.h"

    /*-- Some statics are used for the ever so slight speedup of
         using absolute memory addresssing */

static double dSum;
static double dTmp;
static double xbar;
static double ybar;

    /*-- Sort of useful diagnostics */

#define  StatError(szMsg) (fprintf (stderr, "* Error: %s\n", szMsg))


    /*-- Statistical utility routines --------------------*/

double Mean (register int n, register double *x)
{
  register i;
  register cMissing = 0;		/* Count of missing data */

  if (n <= 0) {
    StatError ("Mean():  Non-positive n");
    return 0.0;
  }  /* if */
  
  dSum = 0.0;
  for (i = n - 1; i >= 0; i--)
    if (IS_MISSING(x[i]))
      cMissing++;
    else
      dSum += x[i];

  n -= cMissing;
  if (n > 0)
    return dSum/n;
  else
    StatError ("Mean(): Too many missing values");
  return 0.0;
}  /* Mean */


double Var (register n, register double *x, double _xbar)
{
  register int i;
  register cMissing = 0;		/* Count of missing data */
  
  xbar = _xbar;
  if (n <= 1) {
    StatError ("Var():  n <= 1");
    return 0.0;
  }  /* if */
  
  dSum = 0.0;
  for (i = n - 1; i >= 0; i--) {
    if (IS_MISSING(x[i]))
      cMissing++;
    else{
      dTmp = x[i] - xbar;
      dSum += dTmp*dTmp;
    }  /* else */
  }  /* for */

  n -= cMissing;
  if (n > 1)
    return dSum/(n - 1);
  else
    StatError ("Var(): Too many missing values");
  return 0.0;
}  /* Var */


double Cov (register int n, register double *x, register double *y,
	    double _xbar, double _ybar)
{
  register int i;
  register cMissing = 0;		/* Count of missing data */
  
  xbar = _xbar;		/* Speed up calc with absolute memory addrs */
  ybar = _ybar;

  if (n <= 1) {
    StatError ("Cov():  n <= 1");
    return 0.0;
  }  /* if */
  
  dSum = 0.0;
  for (i = n - 1; i >= 0; i--)
    if (IS_MISSING(x[i]) || IS_MISSING(y[i]))
      cMissing++;
    else
      dSum += (x[i] - xbar)*(y[i] - ybar); /* maybe optimize? */

  n -= cMissing;
  if (n > 1)
    return dSum/(n - 1);
  else
    StatError ("Cov(): Too many missing values");
  return 0.0;
}  /* Cov */

     
void ElemSqr (register int n, register double *x, register double *x2)
{
  register i;

  if (n <= 0){
    StatError ("ElemSqr():  Non-positive n");
    return;
  }  /* if */
  
  for (i = n - 1; i >= 0; i--)
    x2[i] = x[i]*x[i];
}  /* ElemSqr */


#define ROUND(x) (floor((x) + 0.5))


/* SigDigitRound

   returns x rounded to n significant digits.

   Does this by the following:

   Find m, the exponent of x in scientific notation, which is the
   number of places to shift the decimal of x leftward to get x in the
   form i.dddd.

   Now shift the decimal leftward by (m + n - 1) to get n digits to
   the left of the decimal.

   Round, and shift the decimal back.

   It's that dumb and simple.
*/

double SigDigitRound (double x, int n)
{
  double m = floor(log10(x));
  double scale = 1.0;
  int i, cShift;

  cShift = (int) m - n + 1;		/*-- Number of places to shift */
  if (cShift > 0)
    for (i = cShift; i > 0; i--)	/*-- m is postive, scale down */
      scale /= 10.;
  else
    for (i = cShift; i < 0; i++)	/*-- m is negative, scale up*/
      scale *= 10.;

  return ROUND(scale*x)/scale;
}  /* SigDigitRound */  
