/*++

Copyright (C) 1996-1999 Microsoft Corporation

Module Name:

    statfuns.c

Abstract:

    Statistical calculation functions

--*/

#include <windows.h>
#include <stdlib.h>
#include <math.h>
#include <pdh.h> 
#include "pdhicalc.h"
#include "pdhitype.h"
#include "pdhmsg.h"


#define PDHI_FMT_FILTER     (PDH_FMT_LONG | PDH_FMT_DOUBLE | PDH_FMT_LARGE)

PDH_STATUS
APIENTRY
PdhiComputeFirstLastStats (
    IN      PPDHI_COUNTER       pCounter,
    IN      DWORD               dwFormat,
    IN      DWORD               dwFirstEntry,
    IN      DWORD               dwNumEntries,
    IN      PPDH_RAW_COUNTER    lpRawValueArray,
    IN      PPDH_STATISTICS     data
)
{
    DOUBLE  dThisValue = (double)0.0;
    DOUBLE  dMin = (double)+10E8;    // these are just "big" seed numbers
    DOUBLE  dMax = (double)-10E8;    
    DOUBLE  dMean = (double)0.0;
    BOOLEAN bFirstItem = TRUE;

    DWORD   dwItem;
    DWORD   dwValidItemCount = 0;
    DWORD   dwFirstValidItem = 0;
    DWORD   dwLastValidItem = 0;
    DWORD   dwComputeFormat;

    PPDH_RAW_COUNTER pNewCounter;
    PPDH_RAW_COUNTER pOldCounter;
    
    PDH_FMT_COUNTERVALUE    fmtValue;

    DWORD   cStatusReturn;

    __try {
        // initialize th user's data buffer
        data->dwFormat = 0;
        data->count = 0;
        data->min.CStatus = PDH_CSTATUS_INVALID_DATA;
        data->min.largeValue = 0;
        data->max.CStatus = PDH_CSTATUS_INVALID_DATA;
        data->max.largeValue = 0;
        data->mean.CStatus = PDH_CSTATUS_INVALID_DATA;
        data->mean.largeValue = 0;

        // find first valid counter in array
        dwItem = dwFirstEntry;
        pNewCounter = NULL;
        pOldCounter = &lpRawValueArray[dwItem];
        do {
            // get value of this instance if next counter is valid
            if ((pOldCounter->CStatus == PDH_CSTATUS_VALID_DATA) ||
                (pOldCounter->CStatus == PDH_CSTATUS_NEW_DATA)) {
                pNewCounter = pOldCounter;
                dwFirstValidItem = dwItem;
                break;
            } else {
                dwItem = ++dwItem % dwNumEntries;
                pOldCounter = &lpRawValueArray[dwItem];
            }
        } while (dwItem != dwFirstEntry);
        
        // do calculations in Floating point format
        dwComputeFormat = dwFormat;
        dwComputeFormat &= ~PDHI_FMT_FILTER;
        dwComputeFormat |= PDH_FMT_DOUBLE | PDH_FMT_NOCAP100;

        // go to next entry to begin processing
        dwItem = ++dwItem % dwNumEntries;
        pNewCounter = &lpRawValueArray[dwItem];

        // these counters need 2 or more entrys to compute values from
        if ((dwItem != dwFirstEntry) && (dwNumEntries > 1)) {
            // start record found so initialize and continue
            dwLastValidItem = dwItem;

            // step through the remaining entries

            while (dwItem != dwFirstEntry) {
                // get value of this instance if next counter is valid
                if ((pNewCounter->CStatus == PDH_CSTATUS_VALID_DATA) ||
                    (pNewCounter->CStatus == PDH_CSTATUS_NEW_DATA)) {

                    // record this as a valid counter
                    dwLastValidItem = dwItem;

                    // get current value
                    cStatusReturn = PdhiComputeFormattedValue (
                            pCounter->CalcFunc,
                            pCounter->plCounterInfo.dwCounterType,
                            pCounter->lScale,
                            dwComputeFormat,
                            pNewCounter,
                            pOldCounter,
                            & pCounter->TimeBase,
                            0L,
                            & fmtValue);
                    if (cStatusReturn == ERROR_SUCCESS) {
                        dThisValue = fmtValue.doubleValue;

                        // update min & max
                        if (bFirstItem) {
                            dMax = dMin = dThisValue;
                            bFirstItem = FALSE;
                        }
                        else {
                            if (dThisValue > dMax) dMax = dThisValue;
                            if (dThisValue < dMin) dMin = dThisValue;
                        }
                        dwValidItemCount++;
                    }
                }
                pOldCounter = pNewCounter;
                dwItem = ++dwItem % dwNumEntries;
                pNewCounter = &lpRawValueArray[dwItem];
            }
            // compute average
            if (dwValidItemCount > 0) {
                pOldCounter = &lpRawValueArray[dwFirstValidItem];
                pNewCounter = &lpRawValueArray[dwLastValidItem];

                cStatusReturn = PdhiComputeFormattedValue (
                    pCounter->CalcFunc,
                    pCounter->plCounterInfo.dwCounterType,
                    pCounter->lScale,
                    dwComputeFormat,
                    pNewCounter,
                    pOldCounter,
                    &pCounter->TimeBase,
                    0L,
                    &fmtValue);
                if (cStatusReturn == ERROR_SUCCESS) {
                    dMean = fmtValue.doubleValue;
                    cStatusReturn = PDH_CSTATUS_VALID_DATA;
                }
                else {
                    dMean = 0.0;
                }
            } else {
                dMean = 0.0;
                dMax = 0.0;
                dMin = 0.0;
                cStatusReturn = PDH_CSTATUS_INVALID_DATA;
            }
            
        } else {
            // array does not contain a valid counter so exit
            dMean = 0.0;
            dMax = 0.0;
            dMin = 0.0;
            cStatusReturn = PDH_CSTATUS_INVALID_DATA;
        }

        // update user's buffer with new data
        data->dwFormat = dwFormat;
        data->count = dwValidItemCount;
        data->min.CStatus = cStatusReturn;
        data->max.CStatus = cStatusReturn;
        data->mean.CStatus = cStatusReturn;
        switch ((dwFormat & PDHI_FMT_FILTER)) {
            case PDH_FMT_LONG:
                if (dMin > (DOUBLE) MAXLONG) {
                    data->min.longValue = MAXLONG;
                    data->min.CStatus   = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->min.longValue = (long) dMin;
                }
                if (dMax > (DOUBLE) MAXLONG) {
                    data->max.longValue = MAXLONG;
                    data->max.CStatus   = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->max.longValue = (long) dMax;
                }
                if (dMean > (DOUBLE) MAXLONG) {
                    data->mean.longValue = MAXLONG;
                    data->mean.CStatus   = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->mean.longValue = (long) dMean;
                }
                break;

            case PDH_FMT_DOUBLE:
                data->min.doubleValue = dMin;
                data->max.doubleValue = dMax;
                data->mean.doubleValue = dMean;
                break;

            case PDH_FMT_LARGE:
            default:
                if (dMin > (DOUBLE) MAXLONGLONG) {
                    data->min.largeValue = MAXLONGLONG;
                    data->min.CStatus    = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->min.largeValue = (LONGLONG) dMin;
                }
                if (dMax > (DOUBLE) MAXLONGLONG) {
                    data->max.largeValue = MAXLONGLONG;
                    data->max.CStatus    = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->max.largeValue = (LONGLONG) dMax;
                }
                if (dMean > (DOUBLE) MAXLONGLONG) {
                    data->mean.largeValue = MAXLONGLONG;
                    data->mean.CStatus    = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->mean.largeValue = (LONGLONG) dMean;
                }
                break;
        }

    } __except (EXCEPTION_EXECUTE_HANDLER) {
        return PDH_INVALID_ARGUMENT;
    } 
    return ERROR_SUCCESS;
}

