/******************************************************************************
*
*  NSSDC/CDF                                            Calculate statistics.
*
*  Version 2.6a, 18-Nov-97, Hughes STX.
*
*  Modification history:
*
*   V1.0  29-Aug-91, J Love     Original version (for CDF V2.1).
*   V2.0  16-Mar-92, J Love     IBM PC port.  Added fill value filtering.
*   V2.1  21-Aug-92, J Love     CDF V2.3 (shareable/NeXT/zVar).
*   V2.2  10-Dec-93, J Love     CDF V2.4.
*   V2.3   6-Dec-94, J Love     CDF V2.5.
*   V2.4  15-May-95, J Love	Moved `ASSIGNx', `EQx', etc. to `toolbox3.c'
*				(and some calling sequences changed).
*   V2.5   1-Sep-95, J Love	Hyper groups.
*   V2.5a 19-Sep-95, J Love	CHECKforABORTso.
*   V2.5b 29-Sep-95, J Love	Less CHECKforABORTso.
*   V2.6  22-Jul-96, J Love	CDF V2.6.
*   V2.6a 18-Nov-97, J Love	Windows NT/Visual C++.
*
******************************************************************************/

#include "cdfstats.h"

/******************************************************************************
* CALCstat.  Reads each record of a variable and calculates the statistics.
******************************************************************************/

