IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 10550


Ignore:
Timestamp:
Dec 8, 2006, 1:38:54 AM (19 years ago)
Author:
magnier
Message:
  • moved psHistogram functions to psHistogram.c,h (pslib_strict.h, MAkefile.am, psMemory.c)
  • changed psVectorStats return value to bool (psImageGeomManip.c, psImagePixelExtract.c, psImageStats.c, psMinPoly.c,
  • added FITTED_MEAN,STDEV_V2 (quadratic fit to log-peak)
  • added stats->results
  • set stats->results when stats are measured
  • return stats calculated even if not requested
  • moved vectorFittedStats call out of vectorRobustStats to psVectorStats
  • internal calls to private stats function XXX now tests stats->results for the corresponding flag, NOT if value of stats->XXX == NAN.
  • dropped unused function NonEmpty (commented out)
  • rolled up vectorSampleMedian with if tests in the loop
  • rolled up vectorSampleStdev with if tests in the loop
  • call psError on failure for any internal stats call (the user should call psErrorClear if they ignore errors from psVectorStats)
Location:
trunk/psLib
Files:
3 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/psLib/src/imageops/psImageBackground.c

    r10396 r10550  
    7373        if (!psVectorSort(values, values)) {
    7474            psError(PS_ERR_UNKNOWN, false, "Unable to sort values.\n");
    75             psFree(stats);
    7675            psFree(values);
    77             return NULL;
     76            return false;
    7877        }
    7978
     
    9190    } else {
    9291        // XXX leave this as a psphot user option (passed in as part of stats?)
    93         if (stats->options & PS_STAT_FITTED_MEAN) {
     92        if ((stats->options & PS_STAT_FITTED_MEAN) || (stats->options & PS_STAT_FITTED_MEAN_V2)) {
    9493            stats->clipSigma = 1.0;
    9594        }
    96         if (psVectorStats (stats, values, NULL, NULL, 0) == NULL) {
     95        if (!psVectorStats (stats, values, NULL, NULL, 0)) {
    9796            psError(PS_ERR_UNKNOWN, false, "Unable to measure statistics for image background "
    9897                    "(%dx%d, (row0,col0) = (%d,%d)",
    9998                    image->numRows, image->numCols, image->row0, image->col0);
    100             psFree(stats);
    10199            psFree(values);
    102             return NULL;
     100            return false;
    103101        }
    104102    }
    105103
    106104    psFree(values);
    107     return stats;
     105    return true;
    108106}
  • trunk/psLib/src/math/psStats.c

    r10475 r10550  
    77 *  on those data structures.
    88 *
    9  *  @author GLG, MHPCC
    10  *
    11  *  XXX: The following stats members are never used, or set in this code.
    12  *      stats->clippedNvalues
     9 *  @author GLG (MHPCC), EAM (IfA)
    1310 *
    1411 * XXX: Must do
     
    1613 * use ->min and ->max (PS_STAT_USE_RANGE)
    1714 *
    18  *  @version $Revision: 1.193 $ $Name: not supported by cvs2svn $
    19  *  @date $Date: 2006-12-05 20:05:57 $
     15 *  @version $Revision: 1.194 $ $Name: not supported by cvs2svn $
     16 *  @date $Date: 2006-12-08 11:38:54 $
    2017 *
    21  *  Copyright 2004 Maui High Performance Computing Center, University of Hawaii
     18 *  Copyright 2006 IfA, University of Hawaii
    2219 */
    2320
     
    3532#include "psMemory.h"
    3633#include "psAbort.h"
    37 #include "psImage.h"
     34//#include "psImage.h"
    3835#include "psVector.h"
    3936#include "psTrace.h"
    4037#include "psLogMsg.h"
    4138#include "psError.h"
     39#include "psHistogram.h"
    4240#include "psStats.h"
    4341#include "psMinimizeLMM.h"
     
    6462#define PS_BIN_MIDPOINT(HISTOGRAM, BIN_NUM) \
    6563(0.5 * (HISTOGRAM->bounds->data.F32[(BIN_NUM)] + HISTOGRAM->bounds->data.F32[(BIN_NUM)+1]))
     64
    6665/*****************************************************************************/
    6766/* TYPE DEFINITIONS                                                          */
     
    8786NOTE: it is assumed that any call to these statistical functions will have
    8887been preceded by a call to the psVectorStats() function.  Various sanity tests
    89 will only be performed in psVectorStats().  Should we perform the sanity
    90 checks in each routine anyway?
     88will only be performed in psVectorStats().
    9189 
    92 XXX: For many of these private stats routines, what should be done if there
    93 are no acceptable elements in the input vector (if no elements lie within
    94 range, or there are no unmasked elements, or the input vector is NULL)?
    95 Currently we set the value to NAN.
    96  
    97 XXX: Optimization: many routines have an "empty" boolean variable which keeps
    98 track of whether or not the vector has any valid elements.  This code can
    99 possibly be optimized away.
    100  *****************************************************************************/
    101 /******************************************************************************
    102 vectorSampleMean(myVector, maskVector, maskVal, stats): calculates the
    103 mean of the input vector.  If there was a problem with the mean calculation,
    104 this routine sets stats->sampleMean to NAN.
    105  *****************************************************************************/
    106 # if (0)
    107     static bool vectorSampleMean(const psVector* myVector,
    108                                  const psVector* errors,
    109                                  const psVector* maskVector,
    110                                  psMaskType maskVal,
    111                                  psStats* stats)
    112 {
    113     psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    114 
    115     psF32 mean = 0.0;                   // The mean
    116     long count = 0;                     // Number of points contributing to this mean
    117 
    118     psF32 *data = myVector->data.F32;   // Dereference
    119     int numData = myVector->n;          // Number of data points
    120 
    121     // If PS_STAT_USE_RANGE is requested, then we enter a slightly different loop.
    122     if (!errors) {
    123         if (stats->options & PS_STAT_USE_RANGE) {
    124             if (maskVector) {
    125                 // No errors, check range, check mask
    126                 psU8 *maskData = maskVector->data.U8;
    127                 for (long i = 0; i < numData; i++) {
    128                     // Check if the data is with the specified range
    129                     if (!(maskVal & maskData[i]) && stats->min <= data[i] && data[i] <= stats->max) {
    130                         mean += data[i];
    131                         count++;
    132                     }
    133                 }
    134             } else {
    135                 // No errors, check range, no mask
    136                 for (long i = 0; i < numData; i++) {
    137                     if (stats->min <= data[i] && data[i] <= stats->max) {
    138                         mean += data[i];
    139                         count++;
    140                     }
    141                 }
    142             }
    143         } else {
    144             if (maskVector) {
    145                 // No errors, no range check, check mask
    146                 psU8 *maskData = maskVector->data.U8;
    147                 for (long i = 0; i < numData; i++) {
    148                     if (!(maskVal & maskData[i])) {
    149                         mean += data[i];
    150                         count++;
    151                     }
    152                 }
    153 
    154             } else {
    155                 // No errors, no range check, no mask
    156                 for (long i = 0; i < numData; i++) {
    157                     mean += data[i];
    158                 }
    159                 count = numData;
    160             }
    161         }
    162 
    163         if (count > 0) {
    164             mean /= (psF32)count;
    165         } else {
    166             mean = NAN;
    167         }
    168 
    169     } else {
    170         psF32 sumWeights = 0.0;         // The sum of the weights
    171         psF32 *errorsData = errors->data.F32;
    172         if (stats->options & PS_STAT_USE_RANGE) {
    173             if (maskVector) {
    174                 psU8 *maskData = maskVector->data.U8;
    175                 for (long i = 0; i < numData; i++) {
    176                     // Check if the data is with the specified range
    177                     if (!(maskVal & maskData[i]) && stats->min <= data[i] && data[i] <= stats->max) {
    178                         float weight = 1.0 / PS_SQR(errorsData[i]);
    179                         mean += data[i] * weight;
    180                         sumWeights += weight;
    181                         count++;
    182                     }
    183                 }
    184             } else {
    185                 for (long i = 0; i < myVector->n; i++) {
    186                     if (stats->min <= data[i] && data[i] <= stats->max) {
    187                         float weight = 1.0 / PS_SQR(errorsData[i]);
    188                         mean += data[i] * weight;
    189                         sumWeights += weight;
    190                         count++;
    191                     }
    192                 }
    193             }
    194         } else {
    195             if (maskVector) {
    196                 psU8 *maskData = maskVector->data.U8;
    197                 for (long i = 0; i < myVector->n; i++) {
    198                     if (!(maskVal & maskData[i])) {
    199                         float weight = 1.0 / PS_SQR(errorsData[i]);
    200                         mean += data[i] * weight;
    201                         sumWeights += weight;
    202                         count++;
    203                     }
    204                 }
    205             } else {
    206                 for (long i = 0; i < myVector->n; i++) {
    207                     float weight = 1.0 / PS_SQR(errorsData[i]);
    208                     mean += data[i] * weight;
    209                     sumWeights += weight;
    210                 }
    211                 count = myVector->n;
    212             }
    213         }
    214 
    215         if (count > 0) {
    216             mean /= sumWeights;
    217         } else {
    218             mean = NAN;
    219         }
    220     }
    221 
    222     stats->sampleMean = mean;
    223     if (isnan(mean)) {
    224         psTrace("psLib.math", 4, "---- %s(false) end ----\n", __func__);
    225         return false;
    226     }
    227 
    228     psTrace("psLib.math", 4, "---- %s(true) end ----\n", __func__);
    229     return true;
    230 }
    231 #endif
     90For many of these private stats routines, it is possible that there are no acceptable elements
     91in the input vector (if no elements lie within range, or there are no unmasked elements, or the
     92input vector is NULL).  In such cases, we set the desired value to NAN and return an error.
     93The calling function may clear the error.
     94*****************************************************************************/
     95
     96// static prototypes:
     97static psF32 minimizeLMChi2Gauss1D(psVector *deriv, const psVector *params, const psVector *coords);
     98static psF32 fitQuadraticSearchForYThenReturnX(const psVector *xVec, psVector *yVec, psS32 binNum, psF32 yVal);
    23299
    233100/******************************************************************************
     
    236103this routine sets stats->sampleMean to NAN.
    237104 
    238 XXX : using the method below, with a single loop for various options
    239       costs only a small amount and is much easier to debug. running 10000 tests
    240       of 1000 point vectors for the two methods gives:
    241                      separate    single
    242 (mask: 0, range: 0): 0.067 sec   0.073 sec
    243 (mask: 1, range: 0): 0.098 sec  0.102 sec
    244 (mask: 0, range: 0): 0.136 sec  0.162 sec
    245 (mask: 1, range: 0): 0.177 sec  0.181 sec
    246  *****************************************************************************/
     105using the method below, with a single loop for various options costs only a small amount and is
     106much easier to debug. running 10000 tests of 1000 point vectors for the two methods gives:
     107 
     108                     separate    single          w/errors
     109(mask: 0, range: 0): 0.067 sec  0.073 sec (10%)  0.072 sec
     110(mask: 1, range: 0): 0.098 sec  0.102 sec ( 4%)  0.119 sec
     111(mask: 0, range: 0): 0.136 sec  0.162 sec (20%)  0.170 sec
     112(mask: 1, range: 0): 0.177 sec  0.181 sec ( 2%)  0.198 sec
     113 
     114(effect of errors not tested)
     115 
     116To optmize this, use a macro and ifdef in or out the three states (errors, mask, range)
     117*****************************************************************************/
    247118static bool vectorSampleMean(const psVector* myVector,
    248119                             const psVector* errors,
     
    253124    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    254125
     126    long count = 0;                     // Number of points contributing to this mean
    255127    psF32 mean = 0.0;                   // The mean
    256     long count = 0;                     // Number of points contributing to this mean
     128    psF32 weight;
    257129
    258130    psF32 *data = myVector->data.F32;   // Dereference
     
    262134    bool useRange = stats->options & PS_STAT_USE_RANGE;
    263135
    264     // If errors exist, we enter a slightly different loop.
    265     // XXX collapse to a single loop?
    266     if (!errors) {
    267         for (long i = 0; i < numData; i++) {
    268             // Check if the data is with the specified range
    269             if (useRange && (data[i] < stats->min))
    270                 continue;
    271             if (useRange && (data[i] > stats->max))
    272                 continue;
    273             if (maskData && (maskData[i] & maskVal))
    274                 continue;
    275             mean += data[i];
    276             count++;
    277         }
    278         mean = (count > 0) ? mean / (psF32) count : NAN;
    279     } else {
    280         psF32 sumWeights = 0.0;         // The sum of the weights
    281         psF32 *errorsData = errors->data.F32;
    282         for (long i = 0; i < numData; i++) {
    283             // Check if the data is with the specified range
    284             if (useRange && (data[i] < stats->min))
    285                 continue;
    286             if (useRange && (data[i] > stats->max))
    287                 continue;
    288             if (maskData && (maskData[i] & maskVal))
    289                 continue;
    290 
    291             float weight = 1.0 / PS_SQR(errorsData[i]);
     136    psF32 sumWeights = 0.0;  // The sum of the weights
     137    psF32 *errorsData = (errors == NULL) ? NULL : errors->data.F32;
     138
     139    for (long i = 0; i < numData; i++) {
     140        // Check if the data is with the specified range
     141        if (useRange && (data[i] < stats->min))
     142            continue;
     143        if (useRange && (data[i] > stats->max))
     144            continue;
     145        if (maskData && (maskData[i] & maskVal))
     146            continue;
     147        if (errors) {
     148            weight = (errorsData[i] == 0) ? 0.0 : PS_SQR(errorsData[i]);
    292149            mean += data[i] * weight;
    293150            sumWeights += weight;
    294             count++;
    295         }
     151        } else {
     152            mean += data[i];
     153        }
     154        count++;
     155    }
     156    if (errors) {
    296157        mean = (count > 0) ? mean / sumWeights : NAN;
    297     }
    298 
     158    } else {
     159        mean = (count > 0) ? mean / count : NAN;
     160    }
    299161    stats->sampleMean = mean;
    300162    if (isnan(mean)) {
     163        // XXX raise an error here?
    301164        psTrace("psLib.math", 4, "---- %s(false) end ----\n", __func__);
    302165        return false;
    303166    }
    304167
     168    stats->results |= PS_STAT_SAMPLE_MEAN;
    305169    psTrace("psLib.math", 4, "---- %s(true) end ----\n", __func__);
    306170    return true;
     
    319183(mask: 0, range: 0):  0.179 sec  0.208 sec
    320184(mask: 1, range: 0):  0.200 sec  0.244 sec
    321  *****************************************************************************/
     185*****************************************************************************/
    322186static long vectorMinMax(const psVector* myVector,
    323187                         const psVector* maskVector,
     
    350214    }
    351215
     216    // XXX save numValid in psStats?
    352217    if (numValid == 0) {
    353218        stats->max = NAN;
     
    356221        stats->max = max;
    357222        stats->min = min;
     223        stats->results |= PS_STAT_MIN;
     224        stats->results |= PS_STAT_MAX;
    358225    }
    359226    psTrace("psLib.math", 4, "---- %s(%d) end ----\n", __func__, numValid);
    360227    return numValid;
    361228}
    362 
    363 #if 0
    364 /******************************************************************************
    365 vectorCheckNonEmpty(myVector, maskVector, maskVal, stats): This routine returns
    366 "true" if the inputPsVector has 1 or more valid elements (a valid element is an
    367 unmasked element within the specified min/max range).  Otherwise, return
    368 "false".
    369  *****************************************************************************/
    370 static bool vectorCheckNonEmpty(const psVector* myVector,
    371                                 const psVector* maskVector,
    372                                 psMaskType maskVal,
    373                                 psStats* stats)
    374 {
    375     psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    376     PS_ASSERT_VECTOR_NON_NULL(myVector, false);
    377     PS_ASSERT_PTR_NON_NULL(stats, false);
    378 
    379     if (stats->options & PS_STAT_USE_RANGE) {
    380         if (maskVector) {
    381             for (long i = 0; i < myVector->n; i++) {
    382                 if (!(maskVal & maskVector->data.U8[i]) &&
    383                         (stats->min <= myVector->data.F32[i]) &&
    384                         (myVector->data.F32[i] <= stats->max)) {
    385                     psTrace("psLib.math", 4, "---- %s(true) end ----\n", __func__);
    386                     return true;
    387                 }
    388             }
    389         } else {
    390             for (long i = 0; i < myVector->n; i++) {
    391                 if ((stats->min <= myVector->data.F32[i]) &&
    392                         (myVector->data.F32[i] <= stats->max)) {
    393                     psTrace("psLib.math", 4, "---- %s(true) end ----\n", __func__);
    394                     return true;
    395                 }
    396             }
    397         }
    398     } else {
    399         if (maskVector != NULL) {
    400             for (long i = 0; i < myVector->n; i++) {
    401                 if (!(maskVal & maskVector->data.U8[i])) {
    402                     psTrace("psLib.math", 4, "---- %s(true) end ----\n", __func__);
    403                     return true;
    404                 }
    405             }
    406         } else {
    407             if (myVector->n > 0) {
    408                 psTrace("psLib.math", 4, "---- %s(true) end ----\n", __func__);
    409                 return true;
    410             }
    411         }
    412     }
    413     psTrace("psLib.math", 4, "---- %s(false) end ----\n", __func__);
    414     return(false);
    415 }
    416 #endif
    417229
    418230/******************************************************************************
    419231vectorSampleMedian(myVector, maskVector, maskVal, stats): calculates the median
    420232and quartiles of the input vector.  Returns true on success (including if there
    421 were no valid input vector elements).
    422  *****************************************************************************/
    423 static bool vectorSampleMedian(const psVector* myVector,
     233were no valid input vector elements). Expects F32 vector for input.
     234*****************************************************************************/
     235static bool vectorSampleMedian(const psVector* inVector,
    424236                               const psVector* maskVector,
    425237                               psMaskType maskVal,
     
    428240    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    429241
     242    bool useRange = stats->options & PS_STAT_USE_RANGE;
     243    psU8 *maskData = (maskVector == NULL) ? NULL : maskVector->data.U8; // Dereference the vector
     244    psF32 *input = inVector->data.F32; // Dereference the vector
     245
    430246    // Allocate temporary vectors for the data.
    431     psVector* vector = psVectorAllocEmpty(myVector->n, PS_TYPE_F32); // Vector containing the unmasked values
     247    psVector *outVector = psVectorAllocEmpty(inVector->n, PS_TYPE_F32); // Vector containing the unmasked values
     248    psF32 *output = outVector->data.F32; // Dereference the vector
     249
    432250    long count = 0;                     // Number of valid entries
    433251
    434     // Determine if we must only use data points within a min/max range.
    435     if (stats->options & PS_STAT_USE_RANGE) {
    436         // Store all non-masked data points within the min/max range
    437         // into the temporary vectors.
    438         if (maskVector != NULL) {
    439             for (long i = 0; i < myVector->n; i++) {
    440                 if (!(maskVal & maskVector->data.U8[i]) &&
    441                         (stats->min <= myVector->data.F32[i]) &&
    442                         (myVector->data.F32[i] <= stats->max)) {
    443                     vector->data.F32[count] = myVector->data.F32[i];
    444                     count++;
    445                 }
    446             }
    447         } else {
    448             for (long i = 0; i < myVector->n; i++) {
    449                 if ((stats->min <= myVector->data.F32[i]) &&
    450                         (myVector->data.F32[i] <= stats->max)) {
    451                     vector->data.F32[count] = myVector->data.F32[i];
    452                     count++;
    453                 }
    454             }
    455         }
    456     } else {
    457         // Store all non-masked data points into the temporary vectors.
    458         if (maskVector) {
    459             for (long i = 0; i < myVector->n; i++) {
    460                 if (!(maskVal & maskVector->data.U8[i])) {
    461                     vector->data.F32[count] = myVector->data.F32[i];
    462                     count++;
    463                 }
    464             }
    465         } else {
    466             vector = psVectorCopy(vector, myVector, PS_TYPE_F32);
    467             count = myVector->n;
    468         }
    469     }
    470     vector->n = count;
     252    // Store all non-masked data points within the min/max range
     253    // into the temporary vectors.
     254    for (long i = 0; i < inVector->n; i++) {
     255        if (useRange && (input[i] < stats->min))
     256            continue;
     257        if (useRange && (input[i] > stats->max))
     258            continue;
     259        if (maskData && (maskData[i] & maskVal))
     260            continue;
     261
     262        output[count] = input[i];
     263        count++;
     264    }
     265    outVector->n = count;
     266
    471267    if (count == 0) {
    472268        psError(PS_ERR_BAD_PARAMETER_SIZE, true, "No valid data in input vector.\n");
    473         psFree(vector);
     269        psFree(outVector);
     270        stats->sampleUQ = NAN;
     271        stats->sampleLQ = NAN;
    474272        stats->sampleMedian = NAN;
    475273        return false;
     
    477275
    478276    // Sort the temporary vector.
    479     vector = psVectorSort(vector, vector); // Sort in-place (since it's a copy, it's OK)
    480     if (!vector) {
    481         psError(PS_ERR_UNEXPECTED_NULL,
    482                 false,
    483                 _("Failed to sort input data."));
    484         psTrace("psLib.math", 4, "---- %s(false) end ----\n", __func__);
    485         psFree(vector);
     277    // XXX this is messed up: should psVectorSort free on error or not?
     278    if (!psVectorSort(outVector, outVector)) { // Sort in-place (since it's a copy, it's OK)
     279        psError(PS_ERR_UNEXPECTED_NULL, false, _("Failed to sort input data."));
     280        stats->sampleUQ = NAN;
     281        stats->sampleLQ = NAN;
     282        stats->sampleMedian = NAN;
    486283        return false;
    487284    }
    488285
    489     // Calculate the median exactly.  Use the average if the number of samples
    490     // is even.
     286    // Calculate the median.  Use the average if the number of samples if even.
    491287    if (count % 2 == 0) {
    492         stats->sampleMedian = 0.5 * (vector->data.F32[(count / 2) - 1] +
    493                                      vector->data.F32[count / 2]);
     288        stats->sampleMedian = 0.5 * (output[(count/2) - 1] + output[count/2]);
    494289    } else {
    495         stats->sampleMedian = vector->data.F32[count / 2];
     290        stats->sampleMedian = output[count/2];
    496291    }
    497292
    498293    // Calculate the quartile points exactly.
    499     stats->sampleUQ = vector->data.F32[3 * (count / 4)];
    500     stats->sampleLQ = vector->data.F32[count / 4];
     294    stats->sampleUQ = output[(int)(0.75*count)];
     295    stats->sampleLQ = output[(int)(0.25*count)];
     296
     297    stats->results |= PS_STAT_SAMPLE_MEDIAN;
     298    stats->results |= PS_STAT_SAMPLE_QUARTILE;
    501299
    502300    // Free the temporary data structures.
    503     psFree(vector);
     301    psFree(outVector);
    504302
    505303    // Return "true" on success.
    506304    psTrace("psLib.math", 4, "---- %s(true) end ----\n", __func__);
    507305    return true;
    508 }
    509 
    510 /******************************************************************************
    511 vectorSmoothHistGaussian(): This routine smoothes the data in the input
    512 robustHistogram with a Gaussian of width sigma.  It returns a psVector of the
    513 smoothed data.
    514  *****************************************************************************/
    515 psVector *vectorSmoothHistGaussian(psHistogram *histogram,
    516                                    psF32 sigma)
    517 {
    518     psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    519     psTrace("psLib.math", 5, "(histogram->nums->n, sigma) is (%d, %.2f\n", (int) histogram->nums->n, sigma);
    520     PS_ASSERT_PTR_NON_NULL(histogram, NULL);
    521     PS_ASSERT_PTR_NON_NULL(histogram->bounds, NULL);
    522     PS_ASSERT_PTR_NON_NULL(histogram->nums, NULL);
    523     if (psTraceGetLevel("psLib.math") >= 8) {
    524         PS_VECTOR_PRINT_F32(histogram->nums);
    525     }
    526 
    527     long numBins = histogram->nums->n;  // Number of histogram bins
    528     psVector *smooth = psVectorAlloc(numBins, PS_TYPE_F32); // Smoothed version of histogram bins
    529     const psVector *bounds = histogram->bounds; // The bounds for the histogram bins
    530 
    531     if (!histogram->uniform) {
    532         //
    533         // We get here if the histogram is non-uniform.  This code is not tested.
    534         // However, it is also not used anywhere, yet.
    535         //
    536         psLogMsg(__func__, PS_LOG_WARN, "WARNING: vectorSmoothHistGaussian() on non-uniform histograms has not been tested or used.\n");
    537 
    538         for (long i = 0; i < numBins; i++) {
    539             // Determine the midpoint of bin i.
    540             float iMid = PS_BIN_MIDPOINT(histogram, i);
    541 
    542             //
    543             // We determine the bin numbers (jMin:jMax) corresponding to a
    544             // range of data values surrounding iMid.  The range is of size:
    545             // 2*PS_GAUSS_WIDTH*sigma
    546             long jMin, jMax;
    547             psF32 x = iMid - (PS_GAUSS_WIDTH * sigma);
    548             for (jMin = i; jMin > 0 && bounds->data.F32[jMin] > x; jMin--)
    549                 ;
    550             x = iMid + (PS_GAUSS_WIDTH * sigma);
    551             for (jMax = i; jMax < bounds->n - 1 && bounds->data.F32[jMax + 1] > x; jMax++)
    552                 ;
    553 
    554             //
    555             // Loop from jMin to jMax, computing the gaussian of data i.
    556             //
    557             smooth->data.F32[i] = 0.0;
    558             for (long j = jMin ; j <= jMax ; j++) {
    559                 float jMid = PS_BIN_MIDPOINT(histogram, j);
    560                 smooth->data.F32[i] +=
    561                     histogram->nums->data.F32[j] * psGaussian(jMid, iMid, sigma, true);
    562             }
    563         }
    564     } else {
    565         //
    566         // We get here if the histogram is uniform.
    567         //
    568         for (long i = 0; i < numBins; i++) {
    569             psF32 binSize = bounds->data.F32[1] - bounds->data.F32[0];
    570             psS32 gaussWidth = ((PS_GAUSS_WIDTH * sigma) / binSize);
    571 
    572             //
    573             // XXX: The following is wrong.  However, in practice, the sigma was too
    574             // large compared to the binSize.  This meant that the smoothing was done
    575             // over 500 bins in the robust stats algorithm.  This mean that the smoothing
    576             // took way too long.
    577             //
    578             #if 0
    579 
    580             gaussWidth = 10;
    581             #endif
    582             //
    583             // We determine the bin numbers (jMin:jMax) corresponding to a
    584             // range of data values surrounding iMid.  The range is of size:
    585             // 2*PS_GAUSS_WIDTH*sigma
    586             //
    587             psS32 jMin = PS_MAX(i - gaussWidth, 0);
    588             psS32 jMax = PS_MIN(i + gaussWidth, bounds->n - 1);
    589 
    590             //
    591             // Loop from jMin to jMax, computing the gaussian of data i.
    592             //
    593             smooth->data.F32[i] = 0.0;
    594             float iMid = PS_BIN_MIDPOINT(histogram, i);
    595             for (long j = jMin ; j <= jMax ; j++) {
    596                 float jMid = PS_BIN_MIDPOINT(histogram, j);
    597                 smooth->data.F32[i] +=
    598                     histogram->nums->data.F32[j] * psGaussian(jMid, iMid, sigma, true);
    599             }
    600         }
    601     }
    602 
    603     if (psTraceGetLevel("psLib.math") >= 8) {
    604         PS_VECTOR_PRINT_F32(smooth);
    605     }
    606     psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
    607     return(smooth);
    608306}
    609307
     
    619317    NULL
    620318 
    621  *****************************************************************************/
     319using the method below, with a single loop for various options costs only a small amount and is
     320much easier to debug. running 10000 tests of 1000 point vectors for the two methods gives:
     321 
     322                     single
     323(mask: 0, range: 0): 0.193 sec
     324(mask: 1, range: 0): 0.257 sec
     325(mask: 0, range: 0): 0.349 sec
     326(mask: 1, range: 0): 0.401 sec
     327 
     328*****************************************************************************/
    622329static bool vectorSampleStdev(const psVector* myVector,
    623330                              const psVector* errors,
     
    630337    // This procedure requires the mean.  If it has not been already
    631338    // calculated, then call vectorSampleMean()
    632     if (isnan(stats->sampleMean)) {
     339    if (!(stats->results & PS_STAT_SAMPLE_MEAN)) {
    633340        vectorSampleMean(myVector, errors, maskVector, maskVal, stats);
    634341    }
     342
    635343    // If the mean is NAN, then generate a warning and set the stdev to NAN.
    636344    if (isnan(stats->sampleMean)) {
     
    641349    }
    642350
     351    psF32 *data = myVector->data.F32;   // Dereference
     352    psU8 *maskData = (maskVector == NULL) ? NULL : maskVector->data.U8;
     353    bool useRange = stats->options & PS_STAT_USE_RANGE;
     354    psF32 *errorsData = (errors == NULL) ? NULL : errors->data.F32;
     355
    643356    // Accumulate the sums
    644357    double mean = stats->sampleMean;    // The mean
     
    647360    double errorDivisor = 0.0;          // Division for errors
    648361    long count = 0;                     // Number of data points being used
    649     if (stats->options & PS_STAT_USE_RANGE) {
    650         if (maskVector) {
    651             for (long i = 0; i < myVector->n; i++) {
    652                 if (!(maskVal & maskVector->data.U8[i]) &&
    653                         (stats->min <= myVector->data.F32[i]) &&
    654                         (myVector->data.F32[i] <= stats->max)) {
    655                     double diff = myVector->data.F32[i] - mean;
    656                     sumSquares += PS_SQR(diff);
    657                     sumDiffs += diff;
    658                     count++;
    659                     if (errors) {
    660                         errorDivisor += 1.0 / PS_SQR(errors->data.F32[i]);
    661                     }
    662                 }
    663             }
    664         } else {
    665             for (long i = 0; i < myVector->n; i++) {
    666                 if ((stats->min <= myVector->data.F32[i]) && (myVector->data.F32[i] <= stats->max)) {
    667                     double diff = myVector->data.F32[i] - mean;
    668                     sumSquares += PS_SQR(diff);
    669                     sumDiffs += diff;
    670                     count++;
    671                     if (errors) {
    672                         errorDivisor += 1.0 / PS_SQR(errors->data.F32[i]);
    673                     }
    674                 }
    675             }
    676             count = myVector->n;
    677         }
    678     } else {
    679         if (maskVector) {
    680             for (long i = 0; i < myVector->n; i++) {
    681                 if (!(maskVal & maskVector->data.U8[i])) {
    682                     double diff = myVector->data.F32[i] - mean;
    683                     sumSquares += PS_SQR(diff);
    684                     sumDiffs += diff;
    685                     count++;
    686                     if (errors) {
    687                         errorDivisor += 1.0 / PS_SQR(errors->data.F32[i]);
    688                     }
    689                 }
    690             }
    691         } else {
    692             for (long i = 0; i < myVector->n; i++) {
    693                 double diff = myVector->data.F32[i] - mean;
    694                 sumSquares += PS_SQR(diff);
    695                 sumDiffs += diff;
    696                 count++;
    697                 if (errors) {
    698                     errorDivisor += 1.0 / PS_SQR(errors->data.F32[i]);
    699                 }
    700             }
    701             count = myVector->n;
     362    for (long i = 0; i < myVector->n; i++) {
     363        // Check if the data is with the specified range
     364        if (useRange && (data[i] < stats->min))
     365            continue;
     366        if (useRange && (data[i] > stats->max))
     367            continue;
     368        if (maskData && (maskData[i] & maskVal))
     369            continue;
     370
     371        double diff = data[i] - mean;
     372        sumSquares += PS_SQR(diff);
     373        sumDiffs += diff;
     374        count++;
     375        if (errors) {
     376            errorDivisor += 1.0 / PS_SQR(errorsData[i]);
    702377        }
    703378    }
     
    717392        stats->sampleStdev = (1.0 / sqrtf(errorDivisor));
    718393    } else {
    719         stats->sampleStdev = sqrt((sumSquares - (sumDiffs * sumDiffs / (float)count)) /
    720                                   (float)(count - 1));
    721     }
     394        stats->sampleStdev = sqrt((sumSquares - (sumDiffs * sumDiffs / (float)count)) / (float)(count - 1));
     395    }
     396    stats->results |= PS_STAT_SAMPLE_STDEV;
     397
    722398    psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
    723399
     
    736412Returns
    737413    true for success; false otherwise
    738  *****************************************************************************/
     414*****************************************************************************/
    739415static bool vectorClippedStats(const psVector* myVector,
    740416                               const psVector* errors,
     
    757433                                 PS_CLIPPED_SIGMA_UB, -1);
    758434
    759     // Allocate a psStats structure for calculating the mean, median, and
    760     // stdev.
    761     psStats *statsTmp = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
     435    // unless we succeed, these will have NAN values
     436    stats->clippedMean = NAN;
     437    stats->clippedStdev = NAN;
     438    stats->clippedNvalues = 0;
    762439
    763440    // We copy the mask vector, to preserve the original
     
    773450    }
    774451
    775     // 1. Compute the sample median.
    776     vectorSampleMedian(myVector, tmpMask, maskVal, statsTmp);
    777     if (isnan(statsTmp->sampleMedian)) {
     452    // 1. Compute the sample median, which we save for output
     453    vectorSampleMedian(myVector, tmpMask, maskVal, stats);
     454    if (isnan(stats->sampleMedian)) {
    778455        psLogMsg(__func__, PS_LOG_WARN, "Call to vectorSampleMedian returned NAN\n");
    779         stats->clippedMean = NAN;
    780         stats->clippedStdev = NAN;
    781456        psTrace("psLib.math", 4, "---- %s(false) end ----\n", __func__);
    782457        psFree(tmpMask);
    783         psFree(statsTmp);
    784458        return false;
    785459    }
    786     psTrace("psLib.math", 6, "The initial sample median is %f\n", statsTmp->sampleMedian);
    787 
    788     // 2. Compute the sample standard deviation.
    789     vectorSampleStdev(myVector, errors, tmpMask, maskVal, statsTmp);
    790     if (isnan(statsTmp->sampleStdev)) {
     460    psTrace("psLib.math", 6, "The initial sample median is %f\n", stats->sampleMedian);
     461
     462    // 2. Compute the sample standard deviation, which we save for output
     463    vectorSampleStdev(myVector, errors, tmpMask, maskVal, stats);
     464    if (isnan(stats->sampleStdev)) {
    791465        psLogMsg(__func__, PS_LOG_WARN, "Call to vectorSampleStdev returned NAN\n");
    792         stats->clippedMean = NAN;
    793         stats->clippedStdev = NAN;
    794466        psTrace("psLib.math", 4, "---- %s(false) end ----\n", __func__);
    795467        psFree(tmpMask);
    796         psFree(statsTmp);
    797468        return false;
    798469    }
    799     psTrace("psLib.math", 6, "The initial sample stdev is %f\n", statsTmp->sampleStdev);
     470    psTrace("psLib.math", 6, "The initial sample stdev is %f\n", stats->sampleStdev);
    800471
    801472    // 3. Use the sample median as the first estimator of the mean X.
    802     psF32 clippedMean = statsTmp->sampleMedian;
     473    psF32 clippedMean = stats->sampleMedian;
    803474
    804475    // 4. Use the sample stdev as the first estimator of the mean stdev.
    805     psF32 clippedStdev = statsTmp->sampleStdev;
     476    psF32 clippedStdev = stats->sampleStdev;
    806477
    807478    // 5. Repeat N (stats->clipIter) times:
     
    836507
    837508        // b) compute new mean and stdev
     509        // Allocate a psStats structure for calculating the mean and stdev.
     510        psStats *statsTmp = psStatsAlloc(PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
    838511        vectorSampleMean(myVector, errors, tmpMask, maskVal, statsTmp);
    839512        vectorSampleStdev(myVector, errors, tmpMask, maskVal, statsTmp);
     
    853526            clippedStdev = statsTmp->sampleStdev;
    854527        }
     528        psFree(statsTmp);
    855529    }
    856530
     
    859533
    860534    // 7. The last calcuated value of x is the clipped mean.
    861     if (stats->options & PS_STAT_CLIPPED_MEAN) {
    862         stats->clippedMean = clippedMean;
    863         psTrace("psLib.math", 6, "The final clipped mean is %f\n", clippedMean);
    864     }
    865535    // 8. The last calcuated value of stdev is the clipped stdev.
    866     if (stats->options & PS_STAT_CLIPPED_STDEV) {
    867         stats->clippedStdev = clippedStdev;
    868         psTrace("psLib.math", 6, "The final clipped stdev is %f\n", clippedStdev);
    869     }
     536    // we always return both stats even if only one was requested
     537    stats->clippedMean = clippedMean;
     538    stats->clippedStdev = clippedStdev;
     539
     540    stats->results |= PS_STAT_CLIPPED_MEAN;
     541    stats->results |= PS_STAT_CLIPPED_STDEV;
     542
     543    psTrace("psLib.math", 6, "The final clipped mean is %f\n", clippedMean);
     544    psTrace("psLib.math", 6, "The final clipped stdev is %f\n", clippedStdev);
    870545
    871546    psFree(tmpMask);
    872     psFree(statsTmp);
    873547    psTrace("psLib.math", 4, "---- %s(true) end ----\n", __func__);
    874548    return true;
    875549}
    876 
    877 static psF32 QuadraticInverse(psF32 a,
    878                               psF32 b,
    879                               psF32 c,
    880                               psF32 y,
    881                               psF32 xLo,
    882                               psF32 xHi
    883                              )
    884 {
    885     psF64 tmp = sqrt((y - c)/a + (b*b)/(4.0 * a * a));
    886 
    887     psF64 x1 = -b/(2.0*a) + tmp;
    888     psF64 x2 = -b/(2.0*a) - tmp;
    889 
    890     #if 0
    891 
    892     psF64 y1 = (a * x1 * x1) + (b * x1) + c;
    893     psF64 y2 = (a * x2 * x2) + (b * x2) + c;
    894     printf("QuadraticInverse: %fx^2 + %fx + %f\n", a, b, c);
    895     printf("QuadraticInverse: y is %f\n", y);
    896     printf("QuadraticInverse: (x1, x2) is (%f %f)\n", x1, x2);
    897     printf("QuadraticInverse: (y1, y2) is (%f %f)\n", y1, y2);
    898     #endif
    899 
    900     if (xLo <= x1 && x1 <= xHi) {
    901         return x1;
    902     }
    903     if (xLo <= x2 && x2 <= xHi) {
    904         return x2;
    905     }
    906     return 0.5 * (xLo + xHi);
    907 }
    908 
    909 
    910 /******************************************************************************
    911 fitQuadraticSearchForYThenReturnX(*xVec, *yVec, binNum, yVal): A general
    912 routine which fits a quadratic to three points and returns the x-value
    913 corresponding to the input y-value.  This routine takes psVectors of x/y pairs
    914 as input, and fits a quadratic to the 3 points surrounding element binNum in
    915 the vectors (the midpoint between element i and i+1 is used for x[i]).  It
    916 then determines for what value x does that quadratic f(x) = yVal (the input
    917 parameter).
    918  
    919 *****************************************************************************/
    920 static psF32 fitQuadraticSearchForYThenReturnX(const psVector *xVec,
    921         psVector *yVec,
    922         psS32 binNum,
    923         psF32 yVal
    924                                               )
    925 {
    926     psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    927     psTrace("psLib.math", 5, "binNum, yVal is (%d, %f)\n", binNum, yVal);
    928     if (psTraceGetLevel("psLib.math") >= 8) {
    929         PS_VECTOR_PRINT_F32(xVec);
    930         PS_VECTOR_PRINT_F32(yVec);
    931     }
    932 
    933     PS_ASSERT_VECTOR_NON_NULL(xVec, NAN);
    934     PS_ASSERT_VECTOR_NON_NULL(yVec, NAN);
    935     PS_ASSERT_VECTOR_TYPE(xVec, PS_TYPE_F32, NAN);
    936     PS_ASSERT_VECTOR_TYPE(yVec, PS_TYPE_F32, NAN);
    937     //    PS_ASSERT_VECTORS_SIZE_EQUAL(xVec, yVec, NAN);
    938     PS_ASSERT_INT_WITHIN_RANGE(binNum, 0, (int)(xVec->n - 1), NAN);
    939     PS_ASSERT_INT_WITHIN_RANGE(binNum, 0, (int)(yVec->n - 1), NAN);
    940 
    941     psVector *x = psVectorAlloc(3, PS_TYPE_F64);
    942     psVector *y = psVectorAlloc(3, PS_TYPE_F64);
    943     psF32 tmpFloat = 0.0f;
    944 
    945     if ((binNum >= 1) && (binNum < (yVec->n - 2)) && (binNum < (xVec->n - 2))) {
    946         // The general case.  We have all three points.
    947         x->data.F64[0] = (psF64) (0.5 * (xVec->data.F32[binNum - 1] + xVec->data.F32[binNum]));
    948         x->data.F64[1] = (psF64) (0.5 * (xVec->data.F32[binNum] + xVec->data.F32[binNum+1]));
    949         x->data.F64[2] = (psF64) (0.5 * (xVec->data.F32[binNum+1] + xVec->data.F32[binNum+2]));
    950         y->data.F64[0] = yVec->data.F32[binNum - 1];
    951         y->data.F64[1] = yVec->data.F32[binNum];
    952         y->data.F64[2] = yVec->data.F32[binNum + 1];
    953         psTrace("psLib.math", 6, "x vec (orig) is (%f %f %f %f)\n", xVec->data.F32[binNum - 1],
    954                 xVec->data.F32[binNum], xVec->data.F32[binNum+1], xVec->data.F32[binNum+2]);
    955         psTrace("psLib.math", 6, "x data is (%f %f %f)\n", x->data.F64[0], x->data.F64[1], x->data.F64[2]);
    956         psTrace("psLib.math", 6, "y data is (%f %f %f)\n", y->data.F64[0], y->data.F64[1], y->data.F64[2]);
    957 
    958         //
    959         // Ensure that the y value lies within range of the y values.
    960         //
    961         if (! (((y->data.F64[0] <= yVal) && (yVal <= y->data.F64[2])) ||
    962                 ((y->data.F64[2] <= yVal) && (yVal <= y->data.F64[0]))) ) {
    963             psError(PS_ERR_BAD_PARAMETER_VALUE, true,
    964                     _("Specified yVal, %g, is not within y-range, %g to %g."),
    965                     (psF64)yVal, y->data.F64[0], y->data.F64[2]);
    966         }
    967 
    968         //
    969         // Ensure that the y values are monotonic.
    970         //
    971         if (((y->data.F64[0] < y->data.F64[1]) && !(y->data.F64[1] <= y->data.F64[2])) ||
    972                 ((y->data.F64[0] > y->data.F64[1]) && !(y->data.F64[1] >= y->data.F64[2]))) {
    973             psError(PS_ERR_UNKNOWN, true,
    974                     "This routine must be called with monotonically increasing or decreasing data points.\n");
    975             psFree(x);
    976             psFree(y);
    977             psTrace("psLib.math", 5, "---- %s() end ----\n", __func__);
    978             return NAN;
    979         }
    980 
    981         // Determine the coefficients of the polynomial.
    982         psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
    983         myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, y, NULL, x);
    984         if (myPoly == NULL) {
    985             psError(PS_ERR_UNEXPECTED_NULL, false,
    986                     _("Failed to fit a 1-dimensional polynomial to the three specified data points.  Returning NAN."));
    987             psFree(x);
    988             psFree(y);
    989             psTrace("psLib.math", 5, "---- %s(NAN) end ----\n", __func__);
    990             return NAN;
    991         }
    992         psTrace("psLib.math", 6, "myPoly->coeff[0] is %f\n", myPoly->coeff[0]);
    993         psTrace("psLib.math", 6, "myPoly->coeff[1] is %f\n", myPoly->coeff[1]);
    994         psTrace("psLib.math", 6, "myPoly->coeff[2] is %f\n", myPoly->coeff[2]);
    995         psTrace("psLib.math", 6, "Fitted y vec is (%f %f %f)\n",
    996                 (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[0]),
    997                 (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[1]),
    998                 (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[2]));
    999 
    1000         psTrace("psLib.math", 6, "We fit the polynomial, now find x such that f(x) equals %f\n", yVal);
    1001         tmpFloat = QuadraticInverse(myPoly->coeff[2], myPoly->coeff[1], myPoly->coeff[0], yVal,
    1002                                     x->data.F64[0], x->data.F64[2]);
    1003         psFree(myPoly);
    1004 
    1005         if (isnan(tmpFloat)) {
    1006             psError(PS_ERR_UNEXPECTED_NULL,
    1007                     false, _("Failed to determine the median of the fitted polynomial.  Returning NAN."));
    1008             psFree(x);
    1009             psFree(y);
    1010             psTrace("psLib.math", 5, "---- %s(NAN) end ----\n", __func__);
    1011             return(NAN);
    1012         }
    1013     } else {
    1014         // These are special cases where the bin is at the beginning or end of the vector.
    1015         if (binNum == 0) {
    1016             // We have two points only at the beginning of the vectors x and y.
    1017             tmpFloat = 0.5 * (xVec->data.F32[binNum] +
    1018                               xVec->data.F32[binNum + 1]);
    1019         } else if (binNum == (xVec->n - 1)) {
    1020             // The special case where we have two points only at the end of
    1021             // the vectors x and y.
    1022             // XXX: Is this right?
    1023             tmpFloat = xVec->data.F32[binNum];
    1024         } else if (binNum == (xVec->n - 2)) {
    1025             // XXX: Is this right?
    1026             tmpFloat = 0.5 * (xVec->data.F32[binNum] + xVec->data.F32[binNum + 1]);
    1027         }
    1028     }
    1029 
    1030     psTrace("psLib.math", 6, "FIT: return %f\n", tmpFloat);
    1031     psFree(x);
    1032     psFree(y);
    1033 
    1034     psTrace("psLib.math", 5, "---- %s(%f) end ----\n", __func__, tmpFloat);
    1035     return tmpFloat;
    1036 }
    1037 
    1038 /******************************************************************************
    1039 NOTE: We assume unnormalized gaussians.
    1040  *****************************************************************************/
    1041 static psF32 minimizeLMChi2Gauss1D(psVector *deriv,
    1042                                    const psVector *params,
    1043                                    const psVector *coords
    1044                                   )
    1045 {
    1046     psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    1047     PS_ASSERT_VECTOR_NON_NULL(params, NAN);
    1048     PS_ASSERT_VECTOR_SIZE(params, (long)2, NAN);
    1049     PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, NAN);
    1050     PS_ASSERT_VECTOR_NON_NULL(coords, NAN);
    1051     PS_ASSERT_VECTOR_SIZE(coords, (long)1, NAN);
    1052     PS_ASSERT_VECTOR_TYPE(coords, PS_TYPE_F32, NAN);
    1053 
    1054     psF32 x = coords->data.F32[0];
    1055     psF32 mean = params->data.F32[0];
    1056     psF32 var = params->data.F32[1];
    1057     psF32 dx = (x - mean);
    1058 
    1059     psF32 gauss = exp (-0.5*PS_SQR(dx)/var);
    1060     if (deriv) {
    1061         PS_ASSERT_VECTOR_SIZE(deriv, (long)2, NAN);
    1062         PS_ASSERT_VECTOR_TYPE(deriv, PS_TYPE_F32, NAN);
    1063         psF32 tmp = dx * gauss;
    1064         deriv->data.F32[0] = tmp / var;
    1065         deriv->data.F32[1] = tmp * dx / (var * var);
    1066     }
    1067 
    1068 
    1069     psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
    1070     return gauss;
    1071 }
    1072 
    1073 /*
    1074  * vectorFittedStats requires guess for fittedMean and fittedStdev
    1075  * robustN50 should also be set
    1076  */
    1077 static bool vectorFittedStats (const psVector* myVector,
    1078                                const psVector* errors,
    1079                                psVector* mask,
    1080                                psMaskType maskVal,
    1081                                psStats* stats)
    1082 {
    1083 
    1084     psTrace("psLib.math", 6, "The guess mean  is %f.\n", stats->fittedMean);
    1085     psTrace("psLib.math", 6, "The guess stdev is %f.\n", stats->fittedStdev);
    1086 
    1087     psStats *statsMinMax = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
    1088 
    1089     psScalar tmpScalar;                 // Temporary scalar variable, for p_psVectorBinDisect
    1090     tmpScalar.type.type = PS_TYPE_F32;
    1091 
    1092     psF32 binSize = 1;
    1093     if (stats->options & PS_STAT_USE_BINSIZE) {
    1094         // Set initial bin size to the specified value.
    1095         binSize = stats->binsize;
    1096         psTrace("psLib.math", 6, "Setting initial robust bin size to %.2f\n", binSize);
    1097     } else {
    1098         // construct a histogram with (sigma/10 < binsize < sigma)
    1099         // set roughly so that the lowest bins have about 2 cnts
    1100         // Nsmallest ~ N50 / (4*dN))
    1101         psF32 dN = PS_MAX (1, PS_MIN (10, stats->robustN50 / 8));
    1102         binSize = stats->fittedStdev / dN;
    1103     }
    1104 
    1105     // Determine the min/max of the vector (which prior outliers masked out)
    1106     int numValid = vectorMinMax(myVector, mask, maskVal, statsMinMax); // Number of values
    1107     float min = statsMinMax->min;
    1108     float max = statsMinMax->max;
    1109     if (numValid == 0 || isnan(min) || isnan(max)) {
    1110         psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
    1111         psFree(statsMinMax);
    1112         psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
    1113         return false;
    1114     }
    1115 
    1116     // Calculate the number of bins.
    1117     long numBins = (max - min) / binSize;
    1118     psTrace("psLib.math", 6, "The new min/max values are (%f, %f).\n", min, max);
    1119     psTrace("psLib.math", 6, "The new bin size is %f.\n", binSize);
    1120     psTrace("psLib.math", 6, "The numBins is %ld\n", numBins);
    1121 
    1122     psHistogram *histogram = psHistogramAlloc(min, max, numBins); // A new histogram (without outliers)
    1123     histogram = psVectorHistogram(histogram, myVector, errors, mask, maskVal);
    1124     if (psTraceGetLevel("psLib.math") >= 8) {
    1125         PS_VECTOR_PRINT_F32(histogram->nums);
    1126     }
    1127 
    1128     long binMin = 0;                // Low bin to check
    1129     long binMax = 0;                // High bin to check
    1130 
    1131     // Fit a Gaussian to the bins in the requested range about the guess mean
    1132     // min,max overrides clipSigma
    1133     psF32 maxFitSigma = 2.0;
    1134     if (isfinite(stats->clipSigma)) {
    1135         maxFitSigma = fabs(stats->clipSigma);
    1136     }
    1137     if (isfinite(stats->max)) {
    1138         maxFitSigma = fabs(stats->max);
    1139     }
    1140 
    1141     psF32 minFitSigma = 2.0;
    1142     if (isfinite(stats->clipSigma)) {
    1143         minFitSigma = fabs(stats->clipSigma);
    1144     }
    1145     if (isfinite(stats->min)) {
    1146         minFitSigma = fabs(stats->min);
    1147     }
    1148 
    1149     tmpScalar.data.F32 = stats->fittedMean - minFitSigma*stats->fittedStdev;
    1150     if (tmpScalar.data.F32 < min) {
    1151         binMin = 0;
    1152     } else {
    1153         binMin = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
    1154     }
    1155     tmpScalar.data.F32 = stats->fittedMean + maxFitSigma*stats->fittedStdev;
    1156     if (tmpScalar.data.F32 > max) {
    1157         binMax = histogram->bounds->n - 1;
    1158     } else {
    1159         binMax = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
    1160     }
    1161     if (binMin < 0 || binMax < 0) {
    1162         psError(PS_ERR_UNKNOWN, false, "Failed to calculate the -%f sigma : +%f sigma bins\n", minFitSigma, maxFitSigma);
    1163         psFree(statsMinMax);
    1164         psFree(histogram);
    1165         psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
    1166         return false;
    1167     }
    1168 
    1169     // Generate the variables that will be used in the Gaussian fitting
    1170     // XXX EAM : we should test / guarantee that there are at least 3 (right?) bins
    1171     psVector *y = psVectorAlloc((1 + (binMax - binMin)), PS_TYPE_F32); // Vector of coordinates
    1172     psArray *x = psArrayAlloc((1 + (binMax - binMin))); // Array of ordinates
    1173     for (long i = binMin, j = 0; i <= binMax ; i++, j++) {
    1174         y->data.F32[j] = histogram->nums->data.F32[i];
    1175         psVector *ordinate = psVectorAlloc(1, PS_TYPE_F32); // The ordinate value
    1176         ordinate->data.F32[0] = PS_BIN_MIDPOINT(histogram, i);
    1177         x->data[j] = ordinate;
    1178     }
    1179     if (psTraceGetLevel("psLib.math") >= 8) {
    1180         // XXX: Print the x array somehow.
    1181         PS_VECTOR_PRINT_F32(y);
    1182     }
    1183     psTrace("psLib.math", 6, "The clipped numBins is %ld\n", y->n);
    1184 
    1185     // Normalize y to [0.0:1.0] (since the psMinimizeLMChi2Gauss1D() functions is [0.0:1.0])
    1186     // XXX EAM : why not just fit the normalization as well??
    1187     p_psNormalizeVectorRange(y, 0.0, 1.0);
    1188 
    1189     // Fit a Gaussian to the data.
    1190     psMinimization *minimizer = psMinimizationAlloc(100, 0.01); // The minimizer information
    1191     psVector *params = psVectorAlloc(2, PS_TYPE_F32); // Parameters for the Gaussian
    1192     // Initial guess for the mean (index 0) and var (index 1).
    1193     params->data.F32[0] = stats->fittedMean;
    1194     params->data.F32[1] = PS_SQR(stats->fittedStdev);
    1195     if (psTraceGetLevel("psLib.math") >= 8) {
    1196         PS_VECTOR_PRINT_F32(params);
    1197         PS_VECTOR_PRINT_F32(y);
    1198     }
    1199     if (!psMinimizeLMChi2(minimizer, NULL, params, NULL, x, y, NULL, minimizeLMChi2Gauss1D)) {
    1200         psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
    1201         psFree(statsMinMax);
    1202         psFree(histogram);
    1203         psFree(x);
    1204         psFree(y);
    1205         psFree(minimizer);
    1206         psFree(params);
    1207         psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
    1208         return false;
    1209     }
    1210     if (psTraceGetLevel("psLib.math") >= 8) {
    1211         PS_VECTOR_PRINT_F32(params);
    1212     }
    1213 
    1214     // The fitted mean is the Gaussian mean.
    1215     stats->fittedMean = params->data.F32[0];
    1216     psTrace("psLib.math", 6, "The fitted mean is %f.\n", stats->fittedMean);
    1217 
    1218     // The fitted standard deviation
    1219     stats->fittedStdev = sqrt(params->data.F32[1]);
    1220     psTrace("psLib.math", 6, "The fitted stdev is %f.\n", stats->fittedStdev);
    1221 
    1222     // Clean up after fitting
    1223     psFree (histogram);
    1224     psFree (x);
    1225     psFree (y);
    1226     psFree (minimizer);
    1227     psFree (params);
    1228     psFree (statsMinMax);
    1229     return true;
    1230 }
    1231 
    1232550
    1233551/******************************************************************************
     
    1278596    }
    1279597
     598    // statsMinMax is only applied to a subset of the data points
    1280599    psStats *statsMinMax = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
    1281600    psHistogram *histogram = NULL;      // Histogram of the data
     
    1287606    // Iterate to get the best bin size
    1288607    for (int iterate = 1; iterate > 0; iterate++) {
    1289         psTrace("psLib.math", 6,
    1290                 "-------------------- Iterating on Bin size.  Iteration number %d --------------------\n",
    1291                 iterate);
     608        psTrace("psLib.math", 6, "-------------------- Iterating on Bin size.  Iteration number %d --------------------\n", iterate);
    1292609
    1293610        // Get the minimum and maximum values
     
    1308625        if (fabs(max - min) <= FLT_EPSILON) {
    1309626            psTrace("psLib.math", 5, "All data points have the same value: %f.\n", min);
    1310             if (stats->options & PS_STAT_ROBUST_MEDIAN) {
    1311                 stats->robustMedian = min;
    1312             }
    1313             if (stats->options & PS_STAT_ROBUST_QUARTILE) {
    1314                 stats->robustUQ = min;
    1315                 stats->robustLQ = min;
    1316             }
     627            stats->robustMedian = min;
     628            stats->robustUQ = min;
     629            stats->robustLQ = min;
    1317630            stats->robustN50 = numValid;
    1318631            psFree(statsMinMax);
     
    1433746            return false;
    1434747        }
    1435         // XXXX we can probably just use the bin values to get sigma without interpolating...
    1436         // ADD step 4b: Interpolate Sigma to find these two positions exactly: these are the 1sigma positions.
    1437         #if 0
    1438         // XXX: I am overriding the ADD for now.  The following code implements the ADD exactly and is having
    1439         // problems fitting a 2nd-order polynomial to data y-values suchs as (1, 1, 100).  Therefore, I
    1440         // commented out the code and am doing simply linear interpolation.
    1441 
    1442         // Value for the 15.8655% mark
    1443         float binLoF32 = fitQuadraticSearchForYThenReturnX(cumulative->bounds, cumulative->nums, binLo,
    1444                          totalDataPoints * 0.158655f);
    1445         // Value for the 84.1345% mark
    1446         float binHiF32 = fitQuadraticSearchForYThenReturnX(cumulative->bounds, cumulative->nums, binHi,
    1447                          totalDataPoints * 0.841345f);
    1448         psTrace("psLib.math", 6, "The exact 15.8655%% and 84.1345%% data point positions are: (%f, %f)\n",
    1449                 binLoF32, binHiF32);
    1450         #else
    1451         // This code basically interpolates to find the positions exactly.
     748
     749        // ADD step 4b: Interpolate Sigma (linearly) to find these two positions exactly: these are the 1sigma positions.
    1452750        psTrace("psLib.math", 6, "binLo is %ld.  Nums at that bin and the next are (%.2f, %.2f)\n",
    1453751                binLo, cumulative->nums->data.F32[binLo], cumulative->nums->data.F32[binLo+1]);
     
    1455753                binHi, cumulative->nums->data.F32[binHi], cumulative->nums->data.F32[binHi+1]);
    1456754
    1457         float deltaBounds = cumulative->bounds->data.F32[binLo+1] - cumulative->bounds->data.F32[binLo];
    1458         float deltaNums;
    1459         float prevPixels;
     755        float deltaBounds, deltaNums, prevPixels;
     756        float percentNums, base, binLoF32;
     757
     758        // find the -1 sigma points with linear interpolation
     759        deltaBounds = cumulative->bounds->data.F32[binLo+1] - cumulative->bounds->data.F32[binLo];
    1460760        if (binLo == 0) {
    1461761            deltaNums = cumulative->nums->data.F32[0];
     
    1465765            prevPixels = cumulative->nums->data.F32[binLo-1];
    1466766        }
    1467         float percentNums = (totalDataPoints * 0.158655f) - prevPixels;
    1468         float base = cumulative->bounds->data.F32[binLo];
    1469         float binLoF32 = base + (deltaBounds / deltaNums) * percentNums; // Value for the 15.8655% mark
     767        percentNums = (totalDataPoints * 0.158655f) - prevPixels;
     768        base = cumulative->bounds->data.F32[binLo];
     769        binLoF32 = base + (deltaBounds / deltaNums) * percentNums; // Value for the 15.8655% mark
    1470770        psTrace("psLib.math", 6,
    1471771                "(base, deltaBounds, deltaNums, prevPixels, percentNums) is (%.2f %.2f %.2f %.2f %.2f)\n",
    1472772                base, deltaBounds, deltaNums, prevPixels, percentNums);
    1473773
     774        // find the +1 sigma points with linear interpolation
    1474775        deltaBounds = cumulative->bounds->data.F32[binHi+1] - cumulative->bounds->data.F32[binHi];
    1475776        if (binHi == 0) {
     
    1486787                "(base, deltaBounds, deltaNums, prevPixels, percentNums) is (%.2f %.2f %.2f %.2f %.2f)\n",
    1487788                base, deltaBounds, deltaNums, prevPixels, percentNums);
    1488         psTrace("psLib.math", 6,
     789
     790        // report +/- 1 sigma points
     791        psTrace("psLib.math", 5,
    1489792                "The exact 15.8655 and 84.1345 percent data point positions are: (%f, %f)\n",
    1490793                binLoF32, binHiF32);
    1491         #endif
    1492794
    1493795        // ADD step 5: Determine SIGMA as 1/2 of the distance between these positions.
     
    1585887    psTrace("psLib.math", 6, "The robustN50 is %ld.\n", N50);
    1586888
    1587     // Fitted statistics
    1588     if (stats->options & PS_STAT_FITTED_MEAN || stats->options & PS_STAT_FITTED_STDEV) {
    1589         stats->fittedStdev = stats->robustStdev;  // pass the guess sigma
    1590         stats->fittedMean = stats->robustMedian;  // pass the guess mean
    1591         if (!vectorFittedStats(myVector, errors, mask, maskVal, stats)) {
    1592             psError(PS_ERR_UNKNOWN, false, "Unable to estimate statistics (pass 1)");
    1593             psFree(statsMinMax);
    1594             psFree(mask);
    1595 
    1596             return false;
    1597         }
    1598         // if there is a large swing in sigma, try a second time
    1599         if ((stats->fittedStdev/stats->robustStdev) < 0.75) {
    1600             if (!vectorFittedStats (myVector, errors, mask, maskVal, stats)) {
    1601                 psError(PS_ERR_UNKNOWN, false, "Unable to estimate statistics (pass 2)");
    1602                 psFree(statsMinMax);
    1603                 psFree(mask);
    1604 
    1605                 return false;
    1606             }
    1607         }
    1608     }
    1609 
    1610889    // Clean up
    1611890    psFree(statsMinMax);
    1612891    psFree(mask);
    1613892
     893    stats->results |= PS_STAT_ROBUST_MEDIAN;
     894    stats->results |= PS_STAT_ROBUST_STDEV;
     895
    1614896    psTrace("psLib.math", 4, "---- %s(0) end  ----\n", __func__);
    1615897    return true;
    1616898}
    1617899
     900/*
     901 * vectorFittedStats requires guess for fittedMean and fittedStdev
     902 * robustN50 should also be set
     903 */
     904static bool vectorFittedStats (const psVector* myVector,
     905                               const psVector* errors,
     906                               psVector* mask,
     907                               psMaskType maskVal,
     908                               psStats* stats)
     909{
     910
     911    // This procedure requires the mean.  If it has not been already
     912    // calculated, then call vectorSampleMean()
     913    if (!(stats->results & PS_STAT_ROBUST_MEDIAN)) {
     914        vectorRobustStats(myVector, errors, mask, maskVal, stats);
     915    }
     916
     917    // If the mean is NAN, then generate a warning and set the stdev to NAN.
     918    if (isnan(stats->robustMedian)) {
     919        stats->fittedStdev = NAN;
     920        stats->fittedStdev = NAN;
     921        psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
     922        return false;
     923    }
     924
     925    float guessStdev = stats->robustStdev;  // pass the guess sigma
     926    float guessMean = stats->robustMedian;  // pass the guess mean
     927
     928    psTrace("psLib.math", 6, "The guess mean  is %f.\n", guessMean);
     929    psTrace("psLib.math", 6, "The guess stdev is %f.\n", guessStdev);
     930
     931    bool done = false;
     932    for (int iteration = 0; !done && (iteration < 2); iteration ++) {
     933        psStats *statsMinMax = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
     934
     935        psScalar tmpScalar;                 // Temporary scalar variable, for p_psVectorBinDisect
     936        tmpScalar.type.type = PS_TYPE_F32;
     937
     938        psF32 binSize = 1;
     939        if (stats->options & PS_STAT_USE_BINSIZE) {
     940            // Set initial bin size to the specified value.
     941            binSize = stats->binsize;
     942            psTrace("psLib.math", 6, "Setting initial robust bin size to %.2f\n", binSize);
     943        } else {
     944            // construct a histogram with (sigma/10 < binsize < sigma)
     945            // set roughly so that the lowest bins have about 2 cnts
     946            // Nsmallest ~ N50 / (4*dN))
     947            psF32 dN = PS_MAX (1, PS_MIN (10, stats->robustN50 / 8));
     948            binSize = guessStdev / dN;
     949        }
     950
     951        // Determine the min/max of the vector (which prior outliers masked out)
     952        int numValid = vectorMinMax(myVector, mask, maskVal, statsMinMax); // Number of values
     953        float min = statsMinMax->min;
     954        float max = statsMinMax->max;
     955        if (numValid == 0 || isnan(min) || isnan(max)) {
     956            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
     957            psFree(statsMinMax);
     958            psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
     959            return false;
     960        }
     961
     962        // Calculate the number of bins.
     963        // XXX can we calculate the binMin, binMax **before** building this histogram?
     964        long numBins = (max - min) / binSize;
     965        psTrace("psLib.math", 6, "The new min/max values are (%f, %f).\n", min, max);
     966        psTrace("psLib.math", 6, "The new bin size is %f.\n", binSize);
     967        psTrace("psLib.math", 6, "The numBins is %ld\n", numBins);
     968
     969        psHistogram *histogram = psHistogramAlloc(min, max, numBins); // A new histogram (without outliers)
     970        histogram = psVectorHistogram(histogram, myVector, errors, mask, maskVal);
     971        if (psTraceGetLevel("psLib.math") >= 8) {
     972            PS_VECTOR_PRINT_F32(histogram->nums);
     973        }
     974
     975        long binMin = 0;                // Low bin to check
     976        long binMax = 0;                // High bin to check
     977
     978        // Fit a Gaussian to the bins in the requested range about the guess mean
     979        // min,max overrides clipSigma
     980        psF32 maxFitSigma = 2.0;
     981        if (isfinite(stats->clipSigma)) {
     982            maxFitSigma = fabs(stats->clipSigma);
     983        }
     984        if (isfinite(stats->max)) {
     985            maxFitSigma = fabs(stats->max);
     986        }
     987
     988        psF32 minFitSigma = 2.0;
     989        if (isfinite(stats->clipSigma)) {
     990            minFitSigma = fabs(stats->clipSigma);
     991        }
     992        if (isfinite(stats->min)) {
     993            minFitSigma = fabs(stats->min);
     994        }
     995
     996        tmpScalar.data.F32 = guessMean - minFitSigma*guessStdev;
     997        if (tmpScalar.data.F32 < min) {
     998            binMin = 0;
     999        } else {
     1000            binMin = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
     1001        }
     1002        tmpScalar.data.F32 = guessMean + maxFitSigma*guessStdev;
     1003        if (tmpScalar.data.F32 > max) {
     1004            binMax = histogram->bounds->n - 1;
     1005        } else {
     1006            binMax = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
     1007        }
     1008        if (binMin < 0 || binMax < 0) {
     1009            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the -%f sigma : +%f sigma bins\n", minFitSigma, maxFitSigma);
     1010            psFree(statsMinMax);
     1011            psFree(histogram);
     1012            psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
     1013            return false;
     1014        }
     1015
     1016        // Generate the variables that will be used in the Gaussian fitting
     1017        // XXX EAM : we should test / guarantee that there are at least 3 (right?) bins
     1018        psVector *y = psVectorAlloc((1 + (binMax - binMin)), PS_TYPE_F32); // Vector of coordinates
     1019        psArray *x = psArrayAlloc((1 + (binMax - binMin))); // Array of ordinates
     1020        for (long i = binMin, j = 0; i <= binMax ; i++, j++) {
     1021            y->data.F32[j] = histogram->nums->data.F32[i];
     1022            psVector *ordinate = psVectorAlloc(1, PS_TYPE_F32); // The ordinate value
     1023            ordinate->data.F32[0] = PS_BIN_MIDPOINT(histogram, i);
     1024            x->data[j] = ordinate;
     1025        }
     1026        if (psTraceGetLevel("psLib.math") >= 8) {
     1027            // XXX: Print the x array somehow.
     1028            PS_VECTOR_PRINT_F32(y);
     1029        }
     1030        psTrace("psLib.math", 6, "The clipped numBins is %ld\n", y->n);
     1031        psTrace("psLib.math", 6, "The clipped min is %f (%ld)\n", PS_BIN_MIDPOINT(histogram, binMin), binMin);
     1032        psTrace("psLib.math", 6, "The clipped max is %f (%ld)\n", PS_BIN_MIDPOINT(histogram, binMax), binMax);
     1033
     1034        // Normalize y to [0.0:1.0] (since the psMinimizeLMChi2Gauss1D() functions is [0.0:1.0])
     1035        // XXX EAM : why not just fit the normalization as well??
     1036        p_psNormalizeVectorRange(y, 0.0, 1.0);
     1037
     1038        // Fit a Gaussian to the data.
     1039        psMinimization *minimizer = psMinimizationAlloc(100, 0.01); // The minimizer information
     1040        psVector *params = psVectorAlloc(2, PS_TYPE_F32); // Parameters for the Gaussian
     1041        // Initial guess for the mean (index 0) and var (index 1).
     1042        params->data.F32[0] = guessMean;
     1043        params->data.F32[1] = PS_SQR(guessStdev);
     1044        if (psTraceGetLevel("psLib.math") >= 8) {
     1045            PS_VECTOR_PRINT_F32(params);
     1046            PS_VECTOR_PRINT_F32(y);
     1047        }
     1048        if (!psMinimizeLMChi2(minimizer, NULL, params, NULL, x, y, NULL, minimizeLMChi2Gauss1D)) {
     1049            psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
     1050            psFree(params);
     1051            psFree(minimizer);
     1052            psFree(x);
     1053            psFree(y);
     1054            psFree(histogram);
     1055            psFree(statsMinMax);
     1056            psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
     1057            return false;
     1058        }
     1059        if (psTraceGetLevel("psLib.math") >= 8) {
     1060            PS_VECTOR_PRINT_F32(params);
     1061        }
     1062
     1063        guessMean = params->data.F32[0];
     1064        guessStdev = sqrt(params->data.F32[1]);
     1065        if (guessStdev > 0.75*stats->robustStdev) {
     1066            done = true;
     1067        }
     1068
     1069        // Clean up after fitting
     1070        psFree (params);
     1071        psFree (minimizer);
     1072        psFree (x);
     1073        psFree (y);
     1074        psFree (histogram);
     1075        psFree (statsMinMax);
     1076    }
     1077
     1078    // The fitted mean is the Gaussian mean.
     1079    stats->fittedMean = guessMean;
     1080    psTrace("psLib.math", 6, "The fitted mean is %f.\n", stats->fittedMean);
     1081
     1082    // The fitted standard deviation
     1083    stats->fittedStdev = guessStdev;
     1084    psTrace("psLib.math", 6, "The fitted stdev is %f.\n", stats->fittedStdev);
     1085
     1086    stats->results |= PS_STAT_FITTED_MEAN;
     1087    stats->results |= PS_STAT_FITTED_STDEV;
     1088
     1089    return true;
     1090}
     1091
     1092
     1093/********************
     1094 * vectorFittedStats_v2 requires guess for fittedMean and fittedStdev
     1095 * robustN50 should also be set
     1096 * gaussian fit is performed using 2D polynomial to ln(y)
     1097 ********************/
     1098static bool vectorFittedStats_v2 (const psVector* myVector,
     1099                                  const psVector* errors,
     1100                                  psVector* mask,
     1101                                  psMaskType maskVal,
     1102                                  psStats* stats)
     1103{
     1104
     1105    // This procedure requires the mean.  If it has not been already
     1106    // calculated, then call vectorSampleMean()
     1107    if (!(stats->results & PS_STAT_ROBUST_MEDIAN)) {
     1108        vectorRobustStats(myVector, errors, mask, maskVal, stats);
     1109    }
     1110
     1111    // If the mean is NAN, then generate a warning and set the stdev to NAN.
     1112    if (isnan(stats->robustMedian)) {
     1113        stats->fittedStdev = NAN;
     1114        stats->fittedStdev = NAN;
     1115        psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
     1116        return false;
     1117    }
     1118
     1119    float guessStdev = stats->robustStdev;  // pass the guess sigma
     1120    float guessMean = stats->robustMedian;  // pass the guess mean
     1121
     1122    psTrace("psLib.math", 6, "The guess mean  is %f.\n", guessMean);
     1123    psTrace("psLib.math", 6, "The guess stdev is %f.\n", guessStdev);
     1124
     1125    bool done = false;
     1126    for (int iteration = 0; !done && (iteration < 2); iteration ++) {
     1127        psStats *statsMinMax = psStatsAlloc(PS_STAT_MIN | PS_STAT_MAX); // Statistics for min and max
     1128
     1129        psScalar tmpScalar;                 // Temporary scalar variable, for p_psVectorBinDisect
     1130        tmpScalar.type.type = PS_TYPE_F32;
     1131
     1132        psF32 binSize = 1;
     1133        if (stats->options & PS_STAT_USE_BINSIZE) {
     1134            // Set initial bin size to the specified value.
     1135            binSize = stats->binsize;
     1136            psTrace("psLib.math", 6, "Setting initial robust bin size to %.2f\n", binSize);
     1137        } else {
     1138            // construct a histogram with (sigma/10 < binsize < sigma)
     1139            // set roughly so that the lowest bins have about 2 cnts
     1140            // Nsmallest ~ N50 / (4*dN))
     1141            psF32 dN = PS_MAX (1, PS_MIN (10, stats->robustN50 / 8));
     1142            binSize = guessStdev / dN;
     1143        }
     1144
     1145        // Determine the min/max of the vector (which prior outliers masked out)
     1146        int numValid = vectorMinMax(myVector, mask, maskVal, statsMinMax); // Number of values
     1147        float min = statsMinMax->min;
     1148        float max = statsMinMax->max;
     1149        if (numValid == 0 || isnan(min) || isnan(max)) {
     1150            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the min/max of the input vector.\n");
     1151            psFree(statsMinMax);
     1152            psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
     1153            return false;
     1154        }
     1155
     1156        // Calculate the number of bins.
     1157        // XXX can we calculate the binMin, binMax **before** building this histogram?
     1158        long numBins = (max - min) / binSize;
     1159        psTrace("psLib.math", 6, "The new min/max values are (%f, %f).\n", min, max);
     1160        psTrace("psLib.math", 6, "The new bin size is %f.\n", binSize);
     1161        psTrace("psLib.math", 6, "The numBins is %ld\n", numBins);
     1162
     1163        psHistogram *histogram = psHistogramAlloc(min, max, numBins); // A new histogram (without outliers)
     1164        histogram = psVectorHistogram(histogram, myVector, errors, mask, maskVal);
     1165        if (psTraceGetLevel("psLib.math") >= 8) {
     1166            PS_VECTOR_PRINT_F32(histogram->nums);
     1167        }
     1168
     1169        long binMin = 0;                // Low bin to check
     1170        long binMax = 0;                // High bin to check
     1171
     1172        // Fit a Gaussian to the bins in the requested range about the guess mean
     1173        // min,max overrides clipSigma
     1174        psF32 maxFitSigma = 2.0;
     1175        if (isfinite(stats->clipSigma)) {
     1176            maxFitSigma = fabs(stats->clipSigma);
     1177        }
     1178        if (isfinite(stats->max)) {
     1179            maxFitSigma = fabs(stats->max);
     1180        }
     1181
     1182        psF32 minFitSigma = 2.0;
     1183        if (isfinite(stats->clipSigma)) {
     1184            minFitSigma = fabs(stats->clipSigma);
     1185        }
     1186        if (isfinite(stats->min)) {
     1187            minFitSigma = fabs(stats->min);
     1188        }
     1189
     1190        tmpScalar.data.F32 = guessMean - minFitSigma*guessStdev;
     1191        if (tmpScalar.data.F32 < min) {
     1192            binMin = 0;
     1193        } else {
     1194            binMin = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
     1195        }
     1196        tmpScalar.data.F32 = guessMean + maxFitSigma*guessStdev;
     1197        if (tmpScalar.data.F32 > max) {
     1198            binMax = histogram->bounds->n - 1;
     1199        } else {
     1200            binMax = p_psVectorBinDisect(histogram->bounds, &tmpScalar);
     1201        }
     1202        if (binMin < 0 || binMax < 0) {
     1203            psError(PS_ERR_UNKNOWN, false, "Failed to calculate the -%f sigma : +%f sigma bins\n", minFitSigma, maxFitSigma);
     1204            psFree(statsMinMax);
     1205            psFree(histogram);
     1206            psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
     1207            return false;
     1208        }
     1209
     1210        // Generate the variables that will be used in the Gaussian fitting
     1211        // XXX EAM : we should test / guarantee that there are at least 3 (right?) bins
     1212        psVector *y = psVectorAllocEmpty((1 + (binMax - binMin)), PS_TYPE_F32); // Vector of coordinates
     1213        psVector *x = psVectorAllocEmpty((1 + (binMax - binMin)), PS_TYPE_F32); // Vector of ordinates
     1214        long j = 0;
     1215        for (long i = binMin; i <= binMax ; i++) {
     1216            if (histogram->nums->data.F32[i] <= 0.0)
     1217                continue;
     1218            x->data.F32[j] = PS_BIN_MIDPOINT(histogram, i);
     1219            y->data.F32[j] = log(histogram->nums->data.F32[i]);
     1220            // XXX note this is the natural log: expected distribution is A exp(-(x-xo)^2/2sigma^2)
     1221            j++;
     1222        }
     1223        y->n = x->n = j;
     1224        if (psTraceGetLevel("psLib.math") >= 8) {
     1225            // XXX: Print the x array somehow.
     1226            PS_VECTOR_PRINT_F32(y);
     1227        }
     1228        psTrace("psLib.math", 6, "The clipped numBins is %ld\n", y->n);
     1229        psTrace("psLib.math", 6, "The clipped min is %f (%ld)\n", PS_BIN_MIDPOINT(histogram, binMin), binMin);
     1230        psTrace("psLib.math", 6, "The clipped max is %f (%ld)\n", PS_BIN_MIDPOINT(histogram, binMax), binMax);
     1231
     1232        // fit 2nd order polynomial to ln(y) = -(x-xo)^2/2sigma^2
     1233        psPolynomial1D *poly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
     1234
     1235        // XXX how can we protect against some extreme outliers?  the robust histogram
     1236        // should have already dealt with those, but we are again sensitive to them...
     1237        // psStats *fitStats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_STDEV);
     1238        // fitStats->clipIter = 3.0;
     1239        // fitStats->clipSigma = 3.0;
     1240        // psVector *fitMask = psVectorAlloc(y->n, PS_TYPE_U8);
     1241        // psVectorInit (fitMask, 0);
     1242
     1243        if (!psVectorFitPolynomial1D (poly, NULL, 0, y, NULL, x)) {
     1244            psError(PS_ERR_UNKNOWN, false, "Failed to fit a gaussian to the robust histogram.\n");
     1245            psFree(x);
     1246            psFree(y);
     1247            psFree(poly);
     1248            // psFree(fitStats);
     1249            // psFree(fitMask);
     1250            psFree(histogram);
     1251            psFree(statsMinMax);
     1252            psTrace("psLib.math", 4, "---- %s(false) end  ----\n", __func__);
     1253            return false;
     1254        }
     1255
     1256        guessStdev = sqrt(-0.5/poly->coeff[2]);
     1257        guessMean = poly->coeff[1]*PS_SQR(guessStdev);
     1258        if (guessStdev > 0.75*stats->robustStdev) {
     1259            done = true;
     1260        } else {
     1261            psTrace("psLib.math", 6, "The new guess mean  is %f.\n", guessMean);
     1262            psTrace("psLib.math", 6, "The new guess stdev is %f.\n", guessStdev);
     1263        }
     1264
     1265        // Clean up after fitting
     1266        psFree (x);
     1267        psFree (y);
     1268        psFree (poly);
     1269        // psFree (fitStats);
     1270        // psFree (fitMask);
     1271        psFree (histogram);
     1272        psFree (statsMinMax);
     1273    }
     1274
     1275    // The fitted mean is the Gaussian mean.
     1276    stats->fittedMean = guessMean;
     1277    psTrace("psLib.math", 6, "The fitted mean is %f.\n", stats->fittedMean);
     1278
     1279    // The fitted standard deviation
     1280    stats->fittedStdev = guessStdev;
     1281    psTrace("psLib.math", 6, "The fitted stdev is %f.\n", stats->fittedStdev);
     1282
     1283    stats->results |= PS_STAT_FITTED_MEAN_V2;
     1284    stats->results |= PS_STAT_FITTED_STDEV_V2;
     1285
     1286    return true;
     1287}
     1288
     1289
     1290/******************************************************************************
     1291vectorSmoothHistGaussian(): This routine smoothes the data in the input
     1292robustHistogram with a Gaussian of width sigma.  It returns a psVector of the
     1293smoothed data.
     1294 
     1295XXX this function is unused
     1296*****************************************************************************/
     1297psVector *vectorSmoothHistGaussian(psHistogram *histogram,
     1298                                   psF32 sigma)
     1299{
     1300    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
     1301    psTrace("psLib.math", 5, "(histogram->nums->n, sigma) is (%d, %.2f\n", (int) histogram->nums->n, sigma);
     1302    PS_ASSERT_PTR_NON_NULL(histogram, NULL);
     1303    PS_ASSERT_PTR_NON_NULL(histogram->bounds, NULL);
     1304    PS_ASSERT_PTR_NON_NULL(histogram->nums, NULL);
     1305    if (psTraceGetLevel("psLib.math") >= 8) {
     1306        PS_VECTOR_PRINT_F32(histogram->nums);
     1307    }
     1308
     1309    long numBins = histogram->nums->n;  // Number of histogram bins
     1310    psVector *smooth = psVectorAlloc(numBins, PS_TYPE_F32); // Smoothed version of histogram bins
     1311    const psVector *bounds = histogram->bounds; // The bounds for the histogram bins
     1312
     1313    if (!histogram->uniform) {
     1314        //
     1315        // We get here if the histogram is non-uniform.  This code is not tested.
     1316        // However, it is also not used anywhere, yet.
     1317        //
     1318        psLogMsg(__func__, PS_LOG_WARN, "WARNING: vectorSmoothHistGaussian() on non-uniform histograms has not been tested or used.\n");
     1319
     1320        for (long i = 0; i < numBins; i++) {
     1321            // Determine the midpoint of bin i.
     1322            float iMid = PS_BIN_MIDPOINT(histogram, i);
     1323
     1324            //
     1325            // We determine the bin numbers (jMin:jMax) corresponding to a
     1326            // range of data values surrounding iMid.  The range is of size:
     1327            // 2*PS_GAUSS_WIDTH*sigma
     1328            long jMin, jMax;
     1329            psF32 x = iMid - (PS_GAUSS_WIDTH * sigma);
     1330            for (jMin = i; jMin > 0 && bounds->data.F32[jMin] > x; jMin--)
     1331                ;
     1332            x = iMid + (PS_GAUSS_WIDTH * sigma);
     1333            for (jMax = i; jMax < bounds->n - 1 && bounds->data.F32[jMax + 1] > x; jMax++)
     1334                ;
     1335
     1336            //
     1337            // Loop from jMin to jMax, computing the gaussian of data i.
     1338            //
     1339            smooth->data.F32[i] = 0.0;
     1340            for (long j = jMin ; j <= jMax ; j++) {
     1341                float jMid = PS_BIN_MIDPOINT(histogram, j);
     1342                smooth->data.F32[i] +=
     1343                    histogram->nums->data.F32[j] * psGaussian(jMid, iMid, sigma, true);
     1344            }
     1345        }
     1346    } else {
     1347        //
     1348        // We get here if the histogram is uniform.
     1349        //
     1350        for (long i = 0; i < numBins; i++) {
     1351            psF32 binSize = bounds->data.F32[1] - bounds->data.F32[0];
     1352            psS32 gaussWidth = ((PS_GAUSS_WIDTH * sigma) / binSize);
     1353
     1354            //
     1355            // XXX: The following is wrong.  However, in practice, the sigma was too
     1356            // large compared to the binSize.  This meant that the smoothing was done
     1357            // over 500 bins in the robust stats algorithm.  This mean that the smoothing
     1358            // took way too long.
     1359            //
     1360            #if 0
     1361
     1362            gaussWidth = 10;
     1363            #endif
     1364            //
     1365            // We determine the bin numbers (jMin:jMax) corresponding to a
     1366            // range of data values surrounding iMid.  The range is of size:
     1367            // 2*PS_GAUSS_WIDTH*sigma
     1368            //
     1369            psS32 jMin = PS_MAX(i - gaussWidth, 0);
     1370            psS32 jMax = PS_MIN(i + gaussWidth, bounds->n - 1);
     1371
     1372            //
     1373            // Loop from jMin to jMax, computing the gaussian of data i.
     1374            //
     1375            smooth->data.F32[i] = 0.0;
     1376            float iMid = PS_BIN_MIDPOINT(histogram, i);
     1377            for (long j = jMin ; j <= jMax ; j++) {
     1378                float jMid = PS_BIN_MIDPOINT(histogram, j);
     1379                smooth->data.F32[i] +=
     1380                    histogram->nums->data.F32[j] * psGaussian(jMid, iMid, sigma, true);
     1381            }
     1382        }
     1383    }
     1384
     1385    if (psTraceGetLevel("psLib.math") >= 8) {
     1386        PS_VECTOR_PRINT_F32(smooth);
     1387    }
     1388    psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
     1389    return(smooth);
     1390}
     1391
    16181392/*****************************************************************************/
    16191393
     
    16211395
    16221396/*****************************************************************************/
    1623 
    1624 static void histogramFree(psHistogram* myHist);
    16251397
    16261398// We keep statsFree so that we can identify statistics pointers from the memblock
     
    16321404/******************************************************************************
    16331405    psStatsAlloc(): This routine must create a new psStats data structure.
    1634  *****************************************************************************/
     1406*****************************************************************************/
    16351407psStats* psStatsAlloc(psStatsOptions options)
    16361408{
     
    16751447}
    16761448
    1677 
    1678 
    1679 /******************************************************************************
    1680 psHistogramAlloc(lower, upper, n): allocate a uniform histogram structure
    1681 with the specifed upper and lower limits, and the specifed number of bins.
    1682 This routine will also set the bounds for each of the bins.
    1683  
    1684 Input:
    1685     lower
    1686     upper
    1687     n
    1688 Returns:
    1689     The histogram structure
    1690  *****************************************************************************/
    1691 psHistogram* psHistogramAlloc(float lower, float upper, int n)
    1692 {
    1693     psTrace("psLib.math", 3, "---- %s() begin  ----\n", __func__);
    1694     psTrace("psLib.math", 5, "(lower, upper, n) is (%f, %f, %d)\n", lower, upper, n);
    1695     PS_ASSERT_INT_POSITIVE(n, NULL);
    1696     PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(upper, lower, NULL);
    1697 
    1698     // Allocate memory for the new histogram structure.  If there are N bins, then there are N+1 bounds to
    1699     // those bins.
    1700     psHistogram *newHist = (psHistogram* ) psAlloc(sizeof(psHistogram)); // The new histogram structure
    1701     psMemSetDeallocator(newHist, (psFreeFunc) histogramFree);
    1702     psVector* newBounds = psVectorAlloc(n + 1, PS_TYPE_F32);
    1703     newHist->bounds = newBounds;
    1704 
    1705     // Calculate the bounds for each bin.
    1706     psF32 binSize = (upper - lower) / (psF32)n; // The histogram bin size
    1707     // XXX: Is the following necessary? It prevents the max data point from being in a non-existant bin.
    1708     binSize += FLT_EPSILON;
    1709     for (long i = 0; i < n + 1; i++) {
    1710         newBounds->data.F32[i] = lower + (binSize * (psF32)i);
    1711     }
    1712 
    1713     // Allocate the bins, and initialize them to zero.
    1714     newHist->nums = psVectorAlloc(n, PS_TYPE_F32);
    1715     psVectorInit(newHist->nums, 0.0);
    1716 
    1717     // Initialize the other members.
    1718     newHist->minNum = 0;
    1719     newHist->maxNum = 0;
    1720     newHist->uniform = true;
    1721 
    1722     psTrace("psLib.math", 3, "---- %s() end  ----\n", __func__);
    1723     return newHist;
    1724 }
    1725 
    1726 /******************************************************************************
    1727 psHistogramAllocGeneric(bounds): allocate a non-uniform histogram structure
    1728 with the specifed bounds.
    1729  
    1730 Input:
    1731     bounds
    1732 Returns:
    1733     The histogram structure
    1734  *****************************************************************************/
    1735 psHistogram* psHistogramAllocGeneric(const psVector* bounds)
    1736 {
    1737     psTrace("psLib.math", 3, "---- %s() begin  ----\n", __func__);
    1738     PS_ASSERT_VECTOR_NON_NULL(bounds, NULL);
    1739     PS_ASSERT_VECTOR_TYPE(bounds, PS_TYPE_F32, NULL);
    1740     PS_ASSERT_LONG_LARGER_THAN_OR_EQUAL(bounds->n, (long)2, NULL);
    1741 
    1742     // Allocate memory for the new histogram structure.
    1743     psHistogram *newHist = (psHistogram* ) psAlloc(sizeof(psHistogram)); // The new histogram structure
    1744     psMemSetDeallocator(newHist, (psFreeFunc) histogramFree);
    1745     psVector* newBounds = psVectorCopy(NULL, bounds, PS_TYPE_F32);
    1746     newHist->bounds = newBounds;
    1747 
    1748     // Allocate the bins, and initialize them to zero.  If there are N bounds,
    1749     // then there are N-1 bins.
    1750     newHist->nums = psVectorAlloc((bounds->n) - 1, PS_TYPE_F32);
    1751     psVectorInit(newHist->nums, 0.0);
    1752 
    1753     // Initialize the other members.
    1754     newHist->minNum = 0;
    1755     newHist->maxNum = 0;
    1756     newHist->uniform = false;
    1757 
    1758     psTrace("psLib.math", 3, "---- %s() end  ----\n", __func__);
    1759     return (newHist);
    1760 }
    1761 
    1762 static void histogramFree(psHistogram* myHist)
    1763 {
    1764     psFree(myHist->bounds);
    1765     psFree(myHist->nums);
    1766 }
    1767 
    1768 
    1769 bool psMemCheckHistogram(psPtr ptr)
    1770 {
    1771     PS_ASSERT_PTR(ptr, false);
    1772     return ( psMemGetDeallocator(ptr) == (psFreeFunc)histogramFree );
    1773 }
    1774 
    1775 
    1776 
    1777 /*****************************************************************************
    1778 UpdateHistogramBins(binNum, out, data, error): This routine is to be used when
    1779 updating the histogram in the presence of errors in the input data.  We treat
    1780 the data point as a boxcar PDF and update a range of points surrounding the
    1781 histogram bin which contains the point.  The width of that boxcar is defined
    1782 as 2.35 * error.
    1783  
    1784 XXX: Must test this.
    1785  *****************************************************************************/
    1786 static bool UpdateHistogramBins(long binNum, // Bin number of the data point
    1787                                 psHistogram* out, // The histogram to be updated
    1788                                 psF32 data, // The data point value
    1789                                 psF32 error // The error in the data point
    1790                                )
    1791 {
    1792     psTrace("psLib.math", 3, "---- %s() begin  ----\n", __func__);
    1793     PS_ASSERT_PTR_NON_NULL(out, false);
    1794     PS_ASSERT_PTR_NON_NULL(out->bounds, false);
    1795     PS_ASSERT_PTR_NON_NULL(out->nums, false);
    1796     PS_ASSERT_LONG_WITHIN_RANGE(binNum, (long)0, (long)((out->nums->n)-1), false);
    1797     PS_ASSERT_FLOAT_LARGER_THAN_OR_EQUAL(error, 0.0, false);
    1798     PS_ASSERT_FLOAT_WITHIN_RANGE(data, out->bounds->data.F32[0],
    1799                                  out->bounds->data.F32[(out->bounds->n)-1], false);
    1800 
    1801     psF32 boxcarWidth = 2.35 * error;   // Width of the boxcar
    1802     psF32 boxcarCenter = (out->bounds->data.F32[binNum] +
    1803                           out->bounds->data.F32[binNum+1]) / 2.0; // Centre of the boxcar
    1804     psF32 boxcarLeft = boxcarCenter - (boxcarWidth / 2.0); // Left endpoint of the boxcar for the PDF
    1805     psF32 boxcarRight = boxcarCenter + (boxcarWidth / 2.0); // Right endpoint of the boxcar for the PDF
    1806     psS32 boxcarLeftBinNum = 0;         // Bin number for left endpoint
    1807     psS32 boxcarRightBinNum = 0;        // Bin number for right endpoint
    1808 
    1809     // Determine the left endpoint of the boxcar for the PDF.
    1810     for (long bin = binNum; bin >= 0; bin--) {
    1811         if (out->nums->data.F32[bin] <= boxcarLeft) {
    1812             boxcarLeftBinNum = bin;
    1813             break;
    1814         }
    1815     }
    1816 
    1817     // Determine the right endpoint of the boxcar for the PDF.
    1818     for (long bin = binNum; bin < out->nums->n; bin++) {
    1819         if (out->nums->data.F32[bin] >= boxcarRight) {
    1820             boxcarRightBinNum = bin;
    1821             break;
    1822         }
    1823     }
    1824 
    1825     // If the boxcar fits entirely inside this bin, then simply add 1.0 to the
    1826     // bin and return.
    1827     if (boxcarLeftBinNum == boxcarRightBinNum) {
    1828         out->nums->data.F32[binNum] += 1.0;
    1829         psTrace("psLib.math", 3, "---- %s(true) end  ----\n", __func__);
    1830         return true;
    1831     }
    1832 
    1833     // If we get here, multiple bins must be updated.  We handle the left-most endpoint, and right-most
    1834     // endpoints differently.
    1835     out->nums->data.F32[boxcarLeftBinNum] +=
    1836         (out->bounds->data.F32[boxcarLeftBinNum+1] - boxcarLeft) / boxcarWidth;
    1837 
    1838     // Loop through the center bins, if any.
    1839     for (long bin = boxcarLeftBinNum + 1; bin < (boxcarRightBinNum - 1); bin++) {
    1840         out->nums->data.F32[bin] +=
    1841             (out->bounds->data.F32[bin+1] - out->bounds->data.F32[bin]) / boxcarWidth;
    1842     }
    1843 
    1844     // Handle the right endpoint differently.
    1845     out->nums->data.F32[boxcarRightBinNum]+=
    1846         (boxcarRight - out->bounds->data.F32[boxcarRightBinNum]) / boxcarWidth;
    1847 
    1848     psTrace("psLib.math", 3, "---- %s(true) end  ----\n", __func__);
    1849     return true;
    1850 }
    1851 
    1852 
    1853 /*****************************************************************************
    1854 psVectorHistogram(out, in, errors, mask, maskVal): this procedure takes as
    1855 input a preallocated and initialized histogram structure.  It fills the bins
    1856 in that histogram structure in accordance with the input data "in" and the,
    1857 possibly NULL, mask vector.
    1858  
    1859 Inputs:
    1860     out
    1861     in
    1862     mask
    1863     maskVal
    1864 Returns:
    1865     The histogram structure "out".
    1866  *****************************************************************************/
    1867 psHistogram* psVectorHistogram(psHistogram* out,
    1868                                const psVector* values,
    1869                                const psVector* errors,
    1870                                const psVector* mask,
    1871                                psMaskType maskVal)
    1872 {
    1873     psTrace("psLib.math", 3, "---- %s() begin  ----\n", __func__);
    1874     PS_ASSERT_PTR_NON_NULL(out, NULL);
    1875     PS_ASSERT_VECTOR_NON_NULL(out->bounds, NULL);
    1876     PS_ASSERT_VECTOR_TYPE(out->bounds, PS_TYPE_F32, NULL);
    1877     PS_ASSERT_INT_NONNEGATIVE(out->bounds->n, NULL);
    1878     PS_ASSERT_VECTOR_NON_NULL(out->nums, NULL);
    1879     PS_ASSERT_VECTOR_TYPE(out->nums, PS_TYPE_F32, NULL);
    1880     PS_ASSERT_INT_NONNEGATIVE(out->nums->n, NULL);
    1881     PS_ASSERT_VECTOR_NON_NULL(values, out);
    1882     if (mask) {
    1883         PS_ASSERT_VECTORS_SIZE_EQUAL(values, mask, NULL);
    1884         PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_U8, NULL);
    1885     }
    1886     if (errors) {
    1887         PS_ASSERT_VECTORS_SIZE_EQUAL(values, errors, NULL);
    1888         PS_ASSERT_VECTOR_TYPE(errors, values->type.type, NULL);
    1889     }
    1890 
    1891     long binNum = 0;                    // A temporary bin number
    1892     long numBins = out->nums->n;        // The total number of bins
    1893     psScalar tmpScalar;
    1894     tmpScalar.type.type = PS_TYPE_F32;
    1895 
    1896     // Convert input and errors vectors to F32 if necessary.
    1897     psVector* inF32 = NULL;             // F32 version of input vector
    1898     if (values->type.type == PS_TYPE_F32) {
    1899         inF32 = psMemIncrRefCounter((psPtr)values);
    1900     } else {
    1901         inF32 = psVectorCopy(NULL, values, PS_TYPE_F32);
    1902     }
    1903     psVector* errorsF32 = NULL;         // F32 version of errors vector
    1904     if (errors) {
    1905         if (errors->type.type == PS_TYPE_F32) {
    1906             errorsF32 = psMemIncrRefCounter((psPtr)errors);
    1907         } else {
    1908             errorsF32 = psVectorCopy(NULL, errors, PS_TYPE_F32);
    1909         }
    1910     }
    1911 
    1912     for (long i = 0; i < inF32->n; i++) {
    1913         // Check if this pixel is masked, and if so, skip it.
    1914         if (!mask || (mask && (!(mask->data.U8[i] & maskVal)))) {
    1915             if (inF32->data.F32[i] < out->bounds->data.F32[0]) {
    1916                 // If this pixel is below minimum value, count it, then skip.
    1917                 out->minNum++;
    1918             } else if (inF32->data.F32[i] > out->bounds->data.F32[numBins]) {
    1919                 // If this pixel is above maximum value, count it, then skip.
    1920                 out->maxNum++;
    1921             } else {
    1922                 // If this is a uniform histogram, determining the correct
    1923                 // number is trivial.
    1924                 if (out->uniform == true) {
    1925                     float binSize = out->bounds->data.F32[1] - out->bounds->data.F32[0]; // Histogram bin size
    1926                     binNum = (inF32->data.F32[i] - out->bounds->data.F32[0]) / binSize;
    1927                     if (errorsF32) {
    1928                         if (!UpdateHistogramBins(binNum, out, inF32->data.F32[i], errorsF32->data.F32[i])) {
    1929                             psLogMsg(__func__, PS_LOG_WARN, "WARNING: Failed to update the histogram "
    1930                                      "bins with the errors vector.\n");
    1931                         }
    1932                     } else {
    1933                         // This if-statement really shouldn't be necessary.
    1934                         // However, due to numerical lack of precision, we
    1935                         // occasionally produce a binNum outside the range.
    1936                         if (binNum >= out->nums->n) {
    1937                             binNum = out->nums->n - 1;
    1938                         }
    1939                         (out->nums->data.F32[binNum])+= 1.0;
    1940                     }
    1941 
    1942                 } else {
    1943                     // If this is a non-uniform histogram, determining the
    1944                     // correct bin number requires a bit more work.
    1945                     tmpScalar.data.F32 = inF32->data.F32[i];
    1946                     binNum = p_psVectorBinDisect( *(psVector* *)&out->bounds, &tmpScalar);
    1947                     if (binNum < 0) {
    1948                         psLogMsg(__func__, PS_LOG_WARN,
    1949                                  "WARNING: psVectorHistogram(): element outside histogram bounds.\n");
    1950                     } else {
    1951                         if (errorsF32 != NULL) {
    1952                             if (!UpdateHistogramBins(binNum, out, inF32->data.F32[i], errors->data.F32[i])) {
    1953                                 psLogMsg(__func__, PS_LOG_WARN, "WARNING: Failed to update the histogram "
    1954                                          "bins with the errors vector.\n");
    1955                             }
    1956                         } else {
    1957                             out->nums->data.F32[binNum] += 1.0;
    1958                         }
    1959                     }
    1960                 }
    1961             }
    1962         }
    1963     }
    1964 
    1965     psFree(inF32);
    1966     psFree(errorsF32);
    1967 
    1968     psTrace("psLib.math", 3, "---- %s() end  ----\n", __func__);
    1969     return (out);
    1970 }
    1971 
    19721449/******************************************************************************
    19731450psVectorStats(in, mask, maskVal, stats): this is the public API
     
    19841461 
    19851462XXX: Should we free stats if the asserts fail? NO; we don't own it (RHL).
    1986  *****************************************************************************/
    1987 psStats* psVectorStats(
    1988     psStats* stats,
    1989     const psVector* in,
    1990     const psVector* errors,
    1991     const psVector* mask,
    1992     psMaskType maskVal)
     1463*****************************************************************************/
     1464bool psVectorStats(psStats* stats,
     1465                   const psVector* in,
     1466                   const psVector* errors,
     1467                   const psVector* mask,
     1468                   psMaskType maskVal)
    19931469{
    19941470    psTrace("psLib.math", 3,"---- %s() begin  ----\n", __func__);
     
    20361512    }
    20371513
     1514    // init the value of stats->results: this is used internally to check if
     1515    // prior functions have been called
     1516    stats->results = PS_STAT_NONE;
     1517    bool status = true;
     1518
    20381519    // ************************************************************************
    20391520    if (stats->options & PS_STAT_SAMPLE_MEAN) {
    20401521        if (!vectorSampleMean(inF32, errorsF32, maskU8, maskVal, stats)) {
    2041             psLogMsg(__func__, PS_LOG_WARN, "WARNING: vectorSampleMean() returned an error.\n");
    2042             stats->sampleMean = NAN;
     1522            psError(PS_ERR_UNKNOWN, false, "Failed to calculate vector sample mean");
     1523            status &= false;
    20431524        }
    20441525    }
     
    20471528    if (stats->options & (PS_STAT_SAMPLE_MEDIAN | PS_STAT_SAMPLE_QUARTILE)) {
    20481529        if (!vectorSampleMedian(inF32, maskU8, maskVal, stats)) {
    2049             psLogMsg(__func__, PS_LOG_WARN, "WARNING: vectorSampleMedian() returned an error.\n");
    2050             stats->sampleMedian = NAN;
    2051             stats->sampleUQ = NAN;
    2052             stats->sampleLQ = NAN;
     1530            psError(PS_ERR_UNKNOWN, false, "Failed to calculate sample median");
     1531            status &= false;
    20531532        }
    20541533    }
     
    20561535    // ************************************************************************
    20571536    if (stats->options & PS_STAT_SAMPLE_STDEV) {
    2058         if (!vectorSampleMean(inF32, errorsF32, maskU8, maskVal, stats)) {
    2059             psLogMsg(__func__, PS_LOG_WARN, "WARNING: vectorSampleMean() returned an error.\n");
    2060             stats->sampleMean = NAN;
    2061         } else {
    2062             vectorSampleStdev(inF32, errorsF32, maskU8, maskVal, stats);
    2063         }
    2064     }
    2065 
    2066     // ************************************************************************
    2067     if (stats->options & (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE |
    2068                           PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV)) {
    2069         if (!vectorRobustStats(inF32, errorsF32, maskU8, maskVal, stats)) {
    2070             psError(PS_ERR_UNKNOWN, false, _("Failed to calculate the specified statistic."));
    2071             psFree(inF32);
    2072             psFree(errorsF32);
    2073             psTrace("psLib.math", 3,"---- %s(NULL) end  ----\n", __func__);
    2074             return(NULL);
    2075         }
    2076     }
    2077 
    2078     // ************************************************************************
    2079     if ((stats->options & PS_STAT_CLIPPED_MEAN) || (stats->options & PS_STAT_CLIPPED_STDEV)) {
    2080         if (!vectorClippedStats(inF32, errorsF32, maskU8, maskVal, stats)) {
    2081             psError(PS_ERR_UNKNOWN, false, "Failed to calculate clipped statistics for input psVector.\n");
    2082             stats->clippedMean = NAN;
    2083             stats->clippedStdev = NAN;
     1537        if (!vectorSampleStdev(inF32, errorsF32, maskU8, maskVal, stats)) {
     1538            psError(PS_ERR_UNKNOWN, false, "Failed to calculate sample stdev");
     1539            status &= false;
    20841540        }
    20851541    }
     
    20891545        if (vectorMinMax(inF32, maskU8, maskVal, stats) == 0) {
    20901546            psError(PS_ERR_UNKNOWN, false, "Failed to calculate vector minimum and maximum");
     1547            status &= false;
     1548        }
     1549    }
     1550
     1551    // ************************************************************************
     1552    if (stats->options & (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE)) {
     1553        if (!vectorRobustStats(inF32, errorsF32, maskU8, maskVal, stats)) {
     1554            psError(PS_ERR_UNKNOWN, false, _("Failed to calculate robust statistics"));
     1555            status &= false;
     1556        }
     1557    }
     1558
     1559    // ************************************************************************
     1560    if (stats->options & (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV)) {
     1561        if (!vectorFittedStats(inF32, errorsF32, maskU8, maskVal, stats)) {
     1562            psError(PS_ERR_UNKNOWN, false, _("Failed to calculate fitted statistics"));
     1563            status &= false;
     1564        }
     1565    }
     1566
     1567    // ************************************************************************
     1568    if (stats->options & (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2)) {
     1569        if (stats->options & (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV)) {
     1570            psAbort ("stats", "you may not specify both FITTED_MEAN and FITTED_MEAN_V2");
     1571        }
     1572        if (!vectorFittedStats_v2(inF32, errorsF32, maskU8, maskVal, stats)) {
     1573            psError(PS_ERR_UNKNOWN, false, _("Failed to calculate fitted statistics"));
     1574            status &= false;
     1575        }
     1576    }
     1577
     1578    // ************************************************************************
     1579    if ((stats->options & PS_STAT_CLIPPED_MEAN) || (stats->options & PS_STAT_CLIPPED_STDEV)) {
     1580        if (!vectorClippedStats(inF32, errorsF32, maskU8, maskVal, stats)) {
     1581            psError(PS_ERR_UNKNOWN, false, "Failed to calculate clipped statistics\n");
     1582            status &= false;
    20911583        }
    20921584    }
     
    20961588    psFree(maskU8);
    20971589    psTrace("psLib.math", 3,"---- %s() end  ----\n", __func__);
    2098     return (stats);
     1590    return (status);
    20991591}
    21001592
     
    21061598    }
    21071599
    2108     READ_STAT("MEAN",     PS_STAT_SAMPLE_MEAN);
    2109     READ_STAT("STDEV",    PS_STAT_SAMPLE_STDEV);
    2110     READ_STAT("MEDIAN",   PS_STAT_SAMPLE_MEDIAN);
    2111     READ_STAT("QUARTILE", PS_STAT_SAMPLE_QUARTILE);
     1600    READ_STAT("MEAN",       PS_STAT_SAMPLE_MEAN);
     1601    READ_STAT("STDEV",      PS_STAT_SAMPLE_STDEV);
     1602    READ_STAT("MEDIAN",     PS_STAT_SAMPLE_MEDIAN);
     1603    READ_STAT("QUARTILE",   PS_STAT_SAMPLE_QUARTILE);
    21121604    READ_STAT("SAMPLE_MEAN",     PS_STAT_SAMPLE_MEAN);
    21131605    READ_STAT("SAMPLE_STDEV",    PS_STAT_SAMPLE_STDEV);
     
    21181610    READ_STAT("ROBUST_STDEV",    PS_STAT_ROBUST_STDEV);
    21191611    READ_STAT("ROBUST_QUARTILE", PS_STAT_ROBUST_QUARTILE);
    2120     READ_STAT("FITTED",       PS_STAT_FITTED_MEAN);
    2121     READ_STAT("FITTED_MEAN",  PS_STAT_FITTED_MEAN);
    2122     READ_STAT("FITTED_STDEV", PS_STAT_ROBUST_STDEV);
    2123     READ_STAT("CLIPPED",       PS_STAT_CLIPPED_MEAN);
    2124     READ_STAT("CLIPPED_MEAN",  PS_STAT_CLIPPED_MEAN);
    2125     READ_STAT("CLIPPED_STDEV", PS_STAT_CLIPPED_STDEV);
     1612    READ_STAT("FITTED",         PS_STAT_FITTED_MEAN);
     1613    READ_STAT("FITTED_MEAN",    PS_STAT_FITTED_MEAN);
     1614    READ_STAT("FITTED_STDEV",   PS_STAT_FITTED_STDEV);
     1615    READ_STAT("FITTED_V2",       PS_STAT_FITTED_MEAN_V2);
     1616    READ_STAT("FITTED_MEAN_V2",  PS_STAT_FITTED_MEAN_V2);
     1617    READ_STAT("FITTED_STDEV_V2", PS_STAT_FITTED_STDEV_V2);
     1618    READ_STAT("CLIPPED",         PS_STAT_CLIPPED_MEAN);
     1619    READ_STAT("CLIPPED_MEAN",    PS_STAT_CLIPPED_MEAN);
     1620    READ_STAT("CLIPPED_STDEV",   PS_STAT_CLIPPED_STDEV);
    21261621
    21271622    psError(PS_ERR_BAD_PARAMETER_VALUE, true, "Unable to parse statistic: %s\n", string);
     
    21461641    WRITE_STAT("ROBUST_STDEV",    PS_STAT_ROBUST_STDEV);
    21471642    WRITE_STAT("ROBUST_QUARTILE", PS_STAT_ROBUST_QUARTILE);
    2148     WRITE_STAT("FITTED_MEAN",  PS_STAT_FITTED_MEAN);
    2149     WRITE_STAT("FITTED_STDEV", PS_STAT_ROBUST_STDEV);
    2150     WRITE_STAT("CLIPPED_MEAN",  PS_STAT_CLIPPED_MEAN);
    2151     WRITE_STAT("CLIPPED_STDEV", PS_STAT_CLIPPED_STDEV);
     1643    WRITE_STAT("FITTED_MEAN",     PS_STAT_FITTED_MEAN);
     1644    WRITE_STAT("FITTED_STDEV",    PS_STAT_FITTED_STDEV);
     1645    WRITE_STAT("FITTED_MEAN_V2",  PS_STAT_FITTED_MEAN_V2);
     1646    WRITE_STAT("FITTED_STDEV_V2", PS_STAT_FITTED_STDEV_V2);
     1647    WRITE_STAT("CLIPPED_MEAN",    PS_STAT_CLIPPED_MEAN);
     1648    WRITE_STAT("CLIPPED_STDEV",   PS_STAT_CLIPPED_STDEV);
    21521649
    21531650    return string;
     
    21971694    case PS_STAT_FITTED_MEAN:
    21981695    case PS_STAT_FITTED_STDEV:
     1696    case PS_STAT_FITTED_MEAN_V2:
     1697    case PS_STAT_FITTED_STDEV_V2:
    21991698    case PS_STAT_CLIPPED_MEAN:
    22001699    case PS_STAT_CLIPPED_STDEV:
     
    22271726    case PS_STAT_FITTED_STDEV:
    22281727        return stats->fittedStdev;
     1728    case PS_STAT_FITTED_MEAN_V2:
     1729        return stats->fittedMean;
     1730    case PS_STAT_FITTED_STDEV_V2:
     1731        return stats->fittedStdev;
    22291732    case PS_STAT_CLIPPED_MEAN:
    22301733        return stats->clippedMean;
     
    22461749    return NAN;
    22471750}
     1751
     1752// other private functions used above
     1753
     1754static psF32 QuadraticInverse(psF32 a,
     1755                              psF32 b,
     1756                              psF32 c,
     1757                              psF32 y,
     1758                              psF32 xLo,
     1759                              psF32 xHi
     1760                             )
     1761{
     1762    psF64 tmp = sqrt((y - c)/a + (b*b)/(4.0 * a * a));
     1763
     1764    psF64 x1 = -b/(2.0*a) + tmp;
     1765    psF64 x2 = -b/(2.0*a) - tmp;
     1766
     1767    if (xLo <= x1 && x1 <= xHi) {
     1768        return x1;
     1769    }
     1770    if (xLo <= x2 && x2 <= xHi) {
     1771        return x2;
     1772    }
     1773    return 0.5 * (xLo + xHi);
     1774}
     1775
     1776
     1777/******************************************************************************
     1778fitQuadraticSearchForYThenReturnX(*xVec, *yVec, binNum, yVal): A general
     1779routine which fits a quadratic to three points and returns the x-value
     1780corresponding to the input y-value.  This routine takes psVectors of x/y pairs
     1781as input, and fits a quadratic to the 3 points surrounding element binNum in
     1782the vectors (the midpoint between element i and i+1 is used for x[i]).  It
     1783then determines for what value x does that quadratic f(x) = yVal (the input
     1784parameter).
     1785 
     1786*****************************************************************************/
     1787static psF32 fitQuadraticSearchForYThenReturnX(const psVector *xVec,
     1788        psVector *yVec,
     1789        psS32 binNum,
     1790        psF32 yVal
     1791                                              )
     1792{
     1793    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
     1794    psTrace("psLib.math", 5, "binNum, yVal is (%d, %f)\n", binNum, yVal);
     1795    if (psTraceGetLevel("psLib.math") >= 8) {
     1796        PS_VECTOR_PRINT_F32(xVec);
     1797        PS_VECTOR_PRINT_F32(yVec);
     1798    }
     1799
     1800    PS_ASSERT_VECTOR_NON_NULL(xVec, NAN);
     1801    PS_ASSERT_VECTOR_NON_NULL(yVec, NAN);
     1802    PS_ASSERT_VECTOR_TYPE(xVec, PS_TYPE_F32, NAN);
     1803    PS_ASSERT_VECTOR_TYPE(yVec, PS_TYPE_F32, NAN);
     1804    //    PS_ASSERT_VECTORS_SIZE_EQUAL(xVec, yVec, NAN);
     1805    PS_ASSERT_INT_WITHIN_RANGE(binNum, 0, (int)(xVec->n - 1), NAN);
     1806    PS_ASSERT_INT_WITHIN_RANGE(binNum, 0, (int)(yVec->n - 1), NAN);
     1807
     1808    psVector *x = psVectorAlloc(3, PS_TYPE_F64);
     1809    psVector *y = psVectorAlloc(3, PS_TYPE_F64);
     1810    psF32 tmpFloat = 0.0f;
     1811
     1812    if ((binNum >= 1) && (binNum < (yVec->n - 2)) && (binNum < (xVec->n - 2))) {
     1813        // The general case.  We have all three points.
     1814        x->data.F64[0] = (psF64) (0.5 * (xVec->data.F32[binNum - 1] + xVec->data.F32[binNum]));
     1815        x->data.F64[1] = (psF64) (0.5 * (xVec->data.F32[binNum] + xVec->data.F32[binNum+1]));
     1816        x->data.F64[2] = (psF64) (0.5 * (xVec->data.F32[binNum+1] + xVec->data.F32[binNum+2]));
     1817        y->data.F64[0] = yVec->data.F32[binNum - 1];
     1818        y->data.F64[1] = yVec->data.F32[binNum];
     1819        y->data.F64[2] = yVec->data.F32[binNum + 1];
     1820        psTrace("psLib.math", 6, "x vec (orig) is (%f %f %f %f)\n", xVec->data.F32[binNum - 1],
     1821                xVec->data.F32[binNum], xVec->data.F32[binNum+1], xVec->data.F32[binNum+2]);
     1822        psTrace("psLib.math", 6, "x data is (%f %f %f)\n", x->data.F64[0], x->data.F64[1], x->data.F64[2]);
     1823        psTrace("psLib.math", 6, "y data is (%f %f %f)\n", y->data.F64[0], y->data.F64[1], y->data.F64[2]);
     1824
     1825        //
     1826        // Ensure that the y value lies within range of the y values.
     1827        //
     1828        if (! (((y->data.F64[0] <= yVal) && (yVal <= y->data.F64[2])) ||
     1829                ((y->data.F64[2] <= yVal) && (yVal <= y->data.F64[0]))) ) {
     1830            psError(PS_ERR_BAD_PARAMETER_VALUE, true,
     1831                    _("Specified yVal, %g, is not within y-range, %g to %g."),
     1832                    (psF64)yVal, y->data.F64[0], y->data.F64[2]);
     1833        }
     1834
     1835        //
     1836        // Ensure that the y values are monotonic.
     1837        //
     1838        if (((y->data.F64[0] < y->data.F64[1]) && !(y->data.F64[1] <= y->data.F64[2])) ||
     1839                ((y->data.F64[0] > y->data.F64[1]) && !(y->data.F64[1] >= y->data.F64[2]))) {
     1840            psError(PS_ERR_UNKNOWN, true,
     1841                    "This routine must be called with monotonically increasing or decreasing data points.\n");
     1842            psFree(x);
     1843            psFree(y);
     1844            psTrace("psLib.math", 5, "---- %s() end ----\n", __func__);
     1845            return NAN;
     1846        }
     1847
     1848        // Determine the coefficients of the polynomial.
     1849        psPolynomial1D *myPoly = psPolynomial1DAlloc(PS_POLYNOMIAL_ORD, 2);
     1850        myPoly = psVectorFitPolynomial1D(myPoly, NULL, 0, y, NULL, x);
     1851        if (myPoly == NULL) {
     1852            psError(PS_ERR_UNEXPECTED_NULL, false,
     1853                    _("Failed to fit a 1-dimensional polynomial to the three specified data points.  Returning NAN."));
     1854            psFree(x);
     1855            psFree(y);
     1856            psTrace("psLib.math", 5, "---- %s(NAN) end ----\n", __func__);
     1857            return NAN;
     1858        }
     1859        psTrace("psLib.math", 6, "myPoly->coeff[0] is %f\n", myPoly->coeff[0]);
     1860        psTrace("psLib.math", 6, "myPoly->coeff[1] is %f\n", myPoly->coeff[1]);
     1861        psTrace("psLib.math", 6, "myPoly->coeff[2] is %f\n", myPoly->coeff[2]);
     1862        psTrace("psLib.math", 6, "Fitted y vec is (%f %f %f)\n",
     1863                (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[0]),
     1864                (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[1]),
     1865                (psF32) psPolynomial1DEval(myPoly, (psF64) x->data.F64[2]));
     1866
     1867        psTrace("psLib.math", 6, "We fit the polynomial, now find x such that f(x) equals %f\n", yVal);
     1868        tmpFloat = QuadraticInverse(myPoly->coeff[2], myPoly->coeff[1], myPoly->coeff[0], yVal,
     1869                                    x->data.F64[0], x->data.F64[2]);
     1870        psFree(myPoly);
     1871
     1872        if (isnan(tmpFloat)) {
     1873            psError(PS_ERR_UNEXPECTED_NULL,
     1874                    false, _("Failed to determine the median of the fitted polynomial.  Returning NAN."));
     1875            psFree(x);
     1876            psFree(y);
     1877            psTrace("psLib.math", 5, "---- %s(NAN) end ----\n", __func__);
     1878            return(NAN);
     1879        }
     1880    } else {
     1881        // These are special cases where the bin is at the beginning or end of the vector.
     1882        if (binNum == 0) {
     1883            // We have two points only at the beginning of the vectors x and y.
     1884            tmpFloat = 0.5 * (xVec->data.F32[binNum] +
     1885                              xVec->data.F32[binNum + 1]);
     1886        } else if (binNum == (xVec->n - 1)) {
     1887            // The special case where we have two points only at the end of
     1888            // the vectors x and y.
     1889            // XXX: Is this right?
     1890            tmpFloat = xVec->data.F32[binNum];
     1891        } else if (binNum == (xVec->n - 2)) {
     1892            // XXX: Is this right?
     1893            tmpFloat = 0.5 * (xVec->data.F32[binNum] + xVec->data.F32[binNum + 1]);
     1894        }
     1895    }
     1896
     1897    psTrace("psLib.math", 6, "FIT: return %f\n", tmpFloat);
     1898    psFree(x);
     1899    psFree(y);
     1900
     1901    psTrace("psLib.math", 5, "---- %s(%f) end ----\n", __func__, tmpFloat);
     1902    return tmpFloat;
     1903}
     1904
     1905/******************************************************************************
     1906NOTE: We assume unnormalized gaussians.
     1907*****************************************************************************/
     1908static psF32 minimizeLMChi2Gauss1D(psVector *deriv,
     1909                                   const psVector *params,
     1910                                   const psVector *coords
     1911                                  )
     1912{
     1913    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
     1914    PS_ASSERT_VECTOR_NON_NULL(params, NAN);
     1915    PS_ASSERT_VECTOR_SIZE(params, (long)2, NAN);
     1916    PS_ASSERT_VECTOR_TYPE(params, PS_TYPE_F32, NAN);
     1917    PS_ASSERT_VECTOR_NON_NULL(coords, NAN);
     1918    PS_ASSERT_VECTOR_SIZE(coords, (long)1, NAN);
     1919    PS_ASSERT_VECTOR_TYPE(coords, PS_TYPE_F32, NAN);
     1920
     1921    psF32 x = coords->data.F32[0];
     1922    psF32 mean = params->data.F32[0];
     1923    psF32 var = params->data.F32[1];
     1924    psF32 dx = (x - mean);
     1925
     1926    psF32 gauss = exp (-0.5*PS_SQR(dx)/var);
     1927    if (deriv) {
     1928        PS_ASSERT_VECTOR_SIZE(deriv, (long)2, NAN);
     1929        PS_ASSERT_VECTOR_TYPE(deriv, PS_TYPE_F32, NAN);
     1930        psF32 tmp = dx * gauss;
     1931        deriv->data.F32[0] = tmp / var;
     1932        deriv->data.F32[1] = tmp * dx / (var * var);
     1933    }
     1934
     1935
     1936    psTrace("psLib.math", 4, "---- %s() end ----\n", __func__);
     1937    return gauss;
     1938}
  • trunk/psLib/src/math/psStats.h

    r8468 r10550  
    77 *  on those data structures.
    88 *
    9  *  XXX: The following stats members are never used, or set in this code.
    10  *      stats->robustN50
    11  *      stats->clippedNvalues
    12  *      stats->binsize
    13  *
    149 *  @author GLG, MHPCC
    1510 *
    16  *  @version $Revision: 1.56 $ $Name: not supported by cvs2svn $
    17  *  @date $Date: 2006-08-22 15:02:08 $
     11 *  @version $Revision: 1.57 $ $Name: not supported by cvs2svn $
     12 *  @date $Date: 2006-12-08 11:38:54 $
    1813 *
    1914 *  Copyright 2004-2005 Maui High Performance Computing Center, University of Hawaii
    2015 */
     16
    2117#ifndef PS_STATS_H
    2218#define PS_STATS_H
    2319
    24 #include "psVector.h"
     20// #include "psVector.h"
    2521
    2622/// @addtogroup Stats
     
    3531 *  @see psStats, psVectorStats, psImageStats
    3632 */
    37 // XXX: Is PS_STAT_ROBUST_FOR_SAMPLE obsolete?
    3833typedef enum {
     34    PS_STAT_NONE            = 0x000000, ///< Empty set
    3935    PS_STAT_SAMPLE_MEAN     = 0x000001, ///< Sample Mean
    4036    PS_STAT_SAMPLE_MEDIAN   = 0x000002, ///< Sample Median
     
    5147    PS_STAT_MIN             = 0x001000, ///< Minumum
    5248    PS_STAT_USE_RANGE       = 0x002000, ///< Range
    53     PS_STAT_USE_BINSIZE     = 0x004000  ///< Binsize
     49    PS_STAT_USE_BINSIZE     = 0x004000, ///< Binsize
     50    PS_STAT_FITTED_MEAN_V2  = 0x008000, ///< Fitted Mean
     51    PS_STAT_FITTED_STDEV_V2 = 0x010000, ///< Fitted Standard Deviation
    5452} psStatsOptions;
    5553
     
    8179    double binsize;                    ///< binsize for robust fit (input/ouput)
    8280    long nSubsample;                   ///< maxinum number of measurements (input)
    83     psStatsOptions options;            ///< bitmask of calculated values
     81    psStatsOptions options;            ///< bitmask of values requested
     82    psStatsOptions results;            ///< bitmask of values calculated
    8483}
    8584psStats;
     
    8988 *  @return psStats*    the statistical results as specified by stats->options
    9089 */
    91 psStats* psVectorStats(
     90bool psVectorStats(
    9291    psStats* stats,                    ///< stats structure defines stats to be calculated and how
    9392    const psVector* in,                ///< Vector to be analysed.
     
    117116);
    118117
    119 
    120 /******************************************************************************
    121     Histogram functions and data structures.
    122  *****************************************************************************/
    123 
    124 /** The basic histogram structure which contains bounds and bins.
    125  *
    126  *  In this structure, the vector bounds specifies the boundaries of the
    127  *  histogram bins, and must of type psF32, while nums specifies the number
    128  *  of entries in the bin, and must of type psU32. The value of bounds.n must
    129  *  therefore be 1 greater than than nums.n. The two values minNum and maxNum
    130  *  are the number of data values which fell below the lower limit bound or
    131  *  above the upper limit bound, respectively.
    132  */
    133 typedef struct
    134 {
    135     const psVector* bounds;            ///< Bounds for the bins (type F32)
    136     psVector* nums;                    ///< Number in each of the bins (INT)
    137     int minNum;                        ///< Number below the minimum
    138     int maxNum;                        ///< Number above the maximum
    139     bool uniform;                      ///< Is it a uniform distribution?
    140 }
    141 psHistogram;
    142 
    143 /** Allocator for psHistogram where the bounds of the bins are implicitly
    144  *  specified through simply specifying an upper and lower limit along with
    145  *  the size of the bins.
    146  *
    147  *  @return psHistogram*    Newly allocated psHistogram
    148  */
    149 psHistogram* psHistogramAlloc(
    150     float lower,                       ///< Lower limit for the bins
    151     float upper,                       ///< Upper limit for the bins
    152     int n                              ///< Number of bins
    153 );
    154 
    155 
    156 /** Checks the type of a particular pointer.
    157  *
    158  *  Uses the appropriate deallocation function in psMemBlock to check the ptr datatype.
    159  *
    160  *  @return bool:       True if the pointer matches a psHistogram structure, false otherwise.
    161  */
    162 bool psMemCheckHistogram(
    163     psPtr ptr                          ///< the pointer whose type to check
    164 );
    165 
    166 
    167 /** Allocator for psHistogram where the bounds of the bins are explicitly
    168  *  specified.
    169  *
    170  *  @return psHistogram*    Newly allocated psHistogram
    171  */
    172 psHistogram* psHistogramAllocGeneric(
    173     const psVector* bounds             ///< Bounds for the bins
    174 );
    175 
    176 /** Calculate a histogram
    177  *
    178  *  The following function populates the histogram bins from the specified
    179  *  vector (in). It alters and returns the histogram out structure. The input
    180  *  vector may be of types psU8, psU16, psF32, psF64.
    181  *
    182  *  @return psHistogram*   histogram result
    183  */
    184 psHistogram* psVectorHistogram(
    185     psHistogram* out,                  ///< Histogram data
    186     const psVector* values,            ///< Vector to analyse
    187     const psVector* errors,            ///< Errors
    188     const psVector* mask,              ///< Mask dat for input vector
    189     psMaskType maskVal                 ///< Mask value
    190 );
    191 
    192 
    193118// Get the statistics option from a string
    194119psStatsOptions psStatsOptionFromString(const char *string);
     
    207132
    208133#endif // #ifndef PS_STATS_H
     134
     135/*
     136 * private stats functions used in psStats.c:
     137 *
     138 * vectorSampleMean
     139   (none)
     140 * vectorMinMax
     141   (none)
     142 * vectorSampleMedian (also yields SAMPLE_QUARTILE)
     143   (none)
     144 * vectorSampleStdev
     145   (vectorSampleMean)
     146 * vectorClippedStats
     147   (vectorSampleMedian)
     148   (vectorSampleMean (*also subset))
     149   (vectorSampleStdev (*also subset))
     150 * vectorRobustStats
     151   (vectorMinMax (*only subset))
     152 * vectorFittedStats
     153   (vectorRobustStats)
     154 
     155 * private stats functions called by other private stats functions are automatically called by
     156 * those functions.  since they set the stats->results flags, they are not called multiple
     157 * times.
     158 
     159 * the private stats functions do not test for their corresponding stats flags: it is not
     160 * necessary to request them if they are called within this function.
     161 
     162*/
  • trunk/psLib/test/math/tap_psStatsTiming.c

    r10166 r10550  
    99// ok(condition, "condition succeeded");
    1010// skip_start(condition, Nskip, "Skipping tests because of failure");
     11
     12# define DTIME(A,B) ((A.tv_sec - B.tv_sec) + 1e-6*(A.tv_usec - B.tv_usec))
     13struct timeval start, mark;
    1114
    1215int main (void)
     
    2225        rnd->data.F32[i] = psRandomGaussian (seed);
    2326    }
    24     psTimerStart ("test");
    25 
    26     /********** fitted stats ***********/
    27     // test FITTED timing
    28     {
    29         // psMemId id = psMemGetId();
    30 
    31         diag ("check fitted mean speed : 1000 pts / 1000 loops");
     27
     28    diag ("timing for sample mean");
     29    /********** SAMPLE MEAN ***********/
     30    // test stat sample mean (no mask, no range)
     31    {
     32        psMemId id = psMemGetId();
     33
     34        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN);
     35
     36        gettimeofday (&start, NULL);
     37        for (int i = 0; i < 10000; i++)
     38        {
     39            psVectorStats (stats, rnd, NULL, NULL, 0);
     40        }
     41        gettimeofday (&mark, NULL);
     42        psF64 delta = DTIME(mark, start);
     43        ok (delta < 0.1, "sample mean %f (mask: 0, range: 0): %.3f sec", stats->sampleMean, delta);
     44        psFree (stats);
     45
     46        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     47    }
     48
     49    // test stat sample mean (mask, no range)
     50    {
     51        psMemId id = psMemGetId();
     52
     53        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN);
     54        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
     55        psVectorInit (mask, 0);
     56        mask->data.U8[100] = 1;
     57        mask->data.U8[200] = 1;
     58        mask->data.U8[300] = 1;
     59
     60        gettimeofday (&start, NULL);
     61        for (int i = 0; i < 10000; i++)
     62        {
     63            psVectorStats (stats, rnd, NULL, mask, 1);
     64        }
     65        gettimeofday (&mark, NULL);
     66        psF64 delta = DTIME(mark, start);
     67        ok (delta < 0.12, "sample mean %f (mask: 1, range: 0): %.3f sec", stats->sampleMean, delta);
     68        psFree (stats);
     69        psFree (mask);
     70
     71        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     72    }
     73
     74    // test stat sample mean (no mask, range)
     75    {
     76        psMemId id = psMemGetId();
     77
     78        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE);
     79        stats->min = -10;
     80        stats->max = +10;
     81
     82        gettimeofday (&start, NULL);
     83        for (int i = 0; i < 10000; i++)
     84        {
     85            psVectorStats (stats, rnd, NULL, NULL, 0);
     86        }
     87        gettimeofday (&mark, NULL);
     88        psF64 delta = DTIME(mark, start);
     89        ok (delta < 0.18, "sample mean %f (mask: 0, range: 1): %.3f sec", stats->sampleMean, delta);
     90        psFree (stats);
     91
     92        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     93    }
     94
     95    // test stat sample mean (mask, range)
     96    {
     97        psMemId id = psMemGetId();
     98
     99        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE);
     100        stats->min = -10;
     101        stats->max = +10;
     102        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
     103        psVectorInit (mask, 0);
     104        mask->data.U8[100] = 1;
     105        mask->data.U8[200] = 1;
     106        mask->data.U8[300] = 1;
     107
     108        gettimeofday (&start, NULL);
     109        for (int i = 0; i < 10000; i++)
     110        {
     111            psVectorStats (stats, rnd, NULL, mask, 1);
     112        }
     113        gettimeofday (&mark, NULL);
     114        psF64 delta = DTIME(mark, start);
     115        ok (delta < 0.2, "sample mean %f (mask: 1, range: 1): %.3f sec", stats->sampleMean, delta);
     116        psFree (stats);
     117        psFree (mask);
     118
     119        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     120    }
     121
     122    diag ("timing for sample median");
     123    /********** SAMPLE MEDIAN ***********/
     124    // test stat sample median (no mask, no range)
     125    {
     126        psMemId id = psMemGetId();
     127
     128        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
     129
     130        gettimeofday (&start, NULL);
     131        for (int i = 0; i < 10000; i++)
     132        {
     133            psVectorStats (stats, rnd, NULL, NULL, 0);
     134        }
     135        gettimeofday (&mark, NULL);
     136        psF64 delta = DTIME(mark, start);
     137        ok (delta < 2.8, "sample median %f (mask: 0, range: 0): %.3f sec", stats->sampleMedian, delta);
     138        psFree (stats);
     139
     140        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     141    }
     142
     143    // test stat sample median (mask, no range)
     144    {
     145        psMemId id = psMemGetId();
     146
     147        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN);
     148        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
     149        psVectorInit (mask, 0);
     150        mask->data.U8[100] = 1;
     151        mask->data.U8[200] = 1;
     152        mask->data.U8[300] = 1;
     153
     154        gettimeofday (&start, NULL);
     155        for (int i = 0; i < 10000; i++)
     156        {
     157            psVectorStats (stats, rnd, NULL, mask, 1);
     158        }
     159        gettimeofday (&mark, NULL);
     160        psF64 delta = DTIME(mark, start);
     161        ok (delta < 2.8, "sample median %f (mask: 1, range: 0): %.3f sec", stats->sampleMedian, delta);
     162        psFree (stats);
     163        psFree (mask);
     164
     165        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     166    }
     167
     168    // test stat sample median (no mask, range)
     169    {
     170        psMemId id = psMemGetId();
     171
     172        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_USE_RANGE);
     173        stats->min = -10;
     174        stats->max = +10;
     175
     176        gettimeofday (&start, NULL);
     177        for (int i = 0; i < 10000; i++)
     178        {
     179            psVectorStats (stats, rnd, NULL, NULL, 0);
     180        }
     181        gettimeofday (&mark, NULL);
     182        psF64 delta = DTIME(mark, start);
     183        ok (delta < 2.8, "sample median %f (mask: 0, range: 1): %.3f sec", stats->sampleMedian, delta);
     184        psFree (stats);
     185
     186        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     187    }
     188
     189    // test stat sample median (mask, range)
     190    {
     191        psMemId id = psMemGetId();
     192
     193        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEDIAN | PS_STAT_USE_RANGE);
     194        stats->min = -10;
     195        stats->max = +10;
     196        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
     197        psVectorInit (mask, 0);
     198        mask->data.U8[100] = 1;
     199        mask->data.U8[200] = 1;
     200        mask->data.U8[300] = 1;
     201
     202        gettimeofday (&start, NULL);
     203        for (int i = 0; i < 10000; i++)
     204        {
     205            psVectorStats (stats, rnd, NULL, mask, 1);
     206        }
     207        gettimeofday (&mark, NULL);
     208        psF64 delta = DTIME(mark, start);
     209        ok (delta < 2.8, "sample median %f (mask: 1, range: 1): %.3f sec", stats->sampleMedian, delta);
     210        psFree (stats);
     211        psFree (mask);
     212
     213        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     214    }
     215
     216    diag ("timing for sample stdev");
     217    /********** SAMPLE STDEV ***********/
     218    // test stat sample stdev (no mask, no range)
     219    {
     220        psMemId id = psMemGetId();
     221
     222        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_STDEV);
     223
     224        gettimeofday (&start, NULL);
     225        for (int i = 0; i < 10000; i++)
     226        {
     227            psVectorStats (stats, rnd, NULL, NULL, 0);
     228        }
     229        gettimeofday (&mark, NULL);
     230        psF64 delta = DTIME(mark, start);
     231        ok (delta < 0.2, "sample stdev %f (mask: 0, range: 0): %.3f sec", stats->sampleStdev, delta);
     232        psFree (stats);
     233
     234        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     235    }
     236
     237    // test stat sample stdev (mask, no range)
     238    {
     239        psMemId id = psMemGetId();
     240
     241        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_STDEV);
     242        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
     243        psVectorInit (mask, 0);
     244        mask->data.U8[100] = 1;
     245        mask->data.U8[200] = 1;
     246        mask->data.U8[300] = 1;
     247
     248        gettimeofday (&start, NULL);
     249        for (int i = 0; i < 10000; i++)
     250        {
     251            psVectorStats (stats, rnd, NULL, mask, 1);
     252        }
     253        gettimeofday (&mark, NULL);
     254        psF64 delta = DTIME(mark, start);
     255        ok (delta < 0.27, "sample stdev %f (mask: 1, range: 0): %.3f sec", stats->sampleStdev, delta);
     256        psFree (stats);
     257        psFree (mask);
     258
     259        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     260    }
     261
     262    // test stat sample stdev (no mask, range)
     263    {
     264        psMemId id = psMemGetId();
     265
     266        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_STDEV | PS_STAT_USE_RANGE);
     267        stats->min = -10;
     268        stats->max = +10;
     269
     270        gettimeofday (&start, NULL);
     271        for (int i = 0; i < 10000; i++)
     272        {
     273            psVectorStats (stats, rnd, NULL, NULL, 0);
     274        }
     275        gettimeofday (&mark, NULL);
     276        psF64 delta = DTIME(mark, start);
     277        ok (delta < 0.36, "sample stdev %f (mask: 0, range: 1): %.3f sec", stats->sampleStdev, delta);
     278        psFree (stats);
     279
     280        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     281    }
     282
     283    // test stat sample stdev (mask, range)
     284    {
     285        psMemId id = psMemGetId();
     286
     287        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_STDEV | PS_STAT_USE_RANGE);
     288        stats->min = -10;
     289        stats->max = +10;
     290        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
     291        psVectorInit (mask, 0);
     292        mask->data.U8[100] = 1;
     293        mask->data.U8[200] = 1;
     294        mask->data.U8[300] = 1;
     295
     296        gettimeofday (&start, NULL);
     297        for (int i = 0; i < 10000; i++)
     298        {
     299            psVectorStats (stats, rnd, NULL, mask, 1);
     300        }
     301        gettimeofday (&mark, NULL);
     302        psF64 delta = DTIME(mark, start);
     303        ok (delta < 0.42, "sample stdev %f (mask: 1, range: 1): %.3f sec", stats->sampleStdev, delta);
     304        psFree (stats);
     305        psFree (mask);
     306
     307        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     308    }
     309
     310    diag ("timing for sample min,max");
     311    /*************** MIN,MAX ******************/
     312    // test stat min,max (no mask, no range)
     313    {
     314        psMemId id = psMemGetId();
     315
     316        psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
     317        gettimeofday (&start, NULL);
     318        for (int i = 0; i < 10000; i++)
     319        {
     320            psVectorStats (stats, rnd, NULL, NULL, 1);
     321        }
     322        gettimeofday (&mark, NULL);
     323        psF64 delta = DTIME(mark, start);
     324        ok (delta < 0.17, "sample min,max %f,%f (mask: 0, range: 0): %.3f sec", stats->min, stats->max, delta);
     325        psFree (stats);
     326
     327        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     328    }
     329    // test stat min,max (no mask, no range)
     330    {
     331        psMemId id = psMemGetId();
     332
     333        psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
     334        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
     335        psVectorInit (mask, 0);
     336        mask->data.U8[100] = 1;
     337        mask->data.U8[200] = 1;
     338        mask->data.U8[300] = 1;
     339
     340        gettimeofday (&start, NULL);
     341        for (int i = 0; i < 10000; i++)
     342        {
     343            psVectorStats (stats, rnd, NULL, mask, 1);
     344        }
     345        gettimeofday (&mark, NULL);
     346        psF64 delta = DTIME(mark, start);
     347        ok (delta < 0.18, "sample min,max %f,%f (mask: 1, range: 0): %.3f sec", stats->min, stats->max, delta);
     348        psFree (stats);
     349        psFree (mask);
     350
     351        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     352    }
     353    // test stat min,max (no mask, no range)
     354    {
     355        psMemId id = psMemGetId();
     356
     357        psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX | PS_STAT_USE_RANGE);
     358        stats->min = -10;
     359        stats->max = +10;
     360
     361        gettimeofday (&start, NULL);
     362        for (int i = 0; i < 10000; i++)
     363        {
     364            psVectorStats (stats, rnd, NULL, NULL, 1);
     365        }
     366        gettimeofday (&mark, NULL);
     367        psF64 delta = DTIME(mark, start);
     368        ok (delta < 0.22, "sample min,max %f,%f (mask: 0, range: 1): %.3f sec", stats->min, stats->max, delta);
     369        psFree (stats);
     370
     371        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     372    }
     373    // test stat min,max (no mask, no range)
     374    {
     375        psMemId id = psMemGetId();
     376
     377        psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX | PS_STAT_USE_RANGE);
     378        stats->min = -10;
     379        stats->max = +10;
     380        psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
     381        psVectorInit (mask, 0);
     382        mask->data.U8[100] = 1;
     383        mask->data.U8[200] = 1;
     384        mask->data.U8[300] = 1;
     385
     386        gettimeofday (&start, NULL);
     387        for (int i = 0; i < 10000; i++)
     388        {
     389            psVectorStats (stats, rnd, NULL, mask, 1);
     390        }
     391        gettimeofday (&mark, NULL);
     392        psF64 delta = DTIME(mark, start);
     393        ok (delta < 0.26, "sample min,max %f,%f (mask: 1, range: 1): %.3f sec", stats->min, stats->max, delta);
     394        psFree (stats);
     395        psFree (mask);
     396
     397        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     398    }
     399
     400    diag ("timing for clipped stats");
     401    /********** CLIPPED STATS ***********/
     402    {
     403        psMemId id = psMemGetId();
     404
     405        psStats *stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
     406        psVector *rnd2 = psVectorAlloc (1000, PS_TYPE_F32);
     407        for (int i = 0; i < rnd2->n; i++)
     408        {
     409            rnd2->data.F32[i] = psRandomGaussian (seed);
     410        }
     411
     412        gettimeofday (&start, NULL);
     413        for (int i = 0; i < 1000; i++)
     414        {
     415            psVectorStats (stats, rnd2, NULL, NULL, 1);
     416        }
     417        gettimeofday (&mark, NULL);
     418        psF64 delta = DTIME(mark, start);
     419        ok (delta < 0.3, "clipped mean %f, stdev %f (mask: 0, range: 0): %.3f sec (1000 pts / 1000 loops)", stats->clippedMean, stats->clippedStdev, delta);
     420        psFree (stats);
     421        psFree (rnd2);
     422
     423        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     424    }
     425    {
     426        psMemId id = psMemGetId();
     427
     428        psStats *stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
     429        psVector *rnd2 = psVectorAlloc (3000, PS_TYPE_F32);
     430        for (int i = 0; i < rnd2->n; i++)
     431        {
     432            rnd2->data.F32[i] = psRandomGaussian (seed);
     433        }
     434
     435        gettimeofday (&start, NULL);
     436        for (int i = 0; i < 1000; i++)
     437        {
     438            psVectorStats (stats, rnd2, NULL, NULL, 1);
     439        }
     440        gettimeofday (&mark, NULL);
     441        psF64 delta = DTIME(mark, start);
     442        ok (delta < 0.5, "clipped mean %f, stdev %f (mask: 0, range: 0): %.3f sec (3000 pts / 1000 loops)", stats->clippedMean, stats->clippedStdev, delta);
     443        psFree (stats);
     444        psFree (rnd2);
     445
     446        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     447    }
     448    {
     449        psMemId id = psMemGetId();
     450
     451        psStats *stats = psStatsAlloc (PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV);
     452        psVector *rnd2 = psVectorAlloc (10000, PS_TYPE_F32);
     453        for (int i = 0; i < rnd2->n; i++)
     454        {
     455            rnd2->data.F32[i] = psRandomGaussian (seed);
     456        }
     457
     458        gettimeofday (&start, NULL);
     459        for (int i = 0; i < 1000; i++)
     460        {
     461            psVectorStats (stats, rnd2, NULL, NULL, 1);
     462        }
     463        gettimeofday (&mark, NULL);
     464        psF64 delta = DTIME(mark, start);
     465        ok (delta < 1.2, "clipped mean %f, stdev %f (mask: 0, range: 0): %.3f sec (10000 pts / 1000 loops)", stats->clippedMean, stats->clippedStdev, delta);
     466        psFree (stats);
     467        psFree (rnd2);
     468
     469        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     470    }
     471
     472    diag ("timing for robust stats");
     473    /********** ROBUST STATS ***********/
     474    {
     475        psMemId id = psMemGetId();
     476
     477        psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE);
     478        psVector *rnd2 = psVectorAlloc (1000, PS_TYPE_F32);
     479        for (int i = 0; i < rnd2->n; i++)
     480        {
     481            rnd2->data.F32[i] = psRandomGaussian (seed);
     482        }
     483
     484        gettimeofday (&start, NULL);
     485        for (int i = 0; i < 1000; i++)
     486        {
     487            psVectorStats (stats, rnd2, NULL, NULL, 1);
     488        }
     489        gettimeofday (&mark, NULL);
     490        psF64 delta = DTIME(mark, start);
     491        ok (delta < 0.3, "robust mean %f, stdev %f (mask: 0, range: 0): %.3f sec (1000 pts / 1000 loops)", stats->robustMedian, stats->robustStdev, delta);
     492        psFree (stats);
     493        psFree (rnd2);
     494
     495        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     496    }
     497    {
     498        psMemId id = psMemGetId();
     499
     500        psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE);
     501        psVector *rnd2 = psVectorAlloc (3000, PS_TYPE_F32);
     502        for (int i = 0; i < rnd2->n; i++)
     503        {
     504            rnd2->data.F32[i] = psRandomGaussian (seed);
     505        }
     506
     507        gettimeofday (&start, NULL);
     508        for (int i = 0; i < 1000; i++)
     509        {
     510            psVectorStats (stats, rnd2, NULL, NULL, 1);
     511        }
     512        gettimeofday (&mark, NULL);
     513        psF64 delta = DTIME(mark, start);
     514        ok (delta < 0.5, "robust mean %f, stdev %f (mask: 0, range: 0): %.3f sec (3000 pts / 1000 loops)", stats->robustMedian, stats->robustStdev, delta);
     515        psFree (stats);
     516        psFree (rnd2);
     517
     518        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     519    }
     520    {
     521        psMemId id = psMemGetId();
     522
     523        psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE);
     524        psVector *rnd2 = psVectorAlloc (10000, PS_TYPE_F32);
     525        for (int i = 0; i < rnd2->n; i++)
     526        {
     527            rnd2->data.F32[i] = psRandomGaussian (seed);
     528        }
     529
     530        gettimeofday (&start, NULL);
     531        for (int i = 0; i < 1000; i++)
     532        {
     533            psVectorStats (stats, rnd2, NULL, NULL, 1);
     534        }
     535        gettimeofday (&mark, NULL);
     536        psF64 delta = DTIME(mark, start);
     537        ok (delta < 1.2, "robust mean %f, stdev %f (mask: 0, range: 0): %.3f sec (10000 pts / 1000 loops)", stats->robustMedian, stats->robustStdev, delta);
     538        psFree (stats);
     539        psFree (rnd2);
     540
     541        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     542    }
     543
     544    diag ("timing for fitted stats");
     545    /********** FITTED TIMING ***********/
     546    {
     547        psMemId id = psMemGetId();
    32548
    33549        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV);
     
    38554        }
    39555
    40         psTimerClear ("test");
    41         for (int i = 0; i < 1000; i++)
    42         {
    43             psVectorStats (stats, rnd2, NULL, NULL, 1);
    44         }
    45         psF64 delta = psTimerMark("test");
    46         ok (delta < 0.5, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec", stats->fittedMean, stats->fittedStdev, delta);
    47         psFree (stats);
    48 
    49         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    50     }
    51     // test FITTED timing
    52     {
    53         // psMemId id = psMemGetId();
    54 
    55         diag ("check fitted mean speed : 3000 pts / 1000 loops");
     556        gettimeofday (&start, NULL);
     557        for (int i = 0; i < 1000; i++)
     558        {
     559            psVectorStats (stats, rnd2, NULL, NULL, 1);
     560
     561        }
     562        gettimeofday (&mark, NULL);
     563        psF64 delta = DTIME(mark, start);
     564        ok (delta < 0.7, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (1000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
     565        psFree (stats);
     566        psFree (rnd2);
     567
     568        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     569    }
     570    {
     571        psMemId id = psMemGetId();
    56572
    57573        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV);
     
    62578        }
    63579
    64         psTimerClear ("test");
    65         for (int i = 0; i < 1000; i++)
    66         {
    67             psVectorStats (stats, rnd2, NULL, NULL, 1);
    68         }
    69         psF64 delta = psTimerMark("test");
    70         ok (delta < 0.8, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec", stats->fittedMean, stats->fittedStdev, delta);
    71         psFree (stats);
    72 
    73         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    74     }
    75     // test FITTED timing
    76     {
    77         // psMemId id = psMemGetId();
    78 
    79         diag ("check fitted mean speed : 10000 pts / 1000 loops");
     580        gettimeofday (&start, NULL);
     581        for (int i = 0; i < 1000; i++)
     582        {
     583            psVectorStats (stats, rnd2, NULL, NULL, 1);
     584        }
     585        gettimeofday (&mark, NULL);
     586        psF64 delta = DTIME(mark, start);
     587        ok (delta < 0.8, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (3000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
     588        psFree (stats);
     589        psFree (rnd2);
     590
     591        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     592    }
     593    {
     594        psMemId id = psMemGetId();
    80595
    81596        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV);
     
    86601        }
    87602
    88         psTimerClear ("test");
    89         for (int i = 0; i < 1000; i++)
    90         {
    91             psVectorStats (stats, rnd2, NULL, NULL, 1);
    92         }
    93         psF64 delta = psTimerMark("test");
    94         ok (delta < 2.2, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec", stats->fittedMean, stats->fittedStdev, delta);
    95         psFree (stats);
    96 
    97         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    98     }
    99     // test stat min,max (no mask, no range)
    100     {
    101         // psMemId id = psMemGetId();
    102 
    103         diag ("check fitted mean speed (and value)");
    104 
    105         psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV | PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
    106         psVector *sample = psVectorAlloc (1000, PS_TYPE_F32);
    107         psVector *fitted = psVectorAlloc (1000, PS_TYPE_F32);
    108 
    109         for (int i = 0; i < 1000; i++)
    110         {
    111             // generate a new sample
    112             for (int j = 0; j < rnd->n; j++) {
    113                 rnd->data.F32[j] = psRandomGaussian (seed);
    114             }
    115             // measure the stats
    116             psVectorStats (stats, rnd, NULL, NULL, 1);
    117             sample->data.F32[i] = stats->sampleMean;
    118             fitted->data.F32[i] = stats->fittedMean;
    119         }
    120         psFree (stats);
    121 
    122         stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
    123         psVectorStats (stats, sample, NULL, NULL, 1);
    124         ok (stats->sampleStdev < 2/sqrt(1000), "sample mean %f, stdev %f", stats->sampleMean, stats->sampleStdev);
    125         psVectorStats (stats, fitted, NULL, NULL, 1);
    126         ok (stats->sampleStdev < 2/sqrt(1000), "fitted mean %f, stdev %f", stats->sampleMean, stats->sampleStdev);
    127         psFree (stats);
    128 
    129         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    130     }
    131 
    132     /********** robust stats ***********/
    133     // test ROBUST timing
    134     {
    135         // psMemId id = psMemGetId();
    136 
    137         diag ("check robust mean speed : 1000 pts / 1000 loops");
    138 
    139         psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE);
     603        gettimeofday (&start, NULL);
     604        for (int i = 0; i < 1000; i++)
     605        {
     606            psVectorStats (stats, rnd2, NULL, NULL, 1);
     607        }
     608        gettimeofday (&mark, NULL);
     609        psF64 delta = DTIME(mark, start);
     610        ok (delta < 2.2, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (10000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
     611        psFree (stats);
     612        psFree (rnd2);
     613
     614        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     615    }
     616
     617    diag ("timing for fitted (v2) stats");
     618    /********** FITTED (v2) TIMING ***********/
     619    {
     620        psMemId id = psMemGetId();
     621
     622        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2);
    140623        psVector *rnd2 = psVectorAlloc (1000, PS_TYPE_F32);
    141624        for (int i = 0; i < rnd2->n; i++)
     
    144627        }
    145628
    146         psTimerClear ("test");
    147         for (int i = 0; i < 1000; i++)
    148         {
    149             psVectorStats (stats, rnd2, NULL, NULL, 1);
    150         }
    151         psF64 delta = psTimerMark("test");
    152         ok (delta < 0.2, "robust mean %f, stdev %f (mask: 0, range: 0): %.3f sec", stats->robustMedian, stats->robustStdev, delta);
    153         psFree (stats);
    154 
    155         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    156     }
    157     // test ROBUST timing
    158     {
    159         // psMemId id = psMemGetId();
    160 
    161         diag ("check robust mean speed : 3000 pts / 1000 loops");
    162 
    163         psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE);
     629        gettimeofday (&start, NULL);
     630        for (int i = 0; i < 1000; i++)
     631        {
     632            psVectorStats (stats, rnd2, NULL, NULL, 1);
     633
     634        }
     635        gettimeofday (&mark, NULL);
     636        psF64 delta = DTIME(mark, start);
     637        ok (delta < 0.7, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (1000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
     638        psFree (stats);
     639        psFree (rnd2);
     640
     641        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     642    }
     643    {
     644        psMemId id = psMemGetId();
     645
     646        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2);
    164647        psVector *rnd2 = psVectorAlloc (3000, PS_TYPE_F32);
    165648        for (int i = 0; i < rnd2->n; i++)
     
    168651        }
    169652
    170         psTimerClear ("test");
    171         for (int i = 0; i < 1000; i++)
    172         {
    173             psVectorStats (stats, rnd2, NULL, NULL, 1);
    174         }
    175         psF64 delta = psTimerMark("test");
    176         ok (delta < 0.5, "robust mean %f, stdev %f (mask: 0, range: 0): %.3f sec", stats->robustMedian, stats->robustStdev, delta);
    177         psFree (stats);
    178 
    179         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    180     }
    181     // test ROBUST timing
    182     {
    183         // psMemId id = psMemGetId();
    184 
    185         diag ("check robust mean speed : 10000 pts / 1000 loops");
    186 
    187         psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_ROBUST_QUARTILE);
     653        gettimeofday (&start, NULL);
     654        for (int i = 0; i < 1000; i++)
     655        {
     656            psVectorStats (stats, rnd2, NULL, NULL, 1);
     657        }
     658        gettimeofday (&mark, NULL);
     659        psF64 delta = DTIME(mark, start);
     660        ok (delta < 0.8, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (3000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
     661        psFree (stats);
     662        psFree (rnd2);
     663
     664        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     665    }
     666    {
     667        psMemId id = psMemGetId();
     668
     669        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2);
    188670        psVector *rnd2 = psVectorAlloc (10000, PS_TYPE_F32);
    189671        for (int i = 0; i < rnd2->n; i++)
     
    192674        }
    193675
    194         psTimerClear ("test");
    195         for (int i = 0; i < 1000; i++)
    196         {
    197             psVectorStats (stats, rnd2, NULL, NULL, 1);
    198         }
    199         psF64 delta = psTimerMark("test");
    200         ok (delta < 1.2, "robust mean %f, stdev %f (mask: 0, range: 0): %.3f sec", stats->robustMedian, stats->robustStdev, delta);
    201         psFree (stats);
    202 
    203         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    204     }
    205     // test stat min,max (no mask, no range)
    206     {
    207         // psMemId id = psMemGetId();
    208 
    209         diag ("check robust mean speed (and value)");
    210 
    211         psStats *stats = psStatsAlloc (PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
     676        gettimeofday (&start, NULL);
     677        for (int i = 0; i < 1000; i++)
     678        {
     679            psVectorStats (stats, rnd2, NULL, NULL, 1);
     680        }
     681        gettimeofday (&mark, NULL);
     682        psF64 delta = DTIME(mark, start);
     683        ok (delta < 2.2, "fitted mean %f, stdev %f (mask: 0, range: 0): %.3f sec (10000 pts / 1000 loops)", stats->fittedMean, stats->fittedStdev, delta);
     684        psFree (stats);
     685        psFree (rnd2);
     686
     687        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     688    }
     689
     690    diag ("compare sample, robust, and fitted mean and stdev to theoretical");
     691    // compare SAMPLE, FITTED, ROBUST mean to theoretical
     692    {
     693        psMemId id = psMemGetId();
     694
     695        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV);
    212696        psVector *sample = psVectorAlloc (1000, PS_TYPE_F32);
    213697        psVector *robust = psVectorAlloc (1000, PS_TYPE_F32);
     698        psVector *fitted = psVectorAlloc (1000, PS_TYPE_F32);
    214699
    215700        for (int i = 0; i < 1000; i++)
     
    223708            sample->data.F32[i] = stats->sampleMean;
    224709            robust->data.F32[i] = stats->robustMedian;
     710            fitted->data.F32[i] = stats->fittedMean;
    225711        }
    226712        psFree (stats);
     
    228714        stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
    229715        psVectorStats (stats, sample, NULL, NULL, 1);
    230         ok (stats->sampleStdev < 2/sqrt(1000), "sample mean %f, stdev %f", stats->sampleMean, stats->sampleStdev);
     716        ok (stats->sampleStdev < 2/sqrt(1000), "sample mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
    231717        psVectorStats (stats, robust, NULL, NULL, 1);
    232         ok (stats->sampleStdev < 2/sqrt(1000), "robust mean %f, stdev %f", stats->sampleMean, stats->sampleStdev);
    233         psFree (stats);
    234 
    235         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    236     }
    237 
    238     /**********  stats ***********/
    239     // test stat sample mean (no mask, no range)
    240     {
    241         // psMemId id = psMemGetId();
    242 
    243         diag ("check sample mean speed (and value)");
    244 
    245         psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN);
    246 
    247         psTimerClear ("test");
    248         for (int i = 0; i < 10000; i++)
    249         {
    250             psVectorStats (stats, rnd, NULL, NULL, 0);
    251         }
    252         psF64 delta = psTimerMark("test");
    253         ok (delta < 0.1, "sample mean %f (mask: 0, range: 0): %.3f sec", stats->sampleMean, delta);
    254         psFree (stats);
    255 
    256         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    257     }
    258 
    259     // test stat sample mean (mask, no range)
    260     {
    261         // psMemId id = psMemGetId();
    262 
    263         diag ("check sample mean speed (and value)");
    264 
    265         psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN);
    266         psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
    267         psVectorInit (mask, 0);
    268         mask->data.U8[100] = 1;
    269         mask->data.U8[200] = 1;
    270         mask->data.U8[300] = 1;
    271 
    272         psTimerClear ("test");
    273         for (int i = 0; i < 10000; i++)
    274         {
    275             psVectorStats (stats, rnd, NULL, mask, 1);
    276         }
    277         psF64 delta = psTimerMark("test");
    278         ok (delta < 0.12, "sample mean %f (mask: 1, range: 0): %.3f sec", stats->sampleMean, delta);
    279         psFree (stats);
    280 
    281         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    282     }
    283 
    284     // test stat sample mean (no mask, range)
    285     {
    286         // psMemId id = psMemGetId();
    287 
    288         diag ("check sample mean speed (and value)");
    289 
    290         psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE);
    291         stats->min = -10;
    292         stats->max = +10;
    293 
    294         psTimerClear ("test");
    295         for (int i = 0; i < 10000; i++)
    296         {
    297             psVectorStats (stats, rnd, NULL, NULL, 0);
    298         }
    299         psF64 delta = psTimerMark("test");
    300         ok (delta < 0.18, "sample mean %f (mask: 0, range: 1): %.3f sec", stats->sampleMean, delta);
    301         psFree (stats);
    302 
    303         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    304     }
    305 
    306     // test stat sample mean (mask, range)
    307     {
    308         // psMemId id = psMemGetId();
    309 
    310         diag ("check sample mean speed (and value)");
    311 
    312         psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_USE_RANGE);
    313         stats->min = -10;
    314         stats->max = +10;
    315         psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
    316         psVectorInit (mask, 0);
    317         mask->data.U8[100] = 1;
    318         mask->data.U8[200] = 1;
    319         mask->data.U8[300] = 1;
    320 
    321         psTimerClear ("test");
    322         for (int i = 0; i < 10000; i++)
    323         {
    324             psVectorStats (stats, rnd, NULL, mask, 1);
    325         }
    326         psF64 delta = psTimerMark("test");
    327         ok (delta < 0.2, "sample mean %f (mask: 1, range: 1): %.3f sec", stats->sampleMean, delta);
    328         psFree (stats);
    329 
    330         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    331     }
    332 
    333     /*************** min,max ******************/
    334     // test stat min,max (no mask, no range)
    335     {
    336         // psMemId id = psMemGetId();
    337 
    338         diag ("check min,max speed (no mask, no range)");
    339 
    340         psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
    341         psTimerClear ("test");
    342         for (int i = 0; i < 10000; i++)
    343         {
     718        ok (stats->sampleStdev < 2/sqrt(1000), "robust mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
     719        psVectorStats (stats, fitted, NULL, NULL, 1);
     720        ok (stats->sampleStdev < 2/sqrt(1000), "fitted mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
     721        psFree (stats);
     722        psFree (sample);
     723        psFree (robust);
     724        psFree (fitted);
     725
     726        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     727    }
     728
     729    diag ("compare sample, robust, and fitted mean and stdev to theoretical");
     730    // compare SAMPLE, FITTED_V2, ROBUST mean to theoretical
     731    {
     732        psMemId id = psMemGetId();
     733
     734        psStats *stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV | PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2);
     735        psVector *sample = psVectorAlloc (1000, PS_TYPE_F32);
     736        psVector *robust = psVectorAlloc (1000, PS_TYPE_F32);
     737        psVector *fitted = psVectorAlloc (1000, PS_TYPE_F32);
     738
     739        for (int i = 0; i < 1000; i++)
     740        {
     741            // generate a new sample
     742            for (int j = 0; j < rnd->n; j++) {
     743                rnd->data.F32[j] = psRandomGaussian (seed);
     744            }
     745            // measure the stats
    344746            psVectorStats (stats, rnd, NULL, NULL, 1);
    345         }
    346         psF64 delta = psTimerMark("test");
    347         ok (delta < 0.17, "sample min,max %f,%f (mask: 0, range: 0): %.3f sec", stats->min, stats->max, delta);
    348         psFree (stats);
    349 
    350         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    351     }
    352     // test stat min,max (no mask, no range)
    353     {
    354         // psMemId id = psMemGetId();
    355 
    356         diag ("check min,max speed (mask, no range)");
    357 
    358         psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX);
    359         psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
    360         psVectorInit (mask, 0);
    361         mask->data.U8[100] = 1;
    362         mask->data.U8[200] = 1;
    363         mask->data.U8[300] = 1;
    364 
    365         psTimerClear ("test");
    366         for (int i = 0; i < 10000; i++)
    367         {
    368             psVectorStats (stats, rnd, NULL, mask, 1);
    369         }
    370         psF64 delta = psTimerMark("test");
    371         ok (delta < 0.18, "sample min,max %f,%f (mask: 1, range: 0): %.3f sec", stats->min, stats->max, delta);
    372         psFree (stats);
    373 
    374         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    375     }
    376     // test stat min,max (no mask, no range)
    377     {
    378         // psMemId id = psMemGetId();
    379 
    380         diag ("check min,max speed (no mask, range)");
    381 
    382         psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX | PS_STAT_USE_RANGE);
    383         stats->min = -10;
    384         stats->max = +10;
    385 
    386         psTimerClear ("test");
    387         for (int i = 0; i < 10000; i++)
    388         {
    389             psVectorStats (stats, rnd, NULL, NULL, 1);
    390         }
    391         psF64 delta = psTimerMark("test");
    392         ok (delta < 0.22, "sample min,max %f,%f (mask: 0, range: 1): %.3f sec", stats->min, stats->max, delta);
    393         psFree (stats);
    394 
    395         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    396     }
    397     // test stat min,max (no mask, no range)
    398     {
    399         // psMemId id = psMemGetId();
    400 
    401         diag ("check min,max speed (mask, range)");
    402 
    403         psStats *stats = psStatsAlloc (PS_STAT_MIN | PS_STAT_MAX | PS_STAT_USE_RANGE);
    404         stats->min = -10;
    405         stats->max = +10;
    406         psVector *mask = psVectorAlloc (1000, PS_TYPE_U8);
    407         psVectorInit (mask, 0);
    408         mask->data.U8[100] = 1;
    409         mask->data.U8[200] = 1;
    410         mask->data.U8[300] = 1;
    411 
    412         psTimerClear ("test");
    413         for (int i = 0; i < 10000; i++)
    414         {
    415             psVectorStats (stats, rnd, NULL, mask, 1);
    416         }
    417         psF64 delta = psTimerMark("test");
    418         ok (delta < 0.26, "sample min,max %f,%f (mask: 1, range: 1): %.3f sec", stats->min, stats->max, delta);
    419         psFree (stats);
    420 
    421         // ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     747            sample->data.F32[i] = stats->sampleMean;
     748            robust->data.F32[i] = stats->robustMedian;
     749            fitted->data.F32[i] = stats->fittedMean;
     750        }
     751        psFree (stats);
     752
     753        stats = psStatsAlloc (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_STDEV);
     754        psVectorStats (stats, sample, NULL, NULL, 1);
     755        ok (stats->sampleStdev < 2/sqrt(1000), "sample mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
     756        psVectorStats (stats, robust, NULL, NULL, 1);
     757        ok (stats->sampleStdev < 2/sqrt(1000), "robust mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
     758        psVectorStats (stats, fitted, NULL, NULL, 1);
     759        ok (stats->sampleStdev < 2/sqrt(1000), "fitted mean %f, stdev %f (1000 tries)", stats->sampleMean, stats->sampleStdev);
     760        psFree (stats);
     761        psFree (sample);
     762        psFree (robust);
     763        psFree (fitted);
     764
     765        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    422766    }
    423767
  • trunk/psLib/test/math/tap_psStats_Sample_01.c

    r10395 r10550  
    215215                         };
    216216
     217static float yraw_03[] = {
     218                             183.6000061, 171.3600006, 182.5800018, 178.5, 176.4600067,
     219                             176.4600067, 171.3600006, 178.5, 183.6000061, 175.4400024,
     220                             159.1199951, 180.5399933, 183.6000061, 187.6799927, 175.4400024,
     221                             181.5599976, 174.4199982, 179.5200043, 180.5399933, 187.6799927,
     222                             183.6000061, 171.3600006, 177.4799957, 192.7799988, 184.6199951,
     223                             176.4600067, 178.5, 182.5800018, 173.3999939, 191.7599945,
     224                             202.9799957, 167.2799988, 181.5599976, 185.6399994, 173.3999939,
     225                             159.1199951, 178.5, 188.6999969, 179.5200043, 178.5,
     226                             184.6199951, 175.4400024, 186.6600037, 179.5200043, 183.6000061,
     227                             182.5800018, 173.3999939, 181.5599976, 184.6199951, 177.4799957,
     228                             162.1799927, 186.6600037, 163.1999969, 199.9199982, 173.3999939,
     229                             170.3399963, 184.6199951, 190.7400055, 173.3999939, 178.5,
     230                             189.7200012, 192.7799988, 166.2599945, 187.6799927, 174.4199982,
     231                             176.4600067, 181.5599976, 199.9199982, 193.8000031, 170.3399963,
     232                             182.5800018, 199.9199982, 183.6000061, 160.1399994, 184.6199951,
     233                             195.8399963, 173.3999939, 189.7200012, 192.7799988, 162.1799927,
     234                             174.4199982, 190.7400055, 175.4400024, 192.7799988, 181.5599976,
     235                             185.6399994, 184.6199951, 166.2599945, 189.7200012, 192.7799988,
     236                             183.6000061, 190.7400055, 188.6999969, 174.4199982, 184.6199951,
     237                             186.6600037, 182.5800018, 173.3999939, 171.3600006, 166.2599945,
     238                             166.2599945, 176.4600067, 147.8999939, 187.6799927, 166.2599945,
     239                             173.3999939, 179.5200043, 166.2599945, 169.3200073, 172.3800049,
     240                             174.4199982, 195.8399963, 192.7799988, 177.4799957, 181.5599976,
     241                             163.1999969, 171.3600006, 194.8200073, 159.1199951, 173.3999939,
     242                             182.5800018, 178.5, 179.5200043, 184.6199951, 179.5200043,
     243                             164.2200012, 191.7599945, 177.4799957, 186.6600037, 168.3000031,
     244                             168.3000031, 168.3000031, 180.5399933, 179.5200043, 175.4400024,
     245                             174.4199982, 184.6199951, 186.6600037, 164.2200012, 169.3200073,
     246                             187.6799927, 189.7200012, 166.2599945, 169.3200073, 187.6799927,
     247                             172.3800049, 204, 194.8200073, 177.4799957, 188.6999969,
     248                             171.3600006, 184.6199951, 182.5800018, 184.6199951, 176.4600067,
     249                             186.6600037, 170.3399963, 170.3399963, 182.5800018, 184.6199951,
     250                             168.3000031, 175.4400024, 176.4600067, 164.2200012, 183.6000061,
     251                             161.1600037, 175.4400024, 167.2799988, 179.5200043, 176.4600067,
     252                             175.4400024, 179.5200043, 158.1000061, 168.3000031, 168.3000031,
     253                             179.5200043, 192.7799988, 190.7400055, 178.5, 173.3999939,
     254                             180.5399933, 179.5200043, 191.7599945, 184.6199951, 177.4799957,
     255                             182.5800018, 188.6999969, 169.3200073, 162.1799927, 172.3800049,
     256                             160.1399994, 177.4799957, 176.4600067, 180.5399933, 170.3399963,
     257                             168.3000031, 171.3600006, 180.5399933, 162.1799927, 163.1999969,
     258                             170.3399963, 180.5399933, 164.2200012, 183.6000061, 178.5,
     259                             193.8000031, 177.4799957, 178.5, 183.6000061, 192.7799988,
     260                             157.0800018, 176.4600067, 190.7400055, 179.5200043, 157.0800018,
     261                             178.5, 165.2400055, 195.8399963, 177.4799957, 166.2599945,
     262                             172.3800049, 182.5800018, 175.4400024, 187.6799927, 164.2200012,
     263                             178.5, 173.3999939, 173.3999939, 173.3999939, 172.3800049,
     264                             183.6000061, 181.5599976, 163.1999969, 179.5200043, 193.8000031,
     265                             172.3800049, 158.1000061, 171.3600006, 165.2400055, 183.6000061,
     266                             187.6799927, 158.1000061, 171.3600006, 168.3000031, 166.2599945,
     267                             166.2599945, 178.5, 179.5200043, 165.2400055, 176.4600067,
     268                             160.1399994, 193.8000031, 170.3399963, 162.1799927, 180.5399933,
     269                             159.1199951, 189.7200012, 160.1399994, 196.8600006, 173.3999939,
     270                             170.3399963, 165.2400055, 193.8000031, 192.7799988, 184.6199951,
     271                             151.9799957, 167.2799988, 162.1799927, 183.6000061, 166.2599945,
     272                             177.4799957, 168.3000031, 191.7599945, 181.5599976, 166.2599945,
     273                             171.3600006, 188.6999969, 172.3800049, 195.8399963, 189.7200012,
     274                             177.4799957, 167.2799988, 191.7599945, 169.3200073, 181.5599976,
     275                             180.5399933, 162.1799927, 171.3600006, 162.1799927, 171.3600006,
     276                             177.4799957, 167.2799988, 180.5399933, 182.5800018, 187.6799927,
     277                             175.4400024, 193.8000031, 174.4199982, 183.6000061, 179.5200043,
     278                             164.2200012, 162.1799927, 186.6600037, 189.7200012, 186.6600037,
     279                             171.3600006, 187.6799927, 189.7200012, 175.4400024, 184.6199951,
     280                             172.3800049, 177.4799957, 183.6000061, 176.4600067, 171.3600006,
     281                             172.3800049, 181.5599976, 175.4400024, 170.3399963, 196.8600006,
     282                             201.9600067, 180.5399933, 176.4600067, 183.6000061, 175.4400024,
     283                             184.6199951, 163.1999969, 177.4799957, 176.4600067, 175.4400024,
     284                             192.7799988, 167.2799988, 176.4600067, 184.6199951, 184.6199951,
     285                             177.4799957, 185.6399994, 196.8600006, 186.6600037, 184.6199951,
     286                             169.3200073, 164.2200012, 185.6399994, 168.3000031, 186.6600037,
     287                             180.5399933, 177.4799957, 177.4799957, 186.6600037, 191.7599945,
     288                             177.4799957, 181.5599976, 166.2599945, 179.5200043, 184.6199951,
     289                             178.5, 171.3600006, 184.6199951, 192.7799988, 182.5800018,
     290                             186.6600037, 175.4400024, 182.5800018, 173.3999939, 186.6600037,
     291                             162.1799927, 175.4400024, 196.8600006, 166.2599945, 189.7200012,
     292                             189.7200012, 182.5800018, 172.3800049, 165.2400055, 191.7599945,
     293                             174.4199982, 173.3999939, 178.5, 171.3600006, 157.0800018,
     294                             182.5800018, 161.1600037, 195.8399963, 186.6600037, 167.2799988,
     295                             166.2599945, 182.5800018, 178.5, 176.4600067, 181.5599976,
     296                             184.6199951, 183.6000061, 179.5200043, 174.4199982, 167.2799988,
     297                             187.6799927, 176.4600067, 165.2400055, 179.5200043, 157.0800018,
     298                             171.3600006, 170.3399963, 175.4400024, 161.1600037, 185.6399994,
     299                             169.3200073, 192.7799988, 175.4400024, 172.3800049, 180.5399933,
     300                             183.6000061, 174.4199982, 176.4600067, 164.2200012, 183.6000061,
     301                             179.5200043, 165.2400055, 169.3200073, 172.3800049, 149.9400024,
     302                             175.4400024, 188.6999969, 190.7400055, 171.3600006, 172.3800049,
     303                             183.6000061, 178.5, 165.2400055, 176.4600067, 177.4799957,
     304                             188.6999969, 192.7799988, 183.6000061, 163.1999969, 186.6600037,
     305                             183.6000061, 160.1399994, 167.2799988, 172.3800049, 179.5200043,
     306                             189.7200012, 172.3800049, 177.4799957, 161.1600037, 174.4199982,
     307                             190.7400055, 181.5599976, 187.6799927, 176.4600067, 176.4600067,
     308                             183.6000061, 176.4600067, 188.6999969, 174.4199982, 170.3399963,
     309                             185.6399994, 177.4799957, 172.3800049, 179.5200043, 175.4400024,
     310                             170.3399963, 169.3200073, 176.4600067, 177.4799957, 169.3200073,
     311                             199.9199982, 171.3600006, 194.8200073, 188.6999969, 193.8000031,
     312                             182.5800018, 171.3600006, 177.4799957, 175.4400024, 172.3800049,
     313                             166.2599945, 183.6000061, 157.0800018, 177.4799957, 193.8000031,
     314                             168.3000031, 175.4400024, 175.4400024, 170.3399963, 191.7599945,
     315                             189.7200012, 182.5800018, 177.4799957, 157.0800018, 174.4199982,
     316                             189.7200012, 162.1799927, 184.6199951, 164.2200012, 157.0800018,
     317                             197.8800049, 175.4400024, 184.6199951, 202.9799957, 190.7400055,
     318                             171.3600006, 160.1399994, 162.1799927, 176.4600067, 180.5399933,
     319                             206.0399933, 189.7200012, 170.3399963, 175.4400024, 175.4400024,
     320                             185.6399994, 187.6799927, 168.3000031, 176.4600067, 177.4799957,
     321                             185.6399994, 167.2799988, 178.5, 182.5800018, 179.5200043,
     322                             173.3999939, 185.6399994, 196.8600006, 183.6000061, 162.1799927,
     323                             176.4600067, 189.7200012, 208.0800018, 177.4799957, 163.1999969,
     324                             187.6799927, 196.8600006, 180.5399933, 188.6999969, 163.1999969,
     325                             187.6799927, 168.3000031, 182.5800018, 181.5599976, 174.4199982,
     326                             181.5599976, 161.1600037, 163.1999969, 184.6199951, 190.7400055,
     327                             181.5599976, 185.6399994, 186.6600037, 173.3999939, 172.3800049,
     328                             179.5200043, 187.6799927, 191.7599945, 190.7400055, 183.6000061,
     329                             166.2599945, 196.8600006, 172.3800049, 174.4199982, 181.5599976,
     330                             177.4799957, 176.4600067, 188.6999969, 184.6199951, 169.3200073,
     331                             178.5, 186.6600037, 174.4199982, 185.6399994, 201.9600067,
     332                             171.3600006, 177.4799957, 183.6000061, 165.2400055, 189.7200012,
     333                             188.6999969, 178.5, 163.1999969, 169.3200073, 178.5,
     334                             182.5800018, 173.3999939, 177.4799957, 165.2400055, 163.1999969,
     335                             175.4400024, 184.6199951, 189.7200012, 186.6600037, 188.6999969,
     336                             163.1999969, 158.1000061, 172.3800049, 186.6600037, 173.3999939,
     337                             157.0800018, 158.1000061, 172.3800049, 197.8800049, 171.3600006,
     338                             172.3800049, 184.6199951, 173.3999939, 174.4199982, 175.4400024,
     339                             166.2599945, 166.2599945, 172.3800049, 171.3600006, 181.5599976,
     340                             181.5599976, 187.6799927, 180.5399933, 169.3200073, 182.5800018,
     341                             178.5, 179.5200043, 184.6199951, 175.4400024, 175.4400024,
     342                             158.1000061, 182.5800018, 196.8600006, 167.2799988, 178.5,
     343                             174.4199982, 180.5399933, 195.8399963, 183.6000061, 200.9400024,
     344                             189.7200012, 186.6600037, 173.3999939, 173.3999939, 180.5399933,
     345                             172.3800049, 157.0800018, 163.1999969, 171.3600006, 190.7400055,
     346                             196.8600006, 179.5200043, 175.4400024, 169.3200073, 158.1000061,
     347                             157.0800018, 180.5399933, 173.3999939, 170.3399963, 175.4400024,
     348                             193.8000031, 170.3399963, 164.2200012, 174.4199982, 185.6399994,
     349                             178.5, 176.4600067, 176.4600067, 179.5200043, 176.4600067,
     350                             171.3600006, 205.0200043, 184.6199951, 180.5399933, 165.2400055,
     351                             167.2799988, 162.1799927, 165.2400055, 180.5399933, 169.3200073,
     352                             176.4600067, 182.5800018, 182.5800018, 175.4400024, 186.6600037,
     353                             182.5800018, 183.6000061, 163.1999969, 161.1600037, 189.7200012,
     354                             181.5599976, 187.6799927, 173.3999939, 173.3999939, 177.4799957,
     355                             179.5200043, 198.8999939, 177.4799957, 183.6000061, 154.0200043,
     356                             188.6999969, 181.5599976, 177.4799957, 174.4199982, 202.9799957,
     357                             168.3000031, 164.2200012, 187.6799927, 171.3600006, 189.7200012,
     358                             185.6399994, 187.6799927, 157.0800018, 193.8000031, 160.1399994,
     359                             166.2599945, 193.8000031, 166.2599945, 168.3000031, 179.5200043,
     360                             181.5599976, 172.3800049, 183.6000061, 184.6199951, 180.5399933,
     361                             177.4799957, 192.7799988, 171.3600006, 197.8800049, 190.7400055,
     362                             182.5800018, 178.5, 189.7200012, 172.3800049, 199.9199982,
     363                             183.6000061, 179.5200043, 170.3399963, 179.5200043, 181.5599976,
     364                             178.5, 186.6600037, 177.4799957, 160.1399994, 176.4600067,
     365                             173.3999939, 168.3000031, 180.5399933, 179.5200043, 175.4400024,
     366                             188.6999969, 175.4400024, 178.5, 161.1600037, 181.5599976,
     367                             184.6199951, 169.3200073, 187.6799927, 164.2200012, 176.4600067,
     368                             176.4600067, 174.4199982, 189.7200012, 192.7799988, 181.5599976,
     369                             165.2400055, 173.3999939, 184.6199951, 164.2200012, 181.5599976,
     370                             167.2799988, 157.0800018, 175.4400024, 172.3800049, 172.3800049,
     371                             170.3399963, 166.2599945, 185.6399994, 175.4400024, 184.6199951,
     372                             179.5200043, 198.8999939, 189.7200012, 164.2200012, 198.8999939,
     373                             169.3200073, 183.6000061, 191.7599945, 168.3000031, 178.5,
     374                             172.3800049, 169.3200073, 196.8600006, 170.3399963, 192.7799988,
     375                             183.6000061, 186.6600037, 181.5599976, 187.6799927, 198.8999939,
     376                             167.2799988, 177.4799957, 165.2400055, 173.3999939, 182.5800018,
     377                             190.7400055, 167.2799988, 184.6199951, 180.5399933, 165.2400055,
     378                             166.2599945, 162.1799927, 175.4400024, 169.3200073, 187.6799927,
     379                             155.0399933, 173.3999939, 165.2400055, 174.4199982, 183.6000061,
     380                             167.2799988, 186.6600037, 175.4400024, 173.3999939, 177.4799957,
     381                             192.7799988, 180.5399933, 191.7599945, 185.6399994, 194.8200073,
     382                             201.9600067, 166.2599945, 171.3600006, 177.4799957, 194.8200073,
     383                             191.7599945, 177.4799957, 167.2799988, 188.6999969, 172.3800049,
     384                             162.1799927, 169.3200073, 198.8999939, 183.6000061, 170.3399963,
     385                             190.7400055, 170.3399963, 169.3200073, 185.6399994, 181.5599976,
     386                             166.2599945, 187.6799927, 169.3200073, 157.0800018, 165.2400055,
     387                             176.4600067, 174.4199982, 166.2599945, 177.4799957, 195.8399963,
     388                             187.6799927, 186.6600037, 194.8200073, 181.5599976, 172.3800049,
     389                             166.2599945, 168.3000031, 183.6000061, 168.3000031, 174.4199982,
     390                             185.6399994, 180.5399933, 181.5599976, 189.7200012, 172.3800049,
     391                             183.6000061, 187.6799927, 183.6000061, 200.9400024, 184.6199951,
     392                             173.3999939, 176.4600067, 172.3800049, 169.3200073, 166.2599945,
     393                             186.6600037, 181.5599976, 161.1600037, 182.5800018, 179.5200043,
     394                             178.5, 174.4199982, 170.3399963, 179.5200043, 193.8000031,
     395                             188.6999969, 146.8800049, 192.7799988, 171.3600006, 178.5,
     396                             177.4799957, 184.6199951, 180.5399933, 163.1999969, 159.1199951,
     397                             160.1399994, 178.5, 176.4600067, 176.4600067, 192.7799988,
     398                             161.1600037, 166.2599945, 162.1799927, 172.3800049, 175.4400024,
     399                             168.3000031, 201.9600067, 188.6999969, 185.6399994, 175.4400024,
     400                             175.4400024, 182.5800018, 182.5800018, 172.3800049, 175.4400024,
     401                             179.5200043, 184.6199951, 163.1999969, 195.8399963, 180.5399933,
     402                             170.3399963, 212.1600037, 166.2599945, 187.6799927, 179.5200043,
     403                             178.5, 176.4600067, 172.3800049, 183.6000061, 179.5200043,
     404                             176.4600067, 185.6399994, 161.1600037, 187.6799927, 167.2799988,
     405                             187.6799927, 199.9199982, 187.6799927, 169.3200073, 158.1000061,
     406                             200.9400024, 191.7599945, 179.5200043, 170.3399963, 186.6600037,
     407                             170.3399963, 184.6199951, 189.7200012, 197.8800049, 186.6600037,
     408                             171.3600006, 164.2200012, 183.6000061, 180.5399933, 165.2400055,
     409                             160.1399994, 183.6000061, 166.2599945, 183.6000061, 196.8600006,
     410                             175.4400024, 172.3800049, 172.3800049, 181.5599976, 177.4799957,
     411                             173.3999939, 176.4600067, 180.5399933, 176.4600067, 178.5,
     412                             163.1999969, 189.7200012, 175.4400024, 174.4199982, 185.6399994,
     413                             182.5800018, 169.3200073, 194.8200073, 192.7799988, 188.6999969,
     414                             193.8000031, 175.4400024, 165.2400055, 180.5399933, 184.6199951,
     415                             176.4600067, 171.3600006, 188.6999969, 199.9199982, 183.6000061,
     416                             169.3200073, 175.4400024, 180.5399933, 174.4199982, 167.2799988,
     417                             184.6199951, 177.4799957, 180.5399933, 180.5399933, 184.6199951,
     418                             178.5, 186.6600037, 161.1600037, 183.6000061, 168.3000031,
     419                             188.6999969, 184.6199951, 171.3600006, 185.6399994, 167.2799988,
     420                             162.1799927, 186.6600037, 175.4400024, 166.2599945, 182.5800018,
     421                             171.3600006, 176.4600067, 192.7799988, 178.5, 168.3000031,
     422                             182.5800018, 184.6199951, 148.9199982, 172.3800049, 168.3000031,
     423                             181.5599976, 154.0200043, 166.2599945, 174.4199982, 166.2599945,
     424                             178.5, 184.6199951, 176.4600067, 171.3600006, 188.6999969,
     425                             177.4799957, 172.3800049, 176.4600067, 179.5200043, 176.4600067,
     426                             161.1600037, 166.2599945, 181.5599976, 170.3399963, 176.4600067,
     427                             163.1999969, 183.6000061, 206.0399933, 171.3600006, 186.6600037,
     428                             176.4600067, 163.1999969, 179.5200043, 176.4600067, 182.5800018,
     429                             167.2799988, 174.4199982, 188.6999969, 177.4799957, 190.7400055,
     430                             176.4600067, 175.4400024, 174.4199982, 179.5200043, 182.5800018,
     431                             182.5800018, 175.4400024, 165.2400055, 180.5399933, 178.5,
     432                             176.4600067, 191.7599945, 173.3999939, 186.6600037, 165.2400055,
     433                             173.3999939, 188.6999969, 172.3800049, 168.3000031, 193.8000031,
     434                             180.5399933, 172.3800049, 180.5399933, 189.7200012, 170.3399963,
     435                             170.3399963, 195.8399963, 173.3999939, 187.6799927, 165.2400055,
     436                             147.8999939, 173.3999939, 184.6199951, 171.3600006, 178.5,
     437                             163.1999969, 171.3600006, 190.7400055, 180.5399933, 156.0599976,
     438                             164.2200012, 172.3800049, 202.9799957, 201.9600067, 175.4400024,
     439                             193.8000031, 176.4600067, 182.5800018, 184.6199951, 178.5,
     440                             169.3200073, 166.2599945, 173.3999939, 182.5800018, 171.3600006,
     441                             167.2799988, 185.6399994, 179.5200043, 188.6999969, 194.8200073,
     442                             177.4799957, 177.4799957, 164.2200012, 189.7200012, 193.8000031,
     443                             173.3999939, 178.5, 184.6199951, 191.7599945, 191.7599945,
     444                             162.1799927, 171.3600006, 165.2400055, 197.8800049, 169.3200073,
     445                             178.5, 174.4199982, 183.6000061, 189.7200012, 181.5599976,
     446                             185.6399994, 179.5200043, 181.5599976, 161.1600037, 173.3999939,
     447                             187.6799927, 177.4799957, 182.5800018, 171.3600006, 188.6999969,
     448                             194.8200073, 176.4600067, 182.5800018, 157.0800018, 169.3200073,
     449                             170.3399963, 168.3000031, 190.7400055, 170.3399963, 185.6399994,
     450                             195.8399963, 172.3800049, 213.1799927, 187.6799927, 187.6799927,
     451                             178.5, 181.5599976, 183.6000061, 163.1999969, 169.3200073,
     452                             180.5399933, 204, 175.4400024, 158.1000061, 174.4199982,
     453                             183.6000061, 181.5599976, 181.5599976, 180.5399933, 172.3800049,
     454                             188.6999969, 166.2599945, 196.8600006, 187.6799927, 196.8600006,
     455                             164.2200012, 160.1399994, 165.2400055, 172.3800049, 177.4799957,
     456                             173.3999939, 168.3000031, 181.5599976, 164.2200012, 177.4799957,
     457                             182.5800018, 162.1799927, 168.3000031, 181.5599976, 179.5200043,
     458                             171.3600006, 173.3999939, 176.4600067, 197.8800049, 172.3800049,
     459                             189.7200012, 172.3800049, 161.1600037, 183.6000061, 173.3999939,
     460                             188.6999969, 161.1600037, 180.5399933, 160.1399994, 166.2599945,
     461                             164.2200012, 182.5800018, 169.3200073, 176.4600067, 186.6600037,
     462                             192.7799988, 163.1999969, 153, 182.5800018, 184.6199951,
     463                             166.2599945, 167.2799988, 186.6600037, 177.4799957, 174.4199982,
     464                             175.4400024, 164.2200012, 180.5399933, 178.5, 176.4600067,
     465                             164.2200012, 160.1399994, 178.5, 176.4600067, 180.5399933,
     466                             182.5800018, 179.5200043, 169.3200073, 178.5, 172.3800049,
     467                             171.3600006, 178.5, 166.2599945, 171.3600006, 181.5599976,
     468                             162.1799927, 157.0800018, 189.7200012, 177.4799957, 164.2200012,
     469                             179.5200043, 187.6799927, 172.3800049, 158.1000061, 164.2200012,
     470                             177.4799957, 165.2400055, 185.6399994, 174.4199982, 188.6999969,
     471                             173.3999939, 168.3000031, 164.2200012, 187.6799927, 209.1000061,
     472                             172.3800049, 177.4799957, 170.3399963, 172.3800049, 164.2200012,
     473                             190.7400055, 177.4799957, 165.2400055, 168.3000031, 173.3999939,
     474                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     475                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     476                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     477                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     478                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     479                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     480                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     481                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     482                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     483                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     484                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     485                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     486                             0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
     487                         };
     488
    217489int main (void)
    218490{
    219491    plan_tests(26);
    220492
    221     diag("psStats Tests with sample SDSS data from RHL");
     493    diag("psStats Tests with sample SDSS data from RHL and Megacam from EAM");
    222494    diag("this file does not yet define a specific test");
    223     diag ("the fitted mean is currently wrong for these two data sets");
     495    diag("the fitted mean is currently wrong for these two data sets");
    224496
    225497    {
     
    243515
    244516        psVectorStats (stats, y, NULL, NULL, 1);
    245         ok (1, "fitted  mean   %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
    246         ok (1, "robust  median %f, stdev %f", stats->robustMedian, stats->robustStdev);
    247         ok (1, "clipped mean   %f, stdev %f", stats->clippedMean,  stats->clippedStdev);
    248         ok (1, "sample  mean   %f, stdev %f", stats->sampleMean,   stats->sampleStdev);
    249         ok (1, "sample  median %f, stdev %f", stats->sampleMedian, stats->sampleStdev);
     517        ok (1, "sample  mean    %f, stdev %f", stats->sampleMean,   stats->sampleStdev);
     518        ok (1, "sample  median  %f, stdev %f", stats->sampleMedian, stats->sampleStdev);
     519        ok (1, "clipped mean    %f, stdev %f", stats->clippedMean,  stats->clippedStdev);
     520        ok (1, "robust  median  %f, stdev %f", stats->robustMedian, stats->robustStdev);
     521        ok (1, "fitted  mean    %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
     522        psFree (stats);
     523
     524        stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2 | PS_STAT_USE_BINSIZE);
     525        stats->binsize = 1.0;
     526        psVectorStats (stats, y, NULL, NULL, 1);
     527        ok (1, "fitted  mean v2 %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
     528        psFree (stats);
    250529
    251530        psFree (y);
    252         psFree (stats);
    253531        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    254532    }
     
    273551
    274552        psVectorStats (stats, y, NULL, NULL, 1);
    275         ok (1, "fitted  mean   %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
    276         ok (1, "robust  median %f, stdev %f", stats->robustMedian, stats->robustStdev);
    277         ok (1, "clipped mean   %f, stdev %f", stats->clippedMean,  stats->clippedStdev);
    278553        ok (1, "sample  mean   %f, stdev %f", stats->sampleMean,   stats->sampleStdev);
    279554        ok (1, "sample  median %f, stdev %f", stats->sampleMedian, stats->sampleStdev);
     555        ok (1, "clipped mean   %f, stdev %f", stats->clippedMean,  stats->clippedStdev);
     556        ok (1, "robust  median %f, stdev %f", stats->robustMedian, stats->robustStdev);
     557        ok (1, "fitted  mean   %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
     558        psFree (stats);
     559
     560        stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2 | PS_STAT_USE_BINSIZE);
     561        stats->binsize = 1.0;
     562        psVectorStats (stats, y, NULL, NULL, 1);
     563        ok (1, "fitted  mean v2 %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
     564        psFree (stats);
    280565
    281566        psFree (y);
    282         psFree (stats);
    283567        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
    284568    }
    285569
     570    {
     571        psMemId id = psMemGetId();
     572
     573        diag("sample 3");
     574        psStats *stats = psStatsAlloc (PS_STAT_FITTED_MEAN | PS_STAT_FITTED_STDEV |
     575                                       PS_STAT_ROBUST_MEDIAN | PS_STAT_ROBUST_STDEV |
     576                                       PS_STAT_CLIPPED_MEAN | PS_STAT_CLIPPED_STDEV |
     577                                       PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN |
     578                                       PS_STAT_SAMPLE_STDEV | PS_STAT_USE_BINSIZE);
     579        stats->binsize = 1.0;
     580
     581
     582        // copy data in static array
     583        int nPts = sizeof(yraw_03) / sizeof (float);
     584        psVector *y = psVectorAlloc (nPts, PS_TYPE_F32);
     585        for (int i = 0; i < y->n; i++) {
     586            y->data.F32[i] = yraw_03[i];
     587        }
     588
     589        psVectorStats (stats, y, NULL, NULL, 1);
     590        ok (1, "sample  mean   %f, stdev %f", stats->sampleMean,   stats->sampleStdev);
     591        ok (1, "sample  median %f, stdev %f", stats->sampleMedian, stats->sampleStdev);
     592        ok (1, "clipped mean   %f, stdev %f", stats->clippedMean,  stats->clippedStdev);
     593        ok (1, "robust  median %f, stdev %f", stats->robustMedian, stats->robustStdev);
     594        ok (1, "fitted  mean   %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
     595        psFree (stats);
     596
     597        stats = psStatsAlloc (PS_STAT_FITTED_MEAN_V2 | PS_STAT_FITTED_STDEV_V2 | PS_STAT_USE_BINSIZE);
     598        stats->binsize = 1.0;
     599        psVectorStats (stats, y, NULL, NULL, 1);
     600        ok (1, "fitted  mean v2 %f, stdev %f", stats->fittedMean,   stats->fittedStdev);
     601        psFree (stats);
     602
     603        psFree (y);
     604        ok(!psMemCheckLeaks (id, NULL, NULL, false), "no memory leaks");
     605    }
     606
    286607    return exit_status();
    287608}
Note: See TracChangeset for help on using the changeset viewer.