PDH_STATUS
APIENTRY
PdhiComputeRawCountStats (
    IN      PPDHI_COUNTER       pCounter,
    IN      DWORD               dwFormat,
    IN      DWORD               dwFirstEntry,
    IN      DWORD               dwNumEntries,
    IN      PPDH_RAW_COUNTER    lpRawValueArray,
    IN      PPDH_STATISTICS     data
)
{
    DOUBLE  dThisValue = (double)0.0;
    DOUBLE  dMin = (double)+10E8;    // these are just "big" seed numbers
    DOUBLE  dMax = (double)-10E8;    
    DOUBLE  dMean = (double)0.0;
    BOOLEAN bFirstItem = TRUE;
    DOUBLE  dScale;

    DWORD   dwItem;
    DWORD   dwValidItemCount = 0;
    DWORD   dwFirstValidItem = 0;
    DWORD   dwLastValidItem = 0;
    DWORD   dwComputeFormat;

    PPDH_RAW_COUNTER pNewCounter;
    PPDH_RAW_COUNTER pOldCounter = NULL;
    
    PDH_FMT_COUNTERVALUE    fmtValue;

    DWORD   cStatusReturn;

    UNREFERENCED_PARAMETER (dwFirstEntry);
    __try {
        // initialize the user's data buffer
        data->dwFormat = 0;
        data->count = 0;
        data->min.CStatus = PDH_CSTATUS_INVALID_DATA;
        data->min.largeValue = 0;
        data->max.CStatus = PDH_CSTATUS_INVALID_DATA;
        data->max.largeValue = 0;
        data->mean.CStatus = PDH_CSTATUS_INVALID_DATA;
        data->mean.largeValue = 0;

        // find first valid counter in array
        dwItem = 0;
        pNewCounter = lpRawValueArray;
        while (dwItem < dwNumEntries) {
            // get value of this instance if next counter is valid
            if ((pNewCounter->CStatus == PDH_CSTATUS_VALID_DATA) ||
                (pNewCounter->CStatus == PDH_CSTATUS_NEW_DATA)) {
                break;
            } else {
                pOldCounter = pNewCounter;
                pNewCounter++;
                dwItem++;
            }
        }
        
        // do calculations in Floating point format
        dwComputeFormat = dwFormat;
        dwComputeFormat &= ~PDHI_FMT_FILTER;
        dwComputeFormat |= PDH_FMT_DOUBLE | PDH_FMT_NOCAP100;

        if ((dwItem != dwNumEntries) && (dwNumEntries > 0)) {
            // start record found so continue
            dwFirstValidItem = dwItem;

            // step through the remaining entries

            while (dwItem < dwNumEntries) {
                // get value of this instance if next counter is valid
                if ((pNewCounter->CStatus == PDH_CSTATUS_VALID_DATA) ||
                    (pNewCounter->CStatus == PDH_CSTATUS_NEW_DATA)) {
                    dwLastValidItem = dwItem;

                    cStatusReturn = PdhiComputeFormattedValue (
                            pCounter->CalcFunc,
                            pCounter->plCounterInfo.dwCounterType,
                            pCounter->lScale,
                            dwComputeFormat,
                            pNewCounter,
                            pOldCounter,
                            & pCounter->TimeBase,
                            0L,
                            & fmtValue);
                    if (cStatusReturn == ERROR_SUCCESS) {
                        dThisValue = fmtValue.doubleValue;

                        if (bFirstItem) {
                            dMin = dMax = dThisValue;
                            bFirstItem = FALSE;
                        }
                        else {
                            if (dThisValue > dMax) dMax = dThisValue;
                            if (dThisValue < dMin) dMin = dThisValue;
                        }
                        dMean += dThisValue;

                        dwValidItemCount ++;
                    }
                }
                pOldCounter = pNewCounter;
                pNewCounter++;
                dwItem++;
            }
            // compute average
            if (dwValidItemCount > 0) {
                dMean /= (double)dwValidItemCount;

                if (!(dwFormat & PDH_FMT_NOSCALE)) {
                    //now scale
                    dScale = pow (10.0, (double)pCounter->lScale);
                    dMean *= dScale;
                    dMin *= dScale;
                    dMax *= dScale;
                }
                cStatusReturn = PDH_CSTATUS_VALID_DATA;
            } else {
                dMean = 0.0;
                dMax = 0.0;
                dMin = 0.0;
                cStatusReturn = PDH_CSTATUS_INVALID_DATA;
            }
            
        } else {
            // array does not contain a valid counter so exit
            dMean = 0.0;
            dMax = 0.0;
            dMin = 0.0;
            cStatusReturn = PDH_CSTATUS_INVALID_DATA;
        }

        // update user's buffer with new data
        data->dwFormat = dwFormat;
        data->count = dwValidItemCount;
        data->min.CStatus = cStatusReturn;
        data->max.CStatus = cStatusReturn;
        data->mean.CStatus = cStatusReturn;
        switch ((dwFormat & PDHI_FMT_FILTER)) {
            case PDH_FMT_LONG:
                if (dMin > (DOUBLE) MAXLONG) {
                    data->min.longValue = MAXLONG;
                    data->min.CStatus   = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->min.longValue = (long) dMin;
                }
                if (dMax > (DOUBLE) MAXLONG) {
                    data->max.longValue = MAXLONG;
                    data->max.CStatus   = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->max.longValue = (long) dMax;
                }
                if (dMean > (DOUBLE) MAXLONG) {
                    data->mean.longValue = MAXLONG;
                    data->mean.CStatus   = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->mean.longValue = (long) dMean;
                }
                break;

            case PDH_FMT_DOUBLE:
                data->min.doubleValue = dMin;
                data->max.doubleValue = dMax;
                data->mean.doubleValue = dMean;
                break;

            case PDH_FMT_LARGE:
            default:
                if (dMin > (DOUBLE) MAXLONGLONG) {
                    data->min.largeValue = MAXLONGLONG;
                    data->min.CStatus    = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->min.largeValue = (LONGLONG) dMin;
                }
                if (dMax > (DOUBLE) MAXLONGLONG) {
                    data->max.largeValue = MAXLONGLONG;
                    data->max.CStatus    = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->max.largeValue = (LONGLONG) dMax;
                }
                if (dMean > (DOUBLE) MAXLONGLONG) {
                    data->mean.largeValue = MAXLONGLONG;
                    data->mean.CStatus    = PDH_CSTATUS_INVALID_DATA;
                }
                else {
                    data->mean.largeValue = (LONGLONG) dMean;
                }
                break;
        }

    } __except (EXCEPTION_EXECUTE_HANDLER) {
        return PDH_INVALID_ARGUMENT;
    } 
    return ERROR_SUCCESS;
}