Logical CALCstat (Var)
struct VarStruct *Var;
{
  CDFstatus status;
  long attrN;
  long varyCount;
  int dimN;
  long dimSizes[CDF_MAX_DIMS];
  size_t nValueBytes[1];
  Byte **handles[1];
  long nHypers, hyperN;
  long nValues, valueN;
  struct GroupStruct groups;
  struct HyperStruct hyper;
  /****************************************************************************
  * Initialize for this variable.
  ****************************************************************************/
  Var->low = 0;
  Var->high = 0;
  Var->fills = 0;
  Var->oneINrange = FALSE;
  Var->minmaxInited = FALSE;
  Var->monoInited = FALSE;
  varyCount = BOO(Var->recVary,1,0);
  for (dimN = 0; dimN < Var->numDims; dimN++) {
     if (Var->dimVarys[dimN]) {
       dimSizes[dimN] = Var->dimSizes[dimN];
       varyCount++;
     }
     else
       dimSizes[dimN] = 1;
  }
  if (varyCount == 1)
    Var->checkMonotonicVar = TRUE;
  else
    Var->checkMonotonicVar = FALSE;
  Var->nValueBytes = Var->numElemsV * CDFelemSize(Var->dataTypeV);
  /****************************************************************************
  * Allocate memory.
  ****************************************************************************/
  handles[0] = &(Var->buffer);
  nValueBytes[0] = (size_t) Var->nValueBytes;
  AllocateBuffers (Var->varMaxRec + 1, Var->numDims, dimSizes, &groups, 0, 1,
		   handles, nValueBytes, ROWmajor(majority), MINnHYPERS,
		   FatalError);
  Var->min = cdf_AllocateMemory ((size_t) Var->nValueBytes,
			     FatalError);
  Var->max = cdf_AllocateMemory ((size_t) Var->nValueBytes,
			     FatalError);
  Var->last = cdf_AllocateMemory ((size_t) Var->nValueBytes,
			      FatalError);
  if (Var->rangeCheckVar) {
    Var->minINrange = cdf_AllocateMemory ((size_t) Var->nValueBytes,
				      FatalError);
    Var->maxINrange = cdf_AllocateMemory ((size_t) Var->nValueBytes,
				      FatalError);
  }
  /****************************************************************************
  * Read each value...
  ****************************************************************************/
  status = CDFlib (SELECT_, VAR(Var->Z), Var->varN,
		   NULL_);
  if (!StatusHandlerStats(status)) return FALSE;
  InitHyperParms (&hyper, &groups, Var->numDims, &nHypers, &nValues);
  for (hyperN = 0; hyperN < nHypers; hyperN++) {
     status = CDFlib (SELECT_, BOO(Var->Z,zVAR_RECNUMBER_,
					  rVARs_RECNUMBER_), hyper.recNumber,
			       BOO(Var->Z,zVAR_RECCOUNT_,
					  rVARs_RECCOUNT_), hyper.recCount,
			       BOO(Var->Z,
				   zVAR_RECINTERVAL_,
				   rVARs_RECINTERVAL_), hyper.recInterval,
			       BOO(Var->Z,zVAR_DIMINDICES_,
					  rVARs_DIMINDICES_), hyper.dimIndices,
			       BOO(Var->Z,zVAR_DIMCOUNTS_,
					  rVARs_DIMCOUNTS_), hyper.dimCounts,
			       BOO(Var->Z,
				   zVAR_DIMINTERVALS_,
				   rVARs_DIMINTERVALS_), hyper.dimIntervals,
		      GET_, BOO(Var->Z,zVAR_HYPERDATA_,
				       rVAR_HYPERDATA_), Var->buffer,
		      NULL_);
     if (!StatusHandlerStats(status)) return FALSE;
     for (valueN = 0, Var->value = Var->buffer; valueN < nValues;
	  valueN++, Var->value += (size_t) Var->nValueBytes) {
	if (Var->minmaxInited)
	  MinMaxCheck (Var);
	else
	  MinMaxInit (Var);
	if (Var->checkMonotonicVar) Monotonic (Var);
     }
     IncrHyperParms (&hyper, &groups, Var->numDims, ROWmajor(majority),
		     &nValues);
     CHECKforABORTso
  }
  /****************************************************************************
  * Display statistics.
  ****************************************************************************/
  DISPstat (Var);
  /****************************************************************************
  * Update attribute(s) if requested.  (NOTE: macros/functions could be used).
  ****************************************************************************/
  if (updateValids && Var->minmaxInited) {
    status = CDFlib (SELECT_, CDF_READONLY_MODE_, READONLYoff,
		     NULL_);
    if (!StatusHandlerStats(status)) return FALSE;
    status = CDFlib (GET_, ATTR_NUMBER_, "VALIDMIN", &attrN,
		     NULL_);
    if (status == NO_SUCH_ATTR) {
      status = CDFlib (CREATE_, ATTR_, "VALIDMIN", VARIABLE_SCOPE, &attrN,
		       NULL_);
      if (!StatusHandlerStats(status)) return FALSE;
    }
    else
      if (!StatusHandlerStats(status)) return FALSE;
    status = CDFlib (SELECT_, ATTR_, attrN,
			      BOO(Var->Z,zENTRY_,rENTRY_), Var->varN,
		     PUT_, BOO(Var->Z,zENTRY_DATA_,
				      rENTRY_DATA_), Var->dataTypeV,
						     Var->numElemsV, Var->min,
		     NULL_);
    if (!StatusHandlerStats(status)) return FALSE;
    status = CDFlib (GET_, ATTR_NUMBER_, "VALIDMAX", &attrN,
		     NULL_);
    if (status == NO_SUCH_ATTR) {
      status = CDFlib (CREATE_, ATTR_, "VALIDMAX", VARIABLE_SCOPE, &attrN,
		       NULL_);
      if (!StatusHandlerStats(status)) return FALSE;
    }
    else
      if (!StatusHandlerStats(status)) return FALSE;
    status = CDFlib (SELECT_, ATTR_, attrN,
			      BOO(Var->Z,zENTRY_,rENTRY_), Var->varN,
		     PUT_, BOO(Var->Z,zENTRY_DATA_,
				      rENTRY_DATA_), Var->dataTypeV,
						     Var->numElemsV, Var->max,
		     NULL_);
    if (!StatusHandlerStats(status)) return FALSE;
  }
  if (updateScales && Var->minmaxInited) {
    status = CDFlib (SELECT_, CDF_READONLY_MODE_, READONLYoff,
		     NULL_);
    if (!StatusHandlerStats(status)) return FALSE;
    status = CDFlib (GET_, ATTR_NUMBER_, "SCALEMIN", &attrN,
		     NULL_);
    if (status == NO_SUCH_ATTR) {
      status = CDFlib (CREATE_, ATTR_, "SCALEMIN", VARIABLE_SCOPE, &attrN,
		       NULL_);
      if (!StatusHandlerStats(status)) return FALSE;
    }
    else
      if (!StatusHandlerStats(status)) return FALSE;
    status = CDFlib (SELECT_, ATTR_, attrN,
			      BOO(Var->Z,zENTRY_,rENTRY_), Var->varN,
		     PUT_, BOO(Var->Z,zENTRY_DATA_,
				      rENTRY_DATA_), Var->dataTypeV,
						     Var->numElemsV, Var->min,
		     NULL_);
    if (!StatusHandlerStats(status)) return FALSE;
    status = CDFlib (GET_, ATTR_NUMBER_, "SCALEMAX", &attrN,
		     NULL_);
    if (status == NO_SUCH_ATTR) {
      status = CDFlib (CREATE_, ATTR_, "SCALEMAX", VARIABLE_SCOPE, &attrN,
		       NULL_);
      if (!StatusHandlerStats(status)) return FALSE;
    }
    else
      if (!StatusHandlerStats(status)) return FALSE;
    status = CDFlib (SELECT_, ATTR_, attrN,
			      BOO(Var->Z,zENTRY_,rENTRY_), Var->varN,
		     PUT_, BOO(Var->Z,zENTRY_DATA_,
				      rENTRY_DATA_), Var->dataTypeV,
						     Var->numElemsV, Var->max,
		     NULL_);
    if (!StatusHandlerStats(status)) return FALSE;
  }
  if (updateMonotonic && Var->monoInited) {
    char mono[MONOTON_RESULT_LEN+1];

    status = CDFlib (SELECT_, CDF_READONLY_MODE_, READONLYoff,
		     NULL_);
    if (!StatusHandlerStats(status)) return FALSE;
    status = CDFlib (GET_, ATTR_NUMBER_, "MONOTON", &attrN,
		     NULL_);
    if (status == NO_SUCH_ATTR) {
      status = CDFlib (CREATE_, ATTR_, "MONOTON", VARIABLE_SCOPE, &attrN,
		       NULL_);
      if (!StatusHandlerStats(status)) return FALSE;
    }
    else
      if (!StatusHandlerStats(status)) return FALSE;
    switch (Var->monoState) {
      case _Increase:
	strcpyX (mono, "INCREASE", MONOTON_RESULT_LEN);
	break;
      case _Decrease:
	strcpyX (mono, "DECREASE", MONOTON_RESULT_LEN);
	break;
      default:
	strcpyX (mono, "FALSE   ", MONOTON_RESULT_LEN);
    }
    status = CDFlib (SELECT_, ATTR_, attrN,
			      BOO(Var->Z,zENTRY_,rENTRY_), Var->varN,
		     PUT_, BOO(Var->Z,zENTRY_DATA_,
				      rENTRY_DATA_), CDF_CHAR, 8L, mono,
		     NULL_);
    if (!StatusHandlerStats(status)) return FALSE;
  }
  /****************************************************************************
  * Return - note that memory allocated will be freed by the caller (or the
  * caller's caller).
  ****************************************************************************/
  return TRUE;
}

/******************************************************************************
* MinMaxInit.
******************************************************************************/

void MinMaxInit (Var)
struct VarStruct *Var;
{
  Logical use;
  if (Var->ignoreFillsVar)
    if (NEx(Var->value,Var->fillval,Var->dataTypeV,Var->numElemsV))
      use = TRUE;
    else {
      use = FALSE;
      Var->fills++;
    }
  else
    use = TRUE;
  if (use) {
    ASSIGNx (Var->min, Var->value, Var->dataTypeV, Var->numElemsV);
    ASSIGNx (Var->max, Var->value, Var->dataTypeV, Var->numElemsV);
    if (Var->rangeCheckVar) {
      if (LTx(Var->value,Var->validmin,Var->dataTypeV,Var->numElemsV))
        Var->low++;
      else
        if (GTx(Var->value,Var->validmax,Var->dataTypeV,Var->numElemsV))
	  Var->high++;
        else {
	  Var->oneINrange = TRUE;
	  ASSIGNx (Var->minINrange, Var->value, Var->dataTypeV,
		   Var->numElemsV);
	  ASSIGNx (Var->maxINrange, Var->value, Var->dataTypeV,
		   Var->numElemsV);
        }
    }
    Var->minmaxInited = TRUE;
  }
  return;
}

void MinMaxCheck (Var)
struct VarStruct *Var;
{
  Logical use;
  if (Var->ignoreFillsVar)
    if (NEx(Var->value,Var->fillval,Var->dataTypeV,Var->numElemsV))
      use = TRUE;
    else {
      use = FALSE;
      Var->fills++;
    }
  else
    use = TRUE;
  if (use) {
    if (LTx(Var->value,Var->min,Var->dataTypeV,Var->numElemsV))
      ASSIGNx (Var->min, Var->value, Var->dataTypeV, Var->numElemsV);
    else {
      if (GTx(Var->value,Var->max,Var->dataTypeV,Var->numElemsV)) {
        ASSIGNx (Var->max, Var->value, Var->dataTypeV, Var->numElemsV);
      }
    }
    if (Var->rangeCheckVar) {
      if (LTx(Var->value,Var->validmin,Var->dataTypeV,Var->numElemsV))
        Var->low++;
      else
        if (GTx(Var->value,Var->validmax,Var->dataTypeV,Var->numElemsV))
	  Var->high++;
        else
	  if (Var->oneINrange) {
	    if (LTx(Var->value,Var->minINrange,Var->dataTypeV,Var->numElemsV))
	      ASSIGNx (Var->minINrange, Var->value, Var->dataTypeV,
		       Var->numElemsV);
	    else
	      if (GTx(Var->value,Var->maxINrange,
		      Var->dataTypeV,Var->numElemsV)) {
	        ASSIGNx (Var->maxINrange, Var->value, Var->dataTypeV,
		         Var->numElemsV);
	      }
	  }
	  else {
	    Var->oneINrange = TRUE;
	    ASSIGNx (Var->minINrange, Var->value, Var->dataTypeV,
		     Var->numElemsV);
	    ASSIGNx (Var->maxINrange, Var->value, Var->dataTypeV,
		     Var->numElemsV);
	  }
    }
  }
  return;
}