PDH_STATUS
APIENTRY
PdhiComputeNoDataStats (
    IN      PPDHI_COUNTER       pCounter,
    IN      DWORD               dwFormat,
    IN      DWORD               dwFirstEntry,
    IN      DWORD               dwNumEntries,
    IN      PPDH_RAW_COUNTER    lpRawValueArray,
    IN      PPDH_STATISTICS     data
)
{
    UNREFERENCED_PARAMETER (pCounter);
    UNREFERENCED_PARAMETER (dwFirstEntry);
    UNREFERENCED_PARAMETER (dwNumEntries);
    UNREFERENCED_PARAMETER (lpRawValueArray);
    __try {
        data->dwFormat = dwFormat;
        data->count = 0;
        data->min.CStatus = PDH_CSTATUS_INVALID_DATA;
        data->max.CStatus = PDH_CSTATUS_INVALID_DATA;
        data->mean.CStatus = PDH_CSTATUS_INVALID_DATA;
        switch ((dwFormat & PDHI_FMT_FILTER)) {
            case PDH_FMT_LONG:
                data->min.doubleValue = 0;
                data->max.longValue = 0;
                data->mean.longValue = 0;
                break;

            case PDH_FMT_DOUBLE:
                data->min.doubleValue = (double)0;
                data->max.doubleValue = (double)0;
                data->mean.doubleValue = (double)0.0;
                break;

            case PDH_FMT_LARGE:
            default:
                data->min.largeValue = 0;
                data->max.largeValue = 0;
                data->mean.largeValue = 0;
                break;
        }
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        return PDH_INVALID_ARGUMENT;
    } 
    return ERROR_SUCCESS;
}