/******************************************************************************
* Monotonic.
******************************************************************************/

void Monotonic (Var)
struct VarStruct *Var;
{
  Logical use;
  if (Var->ignoreFillsVar)
    if (NEx(Var->value,Var->fillval,Var->dataTypeV,Var->numElemsV))
      use = TRUE;
    else
      use = FALSE;
  else
    use = TRUE;
  if (use) {
    if (!Var->monoInited) {
      ASSIGNx (Var->last, Var->value, Var->dataTypeV, Var->numElemsV);
      Var->monoState = _Init;
      Var->monoInited = TRUE;
    }
    else {
      switch (Var->monoState) {
        case _Init:
	  if (EQx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _Steady;
	    break;
	  }
	  if (LTx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _Decrease;
	    break;
	  }
	  if (GTx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _Increase;
	    break;
	  }
	  break;
        case _Steady:
	  if (LTx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _noIncrease;
	    break;
	  }
	  if (GTx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _noDecrease;
	    break;
	  }
	  break;
        case _Increase:
	  if (LTx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _False;
	    break;
	  }
	  if (EQx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _noDecrease;
	    break;
	  }
	  break;
        case _Decrease:
	  if (GTx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _False;
	    break;
	  }
	  if (EQx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _noIncrease;
	    break;
	  }
	  break;
        case _noIncrease:
	  if (GTx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _False;
	    break;
	  }
	  break;
        case _noDecrease:
	  if (LTx(Var->value,Var->last,Var->dataTypeV,Var->numElemsV)) {
	    Var->monoState = _False;
	    break;
	  }
	case _False:
	  /* Do nothing? */
	  break;
      }
      ASSIGNx (Var->last, Var->value, Var->dataTypeV, Var->numElemsV);
    }
  }
  return;
}
