IPP Software Navigation Tools IPP Links Communication Pan-STARRS Links

Changeset 42506


Ignore:
Timestamp:
Aug 16, 2023, 10:30:15 AM (3 years ago)
Author:
eugene
Message:

add 2D, 3D, 4D versions of psVectorIRLSFitPolynomial, psPolynomialCopy, psPolynomialRecycle

Location:
branches/eam_branches/ipp-20230313/psLib
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • branches/eam_branches/ipp-20230313/psLib/src/math/psMinimizePolyFit.c

    r42496 r42506  
    6969if ((ORIG != NULL) && (ORIG->type.type != PS_TYPE_F64)) { psFree(TEMP); }
    7070
     71psVector *psVector_GetModifiedErrors_Caucy
     72( const psVector *f,
     73  const psVector *fEval,
     74  const psVector *fErr,
     75  const psVector *mask,
     76  psVectorMaskType maskValue);
     77
    7178/*****************************************************************************/
    7279/* TYPE DEFINITIONS                                                          */
     
    9097returned as a psVector sums.
    9198*****************************************************************************/
    92 static psVector *BuildSums1D(
    93     psVector* sums,
    94     psF64 x,
    95     psS32 nTerm)
     99static psVector *BuildSums1D
     100( psVector* sums,
     101  psF64 x,
     102  psS32 nTerm)
    96103{
    97104    psS32 nSum = 0;
     
    10311038}
    10321039
     1040// These should probably be tunable:
     1041# define FIT_TOLERANCE 1e-4
     1042# define FLT_TOLERANCE 1e-6
     1043# define WEIGHT_THRESHOLD 0.3
     1044
     1045// This function accepts F32 and F64 input vectors.
     1046bool psVectorIRLSFitPolynomial1D(
     1047    psPolynomial1D *poly,
     1048    psStats *stats,
     1049    const psVector *mask,
     1050    psVectorMaskType maskValue,
     1051    const psVector *f,
     1052    const psVector *fErr,
     1053    const psVector *xIn)
     1054{
     1055    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
     1056    PS_ASSERT (poly->type == PS_POLYNOMIAL_ORD, false); // XXX for now, only allow ORD
     1057    PS_ASSERT_POLY_NON_NULL(poly, false);
     1058    PS_ASSERT_VECTOR_NON_NULL(f, false);
     1059    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
     1060    if (mask != NULL) {
     1061        PS_ASSERT_VECTORS_SIZE_EQUAL(mask, f, false);
     1062        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     1063    }
     1064    if (fErr != NULL) {
     1065        PS_ASSERT_VECTORS_SIZE_EQUAL(fErr, f, false);
     1066        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
     1067    }
     1068    if (xIn != NULL) {
     1069        PS_ASSERT_VECTORS_SIZE_EQUAL(xIn, f, false);
     1070        PS_ASSERT_VECTOR_TYPE(xIn, f->type.type, false);
     1071    }
     1072
     1073    // Internal pointers for possibly NULL vectors.
     1074    psVector *x = (xIn != NULL) ? psMemIncrRefCounter((psVector *) xIn) : psVectorCreate(NULL, 0, f->n, 1, f->type.type);
     1075
     1076    // initial fit with nominal errors
     1077    if (!psVectorFitPolynomial1D(poly, mask, maskValue, f, fErr, x)) {
     1078        psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
     1079        return false;
     1080    }
     1081
     1082    // use polyOld to save the last fit
     1083    psPolynomial1D *polyOld = NULL;
     1084
     1085    // use clipIter as max number of iterations
     1086    bool converged = false;
     1087    for (psS32 N = 0; !converged && (N < stats->clipIter); N++) {
     1088        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial1D()\n", N);
     1089
     1090        // evaluate the fit at the input positions
     1091        psVector *fEval = psPolynomial1DEvalVector (poly, x);
     1092
     1093        // calculate modified errors based on the deviation from the fit
     1094        psVector *modErr = psVector_GetModifiedErrors_Caucy (f, fEval, fErr, mask, maskValue);
     1095        psFree (fEval);
     1096
     1097        // save the last fit (recycle the structure once allocated)
     1098        polyOld = psPolynomial1DCopy (polyOld, poly);
     1099
     1100        // calculate a new fit with modified errors:
     1101        if (!psVectorFitPolynomial1D(poly, mask, maskValue, f, modErr, x)) {
     1102            psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
     1103            psFree(x);
     1104            psFree(modErr);
     1105            return false;
     1106        }
     1107
     1108        // has the solution converged?
     1109        converged = true;
     1110        for (int ix = 0; ix <= poly->nX; ix++) {
     1111          if ((fabs(poly->coeff[ix] - polyOld->coeff[ix]) > FIT_TOLERANCE * fabs(poly->coeff[ix])) &&
     1112              (fabs(poly->coeff[ix] - polyOld->coeff[ix]) > FLT_TOLERANCE))
     1113            converged = false;
     1114        }
     1115
     1116# if (0)       
     1117        // XXX test:
     1118        FILE *ftest = fopen ("irls.wt.dat", "w");
     1119        for (int i = 0; i < modErr->n; i++) {
     1120            if (modErr->type.type == PS_TYPE_F64) {
     1121                fprintf (ftest, "%d %f\n", i, modErr->data.F64[i]);
     1122            } else {
     1123                fprintf (ftest, "%d %f\n", i, modErr->data.F32[i]);
     1124            }
     1125        }
     1126        fclose (ftest);
     1127# endif
     1128        psFree (modErr);
     1129    }
     1130
     1131    // Free local temporary variables
     1132    psFree(x);
     1133    psFree(polyOld);
     1134
     1135    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
     1136    return true;
     1137}
     1138
     1139/******************************************************************************
     1140 ******************************************************************************
     1141 2-D Vector Code.
     1142 ******************************************************************************
     1143 *****************************************************************************/
     1144
     1145/******************************************************************************
     1146VectorFitPolynomial2DOrd(myPoly, *mask, maskValue, *f, *fErr, *x, *y): This is
     1147a private routine which will fit a 2-D polynomial to a set of (x, y)-(f)
     1148pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
     1149 
     1150 *****************************************************************************/
     1151static bool VectorFitPolynomial2DOrd(
     1152    psPolynomial2D* myPoly,
     1153    const psVector* mask,
     1154    psVectorMaskType maskValue,
     1155    const psVector *f,
     1156    const psVector *fErr,
     1157    const psVector *x,
     1158    const psVector *y)
     1159{
     1160    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
     1161    PS_ASSERT_POLY_NON_NULL(myPoly, false);
     1162    PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
     1163    PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
     1164    PS_ASSERT_VECTOR_NON_NULL(f, false);
     1165    PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
     1166    if (fErr != NULL) {
     1167        PS_ASSERT_VECTORS_SIZE_EQUAL(y, fErr, false);
     1168        PS_ASSERT_VECTOR_TYPE(fErr, PS_TYPE_F64, false);
     1169    }
     1170    PS_ASSERT_VECTOR_NON_NULL(x, false);
     1171    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
     1172    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
     1173    PS_ASSERT_VECTOR_NON_NULL(y, false);
     1174    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
     1175    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
     1176    if (mask != NULL) {
     1177        PS_ASSERT_VECTORS_SIZE_EQUAL(y, mask, false);
     1178        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     1179    }
     1180
     1181    // Number of polynomial terms
     1182    int nXterm = 1 + myPoly->nX;      // Number of terms in x
     1183    int nYterm = 1 + myPoly->nY;      // Number of terms in y
     1184    int nTerm = nXterm * nYterm;            // Total number of terms
     1185
     1186    psImage *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
     1187    psVector *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
     1188
     1189    // Initialize data structures.
     1190    if (!psImageInit(A, 0.0) || !psVectorInit(B, 0.0)) {
     1191        psError(PS_ERR_UNKNOWN, false, "Could initialize data structures A, B.  Returning NULL.\n");
     1192        psFree(A);
     1193        psFree(B);
     1194        psTrace("psLib.math", 6, "---- %s() End ----\n", __func__);
     1195        return false;
     1196    }
     1197
     1198    // Dereference stuff, to make the loop go faster
     1199    psF64 **matrix = A->data.F64;       // Dereference the least-squares matrix
     1200    psF64 *vector = B->data.F64;        // Dereference the least-squares vector
     1201    psMaskType **coeffMask = myPoly->coeffMask;     // Dereference mask for polynomial terms
     1202    psVectorMaskType *dataMask = NULL;              // Dereference mask for data
     1203    if (mask) {
     1204        dataMask = mask->data.PS_TYPE_VECTOR_MASK_DATA;
     1205    }
     1206    psF64 *xData = x->data.F64;         // Dereference x
     1207    psF64 *yData = y->data.F64;         // Dereference y
     1208    psF64 *fData = f->data.F64;         // Dereference f
     1209    psF64 *fErrData = NULL;             // Dereference fErr
     1210    if (fErr) {
     1211        fErrData = fErr->data.F64;
     1212    }
     1213
     1214    // Build the least-squares matrix and vector
     1215    psImage *xySums = NULL;               // The sums: 1, x, x^2, ... x^(2n+1), y, xy, x^2y, ... x^(2n+1)
     1216    for (int k = 0; k < x->n; k++) {
     1217        if (dataMask && dataMask[k] & maskValue) {
     1218            continue;
     1219        }
     1220        xySums = BuildSums2D(xySums, xData[k], yData[k], nXterm, nYterm);
     1221        psF64 **sums = xySums->data.F64;// Dereference sums
     1222
     1223        double wt;                      // Weight
     1224        if (!fErrData) {
     1225            wt = 1.0;
     1226        } else {
     1227            // this filters fErr == 0 values
     1228            wt = (fErrData[k] == 0.0) ? 0.0 : 1.0 / PS_SQR(fErrData[k]);
     1229        }
     1230
     1231        // Iterating over the matrix
     1232        for (int i = 0; i < nTerm; i++) {
     1233            int l = i / nYterm;         // x index
     1234            int m = i % nYterm;         // y index
     1235            if (coeffMask[l][m] & PS_POLY_MASK_SET) {
     1236                matrix[i][i] = 1.0;
     1237                continue;
     1238            }
     1239            vector[i] += fData[k] * sums[l][m] * wt;
     1240            matrix[i][i] += sums[2*l][2*m] * wt; // The diagonal entry
     1241            for (int j = i + 1; j < nTerm; j++) { // Doing the upper diagonal only: we will use symmetry
     1242                int p = j / nYterm;     // x index
     1243                int q = j % nYterm;     // y index
     1244                if (coeffMask[p][q] & PS_POLY_MASK_SET) {
     1245                    continue;
     1246                }
     1247                double value = sums[l+p][m+q] * wt; // Value to add in
     1248                matrix[i][j] += value;
     1249                matrix[j][i] += value;  // Taking advantage of the symmetry
     1250            }
     1251        }
     1252    }
     1253    psFree(xySums);
     1254
     1255    // elements which are masked for fitting need to be subtracted from the vector
     1256    for (int i = 0; i < nTerm; i++) {
     1257        int ix = i / nYterm;         // x index
     1258        int iy = i % nYterm;         // y index
     1259        if (coeffMask[ix][iy] & PS_POLY_MASK_BOTH) {
     1260            continue;
     1261        }
     1262        for (int j = 0; j < nTerm; j++) { // The upper diagonal only: we will use symmetry
     1263            int jx = j / nYterm;         // x index
     1264            int jy = j % nYterm;         // y index
     1265            if (coeffMask[jx][jy] & PS_POLY_MASK_SET) {
     1266                continue;
     1267            }
     1268            if (!(coeffMask[jx][jy] & PS_POLY_MASK_FIT)) {
     1269                continue;
     1270            }
     1271            vector[i] -= matrix[i][j]*myPoly->coeff[jx][jy];
     1272        }
     1273    }
     1274   
     1275    // set the un-fitted and un-set elements to 0 or 1 for pivots
     1276    for (int i = 0; i < nTerm; i++) {
     1277        int ix = i / nYterm;         // x index
     1278        int iy = i % nYterm;         // y index
     1279        if (coeffMask[ix][iy] & PS_POLY_MASK_BOTH) {
     1280            for (int j = 0; j < nTerm; j++) { // The upper diagonal only: we will use symmetry
     1281                matrix[i][j] = 0.0;
     1282                matrix[j][i] = 0.0;
     1283            }
     1284            matrix[i][i] = 1.0;
     1285            continue;
     1286        }
     1287    }
     1288
     1289    if (psTraceGetLevel("psLib.math") >= 4) {
     1290        printf("Least-squares vector:\n");
     1291        for (int i = 0; i < nTerm; i++) {
     1292            printf("%f ", B->data.F64[i]);
     1293        }
     1294        printf("\n");
     1295        printf("Least-squares matrix:\n");
     1296        for (int i = 0; i < nTerm; i++) {
     1297            for (int j = 0; j < nTerm; j++) {
     1298                printf("%f ", A->data.F64[i][j]);
     1299            }
     1300            printf("\n");
     1301        }
     1302    }
     1303
     1304    bool status = false;
     1305    if (USE_GAUSS_JORDAN) {
     1306        status = psMatrixGJSolve(A, B);
     1307    } else {
     1308        status = psMatrixLUSolve(A, B);
     1309    }
     1310    if (!status) {
     1311        psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.\n");
     1312        goto escape;
     1313    }
     1314
     1315    // select the appropriate solution entries (retain the incoming values if masked on the fit)
     1316    for (int i = 0; i < nTerm; i++) {
     1317        int ix = i / nYterm;         // x index
     1318        int iy = i % nYterm;         // y index
     1319        if (coeffMask[ix][iy] & PS_POLY_MASK_FIT) continue;
     1320        myPoly->coeff[ix][iy] = B->data.F64[i];
     1321        myPoly->coeffErr[ix][iy] = sqrt(A->data.F64[i][i]);
     1322    }
     1323    psFree(A);
     1324    psFree(B);
     1325    return true;
     1326
     1327escape:
     1328    psFree (A);
     1329    psFree (B);
     1330    return false;
     1331}
     1332
     1333/******************************************************************************
     1334VectorFitPolynomial2DCheb(myPoly, *mask, maskValue, *f, *fErr, *x, *y): This is
     1335a private routine which will fit a 2-D polynomial to a set of (x, y)-(f)
     1336pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
     1337 
     1338 *****************************************************************************/
     1339static bool VectorFitPolynomial2DCheb(
     1340    psPolynomial2D* myPoly,
     1341    const psVector *f,
     1342    const psVector *x,
     1343    const psVector *y)
     1344{
     1345    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
     1346    PS_ASSERT_POLY_NON_NULL(myPoly, false);
     1347    PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
     1348    PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
     1349    PS_ASSERT_VECTOR_NON_NULL(f, false);
     1350    PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
     1351    PS_ASSERT_VECTOR_NON_NULL(x, false);
     1352    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
     1353    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
     1354    PS_ASSERT_VECTOR_NON_NULL(y, false);
     1355    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
     1356    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
     1357
     1358    // Number of polynomial terms
     1359    int nXterm = 1 + myPoly->nX;      // Number of terms in x
     1360    int nYterm = 1 + myPoly->nY;      // Number of terms in y
     1361    int nTerm = nXterm * nYterm;      // Total number of terms
     1362    if (nXterm > 9) {
     1363        psError(PS_ERR_UNKNOWN, false, "failed 2D chebyshev fit: orders higher than 9 are not yet coded\n");
     1364        return false;
     1365    }
     1366    if (nYterm > 9) {
     1367        psError(PS_ERR_UNKNOWN, false, "failed 2D chebyshev fit: orders higher than 9 are not yet coded\n");
     1368        return false;
     1369    }
     1370
     1371    // determine scale factors
     1372    if (!psChebyshevSetScale (myPoly, x, 0)) { psError(PS_ERR_UNKNOWN, false, "failed 2D chebyshev fit.\n"); return false; }
     1373    if (!psChebyshevSetScale (myPoly, y, 1)) { psError(PS_ERR_UNKNOWN, false, "failed 2D chebyshev fit.\n"); return false; }
     1374
     1375    // generate normalized vectors
     1376    psVector *xNorm = psChebyshevNormVector (myPoly, x, 0);
     1377    psVector *yNorm = psChebyshevNormVector (myPoly, y, 1);
     1378
     1379    // generate the N cheb polynomials based on xNorm, yNorm
     1380    psArray *xPolySet = psArrayAlloc (nXterm);
     1381    for (int i = 0; i < nXterm; i++) {
     1382        xPolySet->data[i] = psChebyshevPolyVector (xNorm, i);
     1383    }
     1384    psArray *yPolySet = psArrayAlloc (nYterm);
     1385    for (int i = 0; i < nYterm; i++) {
     1386        yPolySet->data[i] = psChebyshevPolyVector (yNorm, i);
     1387    }
     1388
     1389    psF64 *fData = f->data.F64;         // Dereference f
     1390
     1391    psImage *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
     1392    psVector *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
     1393
     1394    // Initialize data structures (should not be able to fail)
     1395    psAssert (psImageInit(A, 0.0), "Could initialize data structures A");
     1396    psAssert (psVectorInit(B, 0.0), "Could initialize data structures B");
     1397
     1398    // Dereference stuff, to make the loop go faster
     1399    psF64 **matrix = A->data.F64;       // Dereference the least-squares matrix
     1400    psF64 *vector = B->data.F64;        // Dereference the least-squares vector
     1401
     1402    // loop over all elements of the data vector
     1403    for (int k = 0; k < x->n; k++) {
     1404
     1405        if (!finite(fData[k])) continue;
     1406   
     1407        // XXX can we only calculate the upper diagonal?
     1408        int nelem = 0;
     1409        for (int jx = 0; jx < nXterm; jx++) {
     1410            psVector *jxCheb = xPolySet->data[jx];
     1411            for (int jy = 0; jy < nYterm; jy++) {
     1412                psVector *jyCheb = yPolySet->data[jy];
     1413                psF64 chebValue = jxCheb->data.F64[k] * jyCheb->data.F64[k];
     1414               
     1415                vector[nelem] += fData[k] * chebValue;
     1416
     1417                int melem = 0;
     1418                for (int kx = 0; kx < nXterm; kx++) {
     1419                    psVector *kxCheb = xPolySet->data[kx];
     1420                    for (int ky = 0; ky < nYterm; ky++) {
     1421                        psVector *kyCheb = yPolySet->data[ky];
     1422                        matrix[nelem][melem] += chebValue * kxCheb->data.F64[k]*kyCheb->data.F64[k];
     1423                        melem++;
     1424                    }
     1425                }
     1426                nelem++;
     1427            }
     1428        }
     1429    }
     1430
     1431    if (psTraceGetLevel("psLib.math") >= 4) {
     1432        printf("Least-squares vector:\n");
     1433        for (int i = 0; i < nTerm; i++) {
     1434            printf("%f ", B->data.F64[i]);
     1435        }
     1436        printf("\n");
     1437        printf("Least-squares matrix:\n");
     1438        for (int i = 0; i < nTerm; i++) {
     1439            for (int j = 0; j < nTerm; j++) {
     1440                printf("%f ", A->data.F64[i][j]);
     1441            }
     1442            printf("\n");
     1443        }
     1444    }
     1445
     1446    bool status = false;
     1447    if (USE_GAUSS_JORDAN) {
     1448        status = psMatrixGJSolve(A, B);
     1449    } else {
     1450        status = psMatrixLUSolve(A, B);
     1451    }
     1452    if (!status) {
     1453        psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.\n");
     1454        goto escape;
     1455    }
     1456
     1457    // unroll the result:
     1458    int nelem = 0;
     1459    for (int jx = 0; jx < nXterm; jx++) {
     1460        for (int jy = 0; jy < nYterm; jy++) {
     1461            myPoly->coeff[jx][jy]    = B->data.F64[nelem];
     1462            myPoly->coeffErr[jx][jy] = sqrt(A->data.F64[nelem][nelem]);
     1463            nelem ++;
     1464        }
     1465    }
     1466    psFree(A);
     1467    psFree(B);
     1468
     1469    psFree (xNorm);
     1470    psFree (yNorm);
     1471    psFree (xPolySet);
     1472    psFree (yPolySet);
     1473
     1474    return true;
     1475
     1476escape:
     1477    psFree (A);
     1478    psFree (B);
     1479    return false;
     1480}
     1481
     1482/******************************************************************************
     1483psVectorFitPolynomial2D():  This routine fits a 2D polynomial of arbitrary
     1484degree (specified in poly) to the data points (x, y)-(f) and returns that
     1485polynomial.  Types F32 and F64 are supported, however, type F32 is done via
     1486vector conversion only.
     1487 *****************************************************************************/
     1488bool psVectorFitPolynomial2D(
     1489    psPolynomial2D *poly,
     1490    const psVector *mask,
     1491    psVectorMaskType maskValue,
     1492    const psVector *f,
     1493    const psVector *fErr,
     1494    const psVector *x,
     1495    const psVector *y)
     1496{
     1497    PS_ASSERT_POLY_NON_NULL(poly, false);
     1498    // PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
     1499
     1500    PS_ASSERT_VECTOR_NON_NULL(f, false);
     1501    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
     1502    PS_ASSERT_VECTOR_NON_NULL(x, false);
     1503    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
     1504    PS_ASSERT_VECTOR_NON_NULL(y, false);
     1505    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
     1506    if (mask != NULL) {
     1507        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
     1508        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     1509    }
     1510    if (fErr != NULL) {
     1511        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
     1512        PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, false);
     1513    }
     1514
     1515    // Convert input vectors to F64 if necessary.
     1516    psVector *f64 = (f->type.type == PS_TYPE_F64) ? (psVector *) f : psVectorCopy(NULL, f, PS_TYPE_F64);
     1517    psVector *x64 = (x->type.type == PS_TYPE_F64) ? (psVector *) x : psVectorCopy(NULL, x, PS_TYPE_F64);
     1518    psVector *y64 = (y->type.type == PS_TYPE_F64) ? (psVector *) y : psVectorCopy(NULL, y, PS_TYPE_F64);
     1519
     1520    psVector *fErr64 = NULL;
     1521    if (fErr != NULL) {
     1522        fErr64 = (fErr->type.type == PS_TYPE_F64) ? (psVector *) fErr : psVectorCopy(NULL, fErr, PS_TYPE_F64);
     1523    }
     1524
     1525    bool result = true;
     1526
     1527    switch (poly->type) {
     1528    case PS_POLYNOMIAL_ORD:
     1529        result = VectorFitPolynomial2DOrd(poly, mask, maskValue, f64, fErr64, x64, y64);
     1530        if (!result) {
     1531            psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
     1532        }
     1533        break;
     1534    case PS_POLYNOMIAL_CHEB:
     1535      if (mask != NULL) {
     1536          psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring mask and maskValue with Chebyshev polynomials.\n");
     1537      }
     1538      if (fErr != NULL) {
     1539          psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring error values for Chebyshev polynomials.\n");
     1540      }
     1541      result = VectorFitPolynomial2DCheb(poly, f64, x64, y64);
     1542      if (!result) {
     1543          psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
     1544      }
     1545      break;
     1546    default:
     1547        psError(PS_ERR_UNKNOWN, true, "Incorrect polynomial type.  Returning NULL.\n");
     1548        result = false;
     1549        break;
     1550    }
     1551
     1552    // Free psVectors that were created for NULL arguments.
     1553    PS_FREE_TEMP_F64_VECTOR (f, f64);
     1554    PS_FREE_TEMP_F64_VECTOR (x, x64);
     1555    PS_FREE_TEMP_F64_VECTOR (y, y64);
     1556    PS_FREE_TEMP_F64_VECTOR (fErr, fErr64);
     1557
     1558    return result;
     1559}
     1560
     1561bool psVectorClipFitPolynomial2D(
     1562    psPolynomial2D *poly,
     1563    psStats *stats,
     1564    const psVector *mask,
     1565    psVectorMaskType maskValue,
     1566    const psVector *f,
     1567    const psVector *fErr,
     1568    const psVector *x,
     1569    const psVector *y)
     1570{
     1571    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
     1572    PS_ASSERT_POLY_NON_NULL(poly, false);
     1573    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
     1574    PS_ASSERT_PTR_NON_NULL(stats, false);
     1575    PS_ASSERT_VECTOR_NON_NULL(mask, false);
     1576    PS_ASSERT_VECTOR_NON_NULL(f, false);
     1577    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
     1578
     1579    PS_ASSERT_VECTOR_NON_NULL(x, false);
     1580    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
     1581    PS_ASSERT_VECTOR_TYPE(x, f->type.type, false);
     1582
     1583    PS_ASSERT_VECTOR_NON_NULL(y, false);
     1584    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
     1585    PS_ASSERT_VECTOR_TYPE(y, f->type.type, false);
     1586
     1587    PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
     1588    PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     1589
     1590    if (fErr != NULL) {
     1591        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
     1592        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
     1593    }
     1594
     1595    // the user supplies one of various stats option pairs,
     1596    // determine the desired mean and stdev STATS options:
     1597    // XXX enforce consistency?
     1598    // XXX psStatsGetValue() probably has inverted precedence
     1599    psStatsOptions meanOption = stats->options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN | PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN);
     1600    psStatsOptions stdevOption = stats->options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV | PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV);
     1601    if (!meanOption) {
     1602        psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
     1603        return false;
     1604    }
     1605    if (!stdevOption) {
     1606        psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
     1607        return false;
     1608    }
     1609
     1610    // clipping range defined by min and max and/or clipSigma
     1611    psF32 minClipSigma;
     1612    psF32 maxClipSigma;
     1613    if (isfinite(stats->max)) {
     1614        maxClipSigma = fabs(stats->max);
     1615    } else {
     1616        maxClipSigma = fabs(stats->clipSigma);
     1617    }
     1618    if (isfinite(stats->min)) {
     1619        minClipSigma = fabs(stats->min);
     1620    } else {
     1621        minClipSigma = fabs(stats->clipSigma);
     1622    }
     1623    psVector *resid = psVectorAlloc(f->n, PS_TYPE_F64);
     1624
     1625    psTrace("psLib.math", 4, "stats->clipIter is %d\n", stats->clipIter);
     1626    psTrace("psLib.math", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
     1627
     1628    for (psS32 N = 0; N < stats->clipIter; N++) {
     1629        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial1D()\n", N);
     1630        psS32 Nkeep = 0;
     1631        if (psTraceGetLevel("psLib.math") >= 7) {
     1632            if (mask != NULL) {
     1633                for (psS32 i = 0 ; i < mask->n ; i++) {
     1634                    psTrace("psLib.math", 7,  "mask[%d] is %d\n", i, mask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
     1635                }
     1636            }
     1637        }
     1638
     1639        if (!psVectorFitPolynomial2D(poly, mask, maskValue, f, fErr, x, y)) {
     1640            psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the data.  Returning false.\n");
     1641            psFree(resid);
     1642            return false;
     1643        }
     1644
     1645        psVector *fit = psPolynomial2DEvalVector(poly, x, y);
     1646        if (fit == NULL) {
     1647            psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial3DEvalVector().  Returning NULL.\n");
     1648            psFree(resid);
     1649            return false;
     1650        }
     1651
     1652        for (psS32 i = 0 ; i < f->n ; i++) {
     1653            if (f->type.type == PS_TYPE_F64) {
     1654                resid->data.F64[i] = f->data.F64[i] - fit->data.F64[i];
     1655            } else {
     1656                resid->data.F64[i] = (psF64) (f->data.F32[i] - fit->data.F32[i]);
     1657            }
     1658        }
     1659
     1660        if (psTraceGetLevel("psLib.math") >= 7) {
     1661            if (mask != NULL) {
     1662                for (psS32 i = 0 ; i < mask->n ; i++) {
     1663                    if (!((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue))) {
     1664                        psTrace("psLib.math", 7,  "point %d at %f %f : value, fit : %f  %f resid: %f\n",
     1665                                i, x->data.F32[i], y->data.F32[i], f->data.F32[i], fit->data.F32[i], resid->data.F64[i]);
     1666                    }
     1667                }
     1668            }
     1669        }
     1670
     1671        if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
     1672            psError(PS_ERR_UNKNOWN, false, "Could not compute statistics on the resid vector.  Returning NULL.\n");
     1673            psFree(resid);
     1674            psFree(fit);
     1675            return false;
     1676        }
     1677
     1678        double meanValue = psStatsGetValue (stats, meanOption);
     1679        double stdevValue = psStatsGetValue (stats, stdevOption);
     1680
     1681        psTrace("psLib.math", 5, "Mean is %f\n", meanValue);
     1682        psTrace("psLib.math", 5, "Stdev is %f\n", stdevValue);
     1683        psF32 minClipValue = -minClipSigma*stdevValue;
     1684        psF32 maxClipValue = +maxClipSigma*stdevValue;
     1685
     1686        // set mask if pts are not valid
     1687        // we are masking out any point which is out of range
     1688        // recovery is not allowed with this scheme
     1689        for (psS32 i = 0; i < resid->n; i++) {
     1690            if ((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue)) {
     1691                continue;
     1692            }
     1693
     1694            if ((resid->data.F64[i] - meanValue > maxClipValue) || (resid->data.F64[i] - meanValue < minClipValue)) {
     1695                if (fit->type.type == PS_TYPE_F64) {
     1696                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
     1697                            i, fit->data.F64[i], i, resid->data.F64[i]);
     1698                } else {
     1699                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
     1700                            i, fit->data.F32[i], i, resid->data.F64[i]);
     1701                }
     1702
     1703                if (mask != NULL) {
     1704                    mask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= 0x01;
     1705                }
     1706                continue;
     1707            }
     1708            Nkeep++;
     1709        }
     1710        psTrace("psLib.math", 4, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
     1711        stats->clippedNvalues = Nkeep;
     1712        psFree(fit);
     1713    }
     1714    // Free local temporary variables
     1715    psFree(resid);
     1716
     1717    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
     1718    return true;
     1719}
     1720
     1721// This function accepts F32 and F64 input vectors.
     1722bool psVectorIRLSFitPolynomial2D(
     1723    psPolynomial2D *poly,
     1724    psStats *stats,
     1725    const psVector *mask,
     1726    psVectorMaskType maskValue,
     1727    const psVector *f,
     1728    const psVector *fErr,
     1729    const psVector *xIn,
     1730    const psVector *yIn)
     1731{
     1732    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
     1733
     1734    PS_ASSERT (poly->type == PS_POLYNOMIAL_ORD, false); // XXX for now, only allow ORD
     1735    PS_ASSERT_POLY_NON_NULL(poly, false);
     1736    PS_ASSERT_VECTOR_NON_NULL(f, false);
     1737    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
     1738    if (mask != NULL) {
     1739        PS_ASSERT_VECTORS_SIZE_EQUAL(mask, f, false);
     1740        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     1741    }
     1742    if (fErr != NULL) {
     1743        PS_ASSERT_VECTORS_SIZE_EQUAL(fErr, f, false);
     1744        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
     1745    }
     1746    if (xIn != NULL) {
     1747        PS_ASSERT_VECTORS_SIZE_EQUAL(xIn, f, false);
     1748        PS_ASSERT_VECTOR_TYPE(xIn, f->type.type, false);
     1749    }
     1750    if (yIn != NULL) {
     1751        PS_ASSERT_VECTORS_SIZE_EQUAL(yIn, f, false);
     1752        PS_ASSERT_VECTOR_TYPE(yIn, f->type.type, false);
     1753    }
     1754
     1755    // Internal pointers for possibly NULL vectors. 
     1756    psVector *x = (xIn != NULL) ? psMemIncrRefCounter((psVector *) xIn) : psVectorCreate(NULL, 0, f->n, 1, f->type.type);
     1757    psVector *y = (yIn != NULL) ? psMemIncrRefCounter((psVector *) yIn) : psVectorCreate(NULL, 0, f->n, 1, f->type.type);
     1758
     1759    // initial fit with nominal errors
     1760    if (!psVectorFitPolynomial2D(poly, mask, maskValue, f, fErr, x, y)) {
     1761        psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
     1762        psFree(x);
     1763        psFree(y);
     1764        return false;
     1765    }
     1766
     1767    // use polyOld to save the last fit
     1768    psPolynomial2D *polyOld = NULL;
     1769
     1770    // use clipIter as max number of iterations
     1771    bool converged = false;
     1772    for (psS32 N = 0; !converged && (N < stats->clipIter); N++) {
     1773        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial2D()\n", N);
     1774
     1775        // evaluate the fit at the input positions
     1776        psVector *fEval = psPolynomial2DEvalVector (poly, x, y);
     1777
     1778        // calculate modified errors based on the deviation from the fit
     1779        psVector *modErr = psVector_GetModifiedErrors_Caucy (f, fEval, fErr, mask, maskValue);
     1780        psFree (fEval);
     1781
     1782        // save the last fit (recycle the structure once allocated)
     1783        polyOld = psPolynomial2DCopy (polyOld, poly);
     1784
     1785        // calculate a new fit with modified errors:
     1786        if (!psVectorFitPolynomial2D(poly, mask, maskValue, f, modErr, x, y)) {
     1787            psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
     1788            psFree(x);
     1789            psFree(y);
     1790            psFree(modErr);
     1791            return false;
     1792        }
     1793
     1794        // has the solution converged?
     1795        converged = true;
     1796        for (int ix = 0; ix <= poly->nX; ix++) {
     1797            for (int iy = 0; iy <= poly->nY; iy++) {
     1798                if ((fabs(poly->coeff[ix][iy] - polyOld->coeff[ix][iy]) > FIT_TOLERANCE * fabs(poly->coeff[ix][iy])) &&
     1799                    (fabs(poly->coeff[ix][iy] - polyOld->coeff[ix][iy]) > FLT_TOLERANCE))
     1800                    converged = false;
     1801            }
     1802        }
     1803        psFree (modErr);
     1804    }
     1805
     1806    // Free local temporary variables
     1807    psFree(x);
     1808    psFree(y);
     1809    psFree(polyOld);
     1810
     1811    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
     1812    return true;
     1813}
     1814
     1815/******************************************************************************
     1816 ******************************************************************************
     1817 3-D Vector Code.
     1818 ******************************************************************************
     1819 *****************************************************************************/
     1820
     1821/******************************************************************************
     1822VectorFitPolynomial3DOrd(myPoly, *mask, maskValue, *f, *fErr, *x, *y, *z):
     1823This is a private routine which will fit a 3-D polynomial to a set of (x,
     1824y, z)-(f) pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
     1825 
     1826 *****************************************************************************/
     1827static bool VectorFitPolynomial3DOrd(
     1828    psPolynomial3D* myPoly,
     1829    const psVector* mask,
     1830    psVectorMaskType maskValue,
     1831    const psVector *f,
     1832    const psVector *fErr,
     1833    const psVector *x,
     1834    const psVector *y,
     1835    const psVector *z)
     1836{
     1837    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
     1838    PS_ASSERT_POLY_NON_NULL(myPoly, false);
     1839    PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
     1840    PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
     1841    PS_ASSERT_INT_NONNEGATIVE(myPoly->nZ, false);
     1842
     1843    PS_ASSERT_VECTOR_NON_NULL(f, false);
     1844    PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
     1845    if (fErr != NULL) {
     1846        PS_ASSERT_VECTORS_SIZE_EQUAL(y, fErr, false);
     1847        PS_ASSERT_VECTOR_TYPE(fErr, PS_TYPE_F64, false);
     1848    }
     1849    PS_ASSERT_VECTOR_NON_NULL(x, false);
     1850    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
     1851    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
     1852    PS_ASSERT_VECTOR_NON_NULL(y, false);
     1853    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
     1854    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
     1855    PS_ASSERT_VECTOR_NON_NULL(z, false);
     1856    PS_ASSERT_VECTOR_TYPE(z, PS_TYPE_F64, false);
     1857    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
     1858    if (mask != NULL) {
     1859        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
     1860        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     1861    }
     1862
     1863    int nXterm = 1 + myPoly->nX;        // Number of x terms
     1864    int nYterm = 1 + myPoly->nY;        // Number of y terms
     1865    int nZterm = 1 + myPoly->nZ;        // Number of z terms
     1866    int nTerm = nXterm * nYterm * nZterm; // Total number of terms
     1867    int nData = x->n;                   // Number of data points
     1868    psImage    *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
     1869    psVector   *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
     1870
     1871    // Initialize data structures.
     1872    if (!psImageInit(A, 0.0) || !psVectorInit(B, 0.0)) {
     1873        psError(PS_ERR_UNKNOWN, false, "Could initialize data structures A, B.  Returning NULL.\n");
     1874        psFree(A);
     1875        psFree(B);
     1876        psTrace("psLib.math", 4, "---- %s() End ----\n", __func__);
     1877        return false;
     1878    }
     1879
     1880    // Dereference points for speed in the loop
     1881    psF64 **matrix = A->data.F64;       // Least-squares matrix
     1882    psF64 *vector = B->data.F64;        // Least-squares vector
     1883    psF64 *xData = x->data.F64;         // x
     1884    psF64 *yData = y->data.F64;         // y
     1885    psF64 *zData = z->data.F64;         // z
     1886    psF64 *fData = f->data.F64;         // f
     1887    psF64 *fErrData = NULL;             // Error in f
     1888    if (fErr) {
     1889        fErrData = fErr->data.F64;
     1890    }
     1891    psVectorMaskType *dataMask = NULL;              // Mask for data
     1892    if (mask) {
     1893        dataMask = mask->data.PS_TYPE_VECTOR_MASK_DATA;
     1894    }
     1895    psMaskType ***coeffMask = myPoly->coeffMask;    // Mask for polynomial terms
     1896    int nYZterm = nYterm * nZterm;      // Multiplication of the numbers, to calculate the index
     1897
     1898    // Build the B and A data structs.
     1899    psF64 ***Sums = NULL;         // Sums look like: 1, x, x^2, ... x^(2n+1), y, xy, x^2y, ... x^(2n+1)*y, ...
     1900    for (int k = 0; k < nData; k++) {
     1901        if (dataMask && dataMask[k] & maskValue) {
     1902            continue;
     1903        }
     1904
     1905        Sums = BuildSums3D(Sums, xData[k], yData[k], zData[k], nXterm, nYterm, nZterm);
     1906
     1907        double wt;
     1908        if (fErr == NULL) {
     1909            wt = 1.0;
     1910        } else {
     1911            // this filters fErr == 0 values
     1912            wt = (fErr->data.F64[k] == 0.0) ? 0.0 : 1.0 / PS_SQR(fErrData[k]);
     1913        }
     1914
     1915        for (int i = 0; i < nTerm; i++) {
     1916            int ix = i / nYZterm; // x index
     1917            int iy = (i % nYZterm) / nZterm; // y index
     1918            int iz = (i % nYZterm) % nZterm; // z index
     1919            if (coeffMask[ix][iy][iz] & PS_POLY_MASK_BOTH) {
     1920                matrix[i][i] = 1.0;
     1921                continue;
     1922            }
     1923
     1924            vector[i] += fData[k] * Sums[ix][iy][iz] * wt;
     1925            matrix[i][i] += Sums[2*ix][2*iy][2*iz] * wt;
     1926            for (int j = i + 1; j < nTerm; j++) {
     1927                int jx = j / (nYZterm); // x index
     1928                int jy = (j % nYZterm) / nZterm; // y index
     1929                int jz = (j % nYZterm) % nZterm; // z index
     1930                if (coeffMask[jx][jy][jz] & PS_POLY_MASK_BOTH) {
     1931                    continue;
     1932                }
     1933                double value = Sums[ix+jx][iy+jy][iz+jz] * wt;
     1934                matrix[i][j] += value;
     1935                matrix[j][i] += value;
     1936            }
     1937        }
     1938    }
     1939
     1940    // Free the sums
     1941    for (psS32 ix = 0; ix < 2*nXterm; ix++) {
     1942        for (psS32 iy = 0; iy < 2*nYterm; iy++) {
     1943            psFree(Sums[ix][iy]);
     1944        }
     1945        psFree(Sums[ix]);
     1946    }
     1947    psFree(Sums);
     1948
     1949
     1950    bool status = false;
     1951    if (USE_GAUSS_JORDAN) {
     1952        status = psMatrixGJSolve(A, B);
     1953    } else {
     1954        status = psMatrixLUSolve(A, B);
     1955    }
     1956    if (!status) {
     1957        psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.\n");
     1958        goto escape;
     1959    }
     1960
     1961    // select the appropriate solution entries
     1962    for (int i = 0; i < nTerm; i++) {
     1963        int ix = i / nYZterm; // x index
     1964        int iy = (i % nYZterm) / nZterm; // y index
     1965        int iz = (i % nYZterm) % nZterm; // z index
     1966        if (coeffMask[ix][iy][iz] & PS_POLY_MASK_FIT) continue;
     1967        myPoly->coeff[ix][iy][iz] = B->data.F64[i];
     1968        myPoly->coeffErr[ix][iy][iz] = sqrt(A->data.F64[i][i]);
     1969    }
     1970    psFree(A);
     1971    psFree(B);
     1972    return true;
     1973
     1974escape:
     1975    psFree(A);
     1976    psFree(B);
     1977    return false;
     1978}
     1979
     1980/******************************************************************************
     1981psVectorFitPolynomial3D():  This routine fits a 3D polynomial of arbitrary
     1982degree (specified in poly) to the data points (x, y, z)-(f) and returns that
     1983polynomial.  Types F32 and F64 are supported, however, type F32 is done via
     1984vector conversion only.
     1985 *****************************************************************************/
     1986bool psVectorFitPolynomial3D(
     1987    psPolynomial3D *poly,
     1988    const psVector *mask,
     1989    psVectorMaskType maskValue,
     1990    const psVector *f,
     1991    const psVector *fErr,
     1992    const psVector *x,
     1993    const psVector *y,
     1994    const psVector *z)
     1995{
     1996    PS_ASSERT_POLY_NON_NULL(poly, false);
     1997    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
     1998
     1999    PS_ASSERT_VECTOR_NON_NULL(f, false);
     2000    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
     2001    PS_ASSERT_VECTOR_NON_NULL(x, false);
     2002    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
     2003    PS_ASSERT_VECTOR_NON_NULL(y, false);
     2004    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
     2005    PS_ASSERT_VECTOR_NON_NULL(z, false);
     2006    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
     2007    if (mask != NULL) {
     2008        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
     2009        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     2010    }
     2011    if (fErr != NULL) {
     2012        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
     2013        PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, false);
     2014    }
     2015
     2016    // Convert input vectors to F64 if necessary.
     2017    psVector *f64 = (f->type.type == PS_TYPE_F64) ? (psVector *) f : psVectorCopy(NULL, f, PS_TYPE_F64);
     2018    psVector *x64 = (x->type.type == PS_TYPE_F64) ? (psVector *) x : psVectorCopy(NULL, x, PS_TYPE_F64);
     2019    psVector *y64 = (y->type.type == PS_TYPE_F64) ? (psVector *) y : psVectorCopy(NULL, y, PS_TYPE_F64);
     2020    psVector *z64 = (z->type.type == PS_TYPE_F64) ? (psVector *) z : psVectorCopy(NULL, z, PS_TYPE_F64);
     2021
     2022    psVector *fErr64 = NULL;
     2023    if (fErr != NULL) {
     2024        fErr64 = (fErr->type.type == PS_TYPE_F64) ? (psVector *) fErr : psVectorCopy(NULL, fErr, PS_TYPE_F64);
     2025    }
     2026
     2027    bool result = true;
     2028
     2029    switch (poly->type) {
     2030    case PS_POLYNOMIAL_ORD:
     2031        result = VectorFitPolynomial3DOrd(poly, mask, maskValue, f64, fErr64, x64, y64, z64);
     2032        if (!result) {
     2033            psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
     2034        }
     2035        break;
     2036    case PS_POLYNOMIAL_CHEB:
     2037        if (mask != NULL) {
     2038            psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring mask and maskValue with Chebyshev polynomials.\n");
     2039        }
     2040        psError(PS_ERR_UNKNOWN, true, "3-D Chebyshev polynomial vector fitting has not been implemented.  Returning NULL.\n");
     2041        result = false;
     2042        break;
     2043    default:
     2044        psError(PS_ERR_UNKNOWN, true, "Incorrect polynomial type.  Returning NULL.\n");
     2045        result = false;
     2046        break;
     2047    }
     2048
     2049    // Free psVectors that were created for NULL arguments.
     2050    PS_FREE_TEMP_F64_VECTOR (f, f64);
     2051    PS_FREE_TEMP_F64_VECTOR (x, x64);
     2052    PS_FREE_TEMP_F64_VECTOR (y, y64);
     2053    PS_FREE_TEMP_F64_VECTOR (z, z64);
     2054    PS_FREE_TEMP_F64_VECTOR (fErr, fErr64);
     2055
     2056    return result;
     2057}
     2058
     2059bool psVectorClipFitPolynomial3D(
     2060    psPolynomial3D *poly,
     2061    psStats *stats,
     2062    const psVector *mask,
     2063    psVectorMaskType maskValue,
     2064    const psVector *f,
     2065    const psVector *fErr,
     2066    const psVector *x,
     2067    const psVector *y,
     2068    const psVector *z)
     2069{
     2070    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
     2071    PS_ASSERT_POLY_NON_NULL(poly, false);
     2072    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
     2073    PS_ASSERT_PTR_NON_NULL(stats, false);
     2074    PS_ASSERT_VECTOR_NON_NULL(mask, false);
     2075    PS_ASSERT_VECTOR_NON_NULL(f, false);
     2076    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
     2077
     2078    PS_ASSERT_VECTOR_NON_NULL(x, false);
     2079    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
     2080    PS_ASSERT_VECTOR_TYPE(x, f->type.type, false);
     2081
     2082    PS_ASSERT_VECTOR_NON_NULL(y, false);
     2083    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
     2084    PS_ASSERT_VECTOR_TYPE(y, f->type.type, false);
     2085
     2086    PS_ASSERT_VECTOR_NON_NULL(z, false);
     2087    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
     2088    PS_ASSERT_VECTOR_TYPE(z, f->type.type, false);
     2089
     2090    PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
     2091    PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     2092
     2093    if (fErr != NULL) {
     2094        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
     2095        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
     2096    }
     2097
     2098    // the user supplies one of various stats option pairs,
     2099    // determine the desired mean and stdev STATS options:
     2100    // XXX enforce consistency?
     2101    // XXX psStatsGetValue() probably has inverted precedence
     2102    psStatsOptions meanOption = stats->options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN | PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN);
     2103    psStatsOptions stdevOption = stats->options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV | PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV);
     2104    if (!meanOption) {
     2105        psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
     2106        return false;
     2107    }
     2108    if (!stdevOption) {
     2109        psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
     2110        return false;
     2111    }
     2112
     2113    // clipping range defined by min and max and/or clipSigma
     2114    psF32 minClipSigma;
     2115    psF32 maxClipSigma;
     2116    if (isfinite(stats->max)) {
     2117        maxClipSigma = fabs(stats->max);
     2118    } else {
     2119        maxClipSigma = fabs(stats->clipSigma);
     2120    }
     2121    if (isfinite(stats->min)) {
     2122        minClipSigma = fabs(stats->min);
     2123    } else {
     2124        minClipSigma = fabs(stats->clipSigma);
     2125    }
     2126    psVector *resid = psVectorAlloc(f->n, PS_TYPE_F64);
     2127
     2128    psTrace("psLib.math", 4, "stats->clipIter is %d\n", stats->clipIter);
     2129    psTrace("psLib.math", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
     2130
     2131    for (psS32 N = 0; N < stats->clipIter; N++) {
     2132        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial1D()\n", N);
     2133        psS32 Nkeep = 0;
     2134        if (psTraceGetLevel("psLib.math") >= 6) {
     2135            if (mask != NULL) {
     2136                for (psS32 i = 0 ; i < mask->n ; i++) {
     2137                    psTrace("psLib.math", 6,  "mask[%d] is %d\n", i, mask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
     2138                }
     2139            }
     2140        }
     2141
     2142        if (!psVectorFitPolynomial3D(poly, mask, maskValue, f, fErr, x, y, z)) {
     2143            psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the data.  Returning NULL.\n");
     2144            psFree(resid);
     2145            return false;
     2146        }
     2147        psVector *fit = psPolynomial3DEvalVector(poly, x, y, z);
     2148        if (fit == NULL) {
     2149            psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial3DEvalVector().  Returning NULL.\n");
     2150            psFree(resid);
     2151            return false;
     2152        }
     2153        for (psS32 i = 0 ; i < f->n ; i++) {
     2154            if (f->type.type == PS_TYPE_F64) {
     2155                resid->data.F64[i] = f->data.F64[i] - fit->data.F64[i];
     2156            } else {
     2157                resid->data.F64[i] = ((psF64) f->data.F32[i]) - fit->data.F64[i];
     2158            }
     2159        }
     2160
     2161        if (psTraceGetLevel("psLib.math") >= 6) {
     2162            if (mask != NULL) {
     2163                for (psS32 i = 0 ; i < mask->n ; i++) {
     2164                    if (!((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue))) {
     2165                        psTrace("psLib.math", 6,  "(f, fit)[%d] is (%f, %f).  resid is (%f)\n",
     2166                                i, f->data.F32[i], fit->data.F32[i], resid->data.F64[i]);
     2167                    }
     2168                }
     2169            }
     2170        }
     2171
     2172        if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
     2173            psError(PS_ERR_UNKNOWN, false, "Could not compute statistics on the resid vector.  Returning NULL.\n");
     2174            psFree(resid);
     2175            psFree(fit);
     2176            return false;
     2177        }
     2178
     2179        double meanValue = psStatsGetValue (stats, meanOption);
     2180        double stdevValue = psStatsGetValue (stats, stdevOption);
     2181
     2182        psTrace("psLib.math", 5, "Mean is %f\n", meanValue);
     2183        psTrace("psLib.math", 5, "Stdev is %f\n", stdevValue);
     2184        psF32 minClipValue = -minClipSigma*stdevValue;
     2185        psF32 maxClipValue = +maxClipSigma*stdevValue;
     2186
     2187        // set mask if pts are not valid
     2188        // we are masking out any point which is out of range
     2189        // recovery is not allowed with this scheme
     2190        for (psS32 i = 0; i < resid->n; i++) {
     2191            if ((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue)) {
     2192                continue;
     2193            }
     2194
     2195            if ((resid->data.F64[i] - meanValue > maxClipValue) || (resid->data.F64[i] - meanValue < minClipValue))  {
     2196                if (f->type.type == PS_TYPE_F64) {
     2197                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
     2198                            i, fit->data.F64[i], i, resid->data.F64[i]);
     2199                } else {
     2200                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
     2201                            i, fit->data.F32[i], i, resid->data.F64[i]);
     2202                }
     2203
     2204                if (mask != NULL) {
     2205                    mask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= 0x01;
     2206                }
     2207                continue;
     2208            }
     2209            Nkeep++;
     2210        }
     2211        psTrace("psLib.math", 6, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
     2212        stats->clippedNvalues = Nkeep;
     2213        psFree(fit);
     2214    }
     2215    // Free local temporary variables
     2216    psFree(resid);
     2217
     2218    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
     2219    return true;
     2220}
     2221
     2222// This function accepts F32 and F64 input vectors.
     2223bool psVectorIRLSFitPolynomial3D(
     2224    psPolynomial3D *poly,
     2225    psStats *stats,
     2226    const psVector *mask,
     2227    psVectorMaskType maskValue,
     2228    const psVector *f,
     2229    const psVector *fErr,
     2230    const psVector *xIn,
     2231    const psVector *yIn,
     2232    const psVector *zIn)
     2233{
     2234    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
     2235
     2236    PS_ASSERT (poly->type == PS_POLYNOMIAL_ORD, false); // XXX for now, only allow ORD
     2237    PS_ASSERT_POLY_NON_NULL(poly, false);
     2238    PS_ASSERT_VECTOR_NON_NULL(f, false);
     2239    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
     2240    if (mask != NULL) {
     2241        PS_ASSERT_VECTORS_SIZE_EQUAL(mask, f, false);
     2242        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     2243    }
     2244    if (fErr != NULL) {
     2245        PS_ASSERT_VECTORS_SIZE_EQUAL(fErr, f, false);
     2246        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
     2247    }
     2248    if (xIn != NULL) {
     2249        PS_ASSERT_VECTORS_SIZE_EQUAL(xIn, f, false);
     2250        PS_ASSERT_VECTOR_TYPE(xIn, f->type.type, false);
     2251    }
     2252    if (yIn != NULL) {
     2253        PS_ASSERT_VECTORS_SIZE_EQUAL(yIn, f, false);
     2254        PS_ASSERT_VECTOR_TYPE(yIn, f->type.type, false);
     2255    }
     2256    if (zIn != NULL) {
     2257        PS_ASSERT_VECTORS_SIZE_EQUAL(zIn, f, false);
     2258        PS_ASSERT_VECTOR_TYPE(zIn, f->type.type, false);
     2259    }
     2260
     2261    // Internal pointers for possibly NULL vectors. 
     2262    psVector *x = (xIn != NULL) ? psMemIncrRefCounter((psVector *) xIn) : psVectorCreate(NULL, 0, f->n, 1, f->type.type);
     2263    psVector *y = (yIn != NULL) ? psMemIncrRefCounter((psVector *) yIn) : psVectorCreate(NULL, 0, f->n, 1, f->type.type);
     2264    psVector *z = (zIn != NULL) ? psMemIncrRefCounter((psVector *) zIn) : psVectorCreate(NULL, 0, f->n, 1, f->type.type);
     2265
     2266    // initial fit with nominal errors
     2267    if (!psVectorFitPolynomial3D(poly, mask, maskValue, f, fErr, x, y, z)) {
     2268        psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
     2269        psFree(x);
     2270        psFree(y);
     2271        psFree(z);
     2272        return false;
     2273    }
     2274
     2275    // use polyOld to save the last fit
     2276    psPolynomial3D *polyOld = NULL;
     2277
     2278    // use clipIter as max number of iterations
     2279    bool converged = false;
     2280    for (psS32 N = 0; !converged && (N < stats->clipIter); N++) {
     2281        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial3D()\n", N);
     2282
     2283        // evaluate the fit at the input positions
     2284        psVector *fEval = psPolynomial3DEvalVector (poly, x, y, z);
     2285
     2286        // calculate modified errors based on the deviation from the fit
     2287        psVector *modErr = psVector_GetModifiedErrors_Caucy (f, fEval, fErr, mask, maskValue);
     2288        psFree (fEval);
     2289
     2290        // save the last fit (recycle the structure once allocated)
     2291        polyOld = psPolynomial3DCopy (polyOld, poly);
     2292
     2293        // calculate a new fit with modified errors:
     2294        if (!psVectorFitPolynomial3D(poly, mask, maskValue, f, modErr, x, y, z)) {
     2295            psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
     2296            psFree(x);
     2297            psFree(y);
     2298            psFree(z);
     2299            psFree(modErr);
     2300            return false;
     2301        }
     2302
     2303        // has the solution converged?
     2304        converged = true;
     2305        for (int ix = 0; ix <= poly->nX; ix++) {
     2306            for (int iy = 0; iy <= poly->nY; iy++) {
     2307                for (int iz = 0; iz <= poly->nZ; iz++) {
     2308                    if ((fabs(poly->coeff[ix][iy][iz] - polyOld->coeff[ix][iy][iz]) > FIT_TOLERANCE * fabs(poly->coeff[ix][iy][iz])) &&
     2309                        (fabs(poly->coeff[ix][iy][iz] - polyOld->coeff[ix][iy][iz]) > FLT_TOLERANCE))
     2310                        converged = false;
     2311                }
     2312            }
     2313        }
     2314        psFree (modErr);
     2315    }
     2316
     2317    // Free local temporary variables
     2318    psFree(x);
     2319    psFree(y);
     2320    psFree(z);
     2321    psFree(polyOld);
     2322
     2323    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
     2324    return true;
     2325}
     2326
     2327/******************************************************************************
     2328 ******************************************************************************
     2329 4-D Vector Code.
     2330 ******************************************************************************
     2331 *****************************************************************************/
     2332/******************************************************************************
     2333VectorFitPolynomial4DOrd(myPoly, *mask, maskValue, *f, *fErr, *x, *y, *z, *t):
     2334This is a private routine which will fit a 4-D polynomial to a set of (x,
     2335y, z, t)-(f) pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
     2336 
     2337 *****************************************************************************/
     2338static bool VectorFitPolynomial4DOrd(
     2339    psPolynomial4D* myPoly,
     2340    const psVector* mask,
     2341    psVectorMaskType maskValue,
     2342    const psVector *f,
     2343    const psVector *fErr,
     2344    const psVector *x,
     2345    const psVector *y,
     2346    const psVector *z,
     2347    const psVector *t)
     2348{
     2349    psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
     2350    PS_ASSERT_POLY_NON_NULL(myPoly, false);
     2351    PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
     2352    PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
     2353    PS_ASSERT_INT_NONNEGATIVE(myPoly->nZ, false);
     2354    PS_ASSERT_INT_NONNEGATIVE(myPoly->nT, false);
     2355    PS_ASSERT_VECTOR_NON_NULL(f, false);
     2356    PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
     2357    if (fErr != NULL) {
     2358        PS_ASSERT_VECTORS_SIZE_EQUAL(y, fErr, false);
     2359        PS_ASSERT_VECTOR_TYPE(fErr, PS_TYPE_F64, false);
     2360    }
     2361    PS_ASSERT_VECTOR_NON_NULL(x, false);
     2362    PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
     2363    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
     2364    PS_ASSERT_VECTOR_NON_NULL(y, false);
     2365    PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
     2366    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
     2367    PS_ASSERT_VECTOR_NON_NULL(z, false);
     2368    PS_ASSERT_VECTOR_TYPE(z, PS_TYPE_F64, false);
     2369    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
     2370    PS_ASSERT_VECTOR_NON_NULL(t, false);
     2371    PS_ASSERT_VECTOR_TYPE(t, PS_TYPE_F64, false);
     2372    PS_ASSERT_VECTORS_SIZE_EQUAL(f, t, false);
     2373    if (mask) {
     2374        PS_ASSERT_VECTORS_SIZE_EQUAL(y, mask, false);
     2375        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     2376    }
     2377
     2378
     2379    int nXterm = 1 + myPoly->nX;        // Number of x terms
     2380    int nYterm = 1 + myPoly->nY;        // Number of y terms
     2381    int nZterm = 1 + myPoly->nZ;        // Number of z terms
     2382    int nTterm = 1 + myPoly->nT;        // Number of t terms
     2383    int nTerm = nXterm * nYterm * nZterm * nTterm; // Total number of terms
     2384    int nData = x->n;                   // Number of data points
     2385    psImage    *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
     2386    psVector   *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
     2387
     2388    // Initialize data structures.
     2389    if (!psImageInit(A, 0.0) || !psVectorInit(B, 0.0)) {
     2390        psError(PS_ERR_UNKNOWN, false, "Could initialize data structures A, B.  Returning NULL.\n");
     2391        psFree(A);
     2392        psFree(B);
     2393        psTrace("psLib.math", 4, "---- %s() End ----\n", __func__);
     2394        return false;
     2395    }
     2396
     2397    // Dereference points for speed in the loop
     2398    psF64 **matrix = A->data.F64;       // Least-squares matrix
     2399    psF64 *vector = B->data.F64;        // Least-squares vector
     2400    psF64 *xData = x->data.F64;         // x
     2401    psF64 *yData = y->data.F64;         // y
     2402    psF64 *zData = z->data.F64;         // z
     2403    psF64 *tData = t->data.F64;         // t
     2404    psF64 *fData = f->data.F64;         // f
     2405    psF64 *fErrData = NULL;             // Error in f
     2406    if (fErr) {
     2407        fErrData = fErr->data.F64;
     2408    }
     2409    psVectorMaskType *dataMask = NULL;              // Mask for data
     2410    if (mask) {
     2411        dataMask = mask->data.PS_TYPE_VECTOR_MASK_DATA;
     2412    }
     2413    psMaskType ****coeffMask = myPoly->coeffMask;    // Mask for polynomial terms
     2414    int nYZTterm = nYterm * nZterm * nTterm; // Multiplication of the numbers, for calculating the index
     2415    int nZTterm = nZterm * nTterm;      // Multiplication of the numbers, for calculating the index
     2416
     2417    // Build the B and A data structs.
     2418    psF64 ****Sums = NULL;        // Sums look like: 1, x, x^2, ... x^(2n+1), y, xy, x^2y, ... x^(2n+1)*y, ...
     2419    for (int k = 0; k < nData; k++) {
     2420        if (dataMask && dataMask[k] & maskValue) {
     2421            continue;
     2422        }
     2423
     2424        Sums = BuildSums4D(Sums, xData[k], yData[k], zData[k], tData[k], nXterm, nYterm, nZterm, nTterm);
     2425
     2426        double wt;
     2427        if (fErr == NULL) {
     2428            wt = 1.0;
     2429        } else {
     2430            // this filters fErr == 0 values
     2431            wt = (fErr->data.F64[k] == 0.0) ? 0.0 : 1.0 / PS_SQR(fErrData[k]);
     2432        }
     2433
     2434        for (int i = 0; i < nTerm; i++) {
     2435            int ix = i / (nYZTterm); // x index
     2436            int iy = (i % (nYZTterm)) / (nZTterm); // y index
     2437            int iz = ((i % (nYZTterm)) % (nZTterm)) / nTterm; // z index
     2438            int it = ((i % (nYZTterm)) % (nZTterm)) % nTterm; // t index
     2439            if (coeffMask[ix][iy][iz][it] & PS_POLY_MASK_BOTH) {
     2440                matrix[i][i] = 1.0;
     2441                continue;
     2442            }
     2443
     2444            vector[i] += fData[k] * Sums[ix][iy][iz][it] * wt;
     2445            matrix[i][i] += Sums[2*ix][2*iy][2*iz][2*it] * wt;
     2446            for (int j = i + 1; j < nTerm; j++) {
     2447                int jx = j / nYZTterm; // x index
     2448                int jy = (j % nYZTterm) / nZTterm; // y index
     2449                int jz = ((j % nYZTterm) % nZTterm) / nTterm; // z index
     2450                int jt = ((j % nYZTterm) % nZTterm) % nTterm; // t index
     2451                if (coeffMask[jx][jy][jz][jt] & PS_POLY_MASK_BOTH) {
     2452                    continue;
     2453                }
     2454                double value = Sums[ix+jx][iy+jy][iz+jz][it+jt] * wt;
     2455                matrix[i][j] += value;
     2456                matrix[j][i] += value;
     2457            }
     2458        }
     2459    }
     2460
     2461    // Free the sums
     2462    if (Sums == NULL) {
     2463        assert (nData == 0);
     2464    } else {
     2465        for (int ix = 0; ix < 2*nXterm; ix++) {
     2466            for (int iy = 0; iy < 2*nYterm; iy++) {
     2467                for (int iz = 0; iz < 2*nZterm; iz++) {
     2468                    psFree(Sums[ix][iy][iz]);
     2469                }
     2470                psFree(Sums[ix][iy]);
     2471            }
     2472            psFree(Sums[ix]);
     2473        }
     2474        psFree(Sums);
     2475    }
     2476
     2477    bool status = false;
     2478    if (USE_GAUSS_JORDAN) {
     2479        status = psMatrixGJSolve(A, B);
     2480    } else {
     2481        status = psMatrixLUSolve(A, B);
     2482    }
     2483    if (!status) {
     2484        psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.\n");
     2485        goto escape;
     2486    }
     2487
     2488    // select the appropriate solution entries
     2489    for (int i = 0; i < nTerm; i++) {
     2490        int ix = i / nYZTterm; // x index
     2491        int iy = (i % nYZTterm) / nZTterm; // y index
     2492        int iz = ((i % nYZTterm) % nZTterm) / nTterm; // z index
     2493        int it = ((i % nYZTterm) % nZTterm) % nTterm; // t index
     2494        if (coeffMask[ix][iy][iz][it] & PS_POLY_MASK_FIT) continue;
     2495        myPoly->coeff[ix][iy][iz][it] = B->data.F64[i];
     2496        myPoly->coeffErr[ix][iy][iz][it] = sqrt(A->data.F64[i][i]);
     2497    }
     2498    psFree(A);
     2499    psFree(B);
     2500    return true;
     2501
     2502escape:
     2503    psFree(A);
     2504    psFree(B);
     2505    return false;
     2506}
     2507
     2508/******************************************************************************
     2509psVectorFitPolynomial4D():  This routine fits a 4D polynomial of arbitrary
     2510degree (specified in poly) to the data points (x, y, z, t)-(f) and returns
     2511that polynomial.  Types F32 and F64 are supported, however, type F32 is done
     2512via vector conversion only.
     2513 *****************************************************************************/
     2514bool psVectorFitPolynomial4D(
     2515    psPolynomial4D *poly,
     2516    const psVector *mask,
     2517    psVectorMaskType maskValue,
     2518    const psVector *f,
     2519    const psVector *fErr,
     2520    const psVector *x,
     2521    const psVector *y,
     2522    const psVector *z,
     2523    const psVector *t)
     2524{
     2525    PS_ASSERT_POLY_NON_NULL(poly, false);
     2526    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
     2527
     2528    PS_ASSERT_VECTOR_NON_NULL(f, false);
     2529    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
     2530    PS_ASSERT_VECTOR_NON_NULL(x, false);
     2531    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
     2532    PS_ASSERT_VECTOR_NON_NULL(y, false);
     2533    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
     2534    PS_ASSERT_VECTOR_NON_NULL(z, false);
     2535    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
     2536    PS_ASSERT_VECTOR_NON_NULL(t, false);
     2537    PS_ASSERT_VECTORS_SIZE_EQUAL(f, t, false);
     2538    if (mask) {
     2539        PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
     2540        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     2541    }
     2542    if (fErr != NULL) {
     2543        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
     2544        PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, false);
     2545    }
     2546
     2547    // Convert input vectors to F64 if necessary.
     2548    psVector *f64 = (f->type.type == PS_TYPE_F64) ? (psVector *) f : psVectorCopy(NULL, f, PS_TYPE_F64);
     2549    psVector *x64 = (x->type.type == PS_TYPE_F64) ? (psVector *) x : psVectorCopy(NULL, x, PS_TYPE_F64);
     2550    psVector *y64 = (y->type.type == PS_TYPE_F64) ? (psVector *) y : psVectorCopy(NULL, y, PS_TYPE_F64);
     2551    psVector *z64 = (z->type.type == PS_TYPE_F64) ? (psVector *) z : psVectorCopy(NULL, z, PS_TYPE_F64);
     2552    psVector *t64 = (t->type.type == PS_TYPE_F64) ? (psVector *) t : psVectorCopy(NULL, t, PS_TYPE_F64);
     2553
     2554    psVector *fErr64 = NULL;
     2555    if (fErr != NULL) {
     2556        fErr64 = (fErr->type.type == PS_TYPE_F64) ? (psVector *) fErr : psVectorCopy(NULL, fErr, PS_TYPE_F64);
     2557    }
     2558
     2559    bool result = true;
     2560
     2561    switch (poly->type) {
     2562    case PS_POLYNOMIAL_ORD:
     2563        result = VectorFitPolynomial4DOrd(poly, mask, maskValue, f64, fErr64, x64, y64, z64, t64);
     2564        if (!result) {
     2565            psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
     2566        }
     2567        break;
     2568    case PS_POLYNOMIAL_CHEB:
     2569        if (mask != NULL) {
     2570            psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring mask and maskValue with Chebyshev polynomials.\n");
     2571        }
     2572        psError(PS_ERR_UNKNOWN, true, "4-D Chebyshev polynomial vector fitting has not been implemented.  Returning NULL.\n");
     2573        result = false;
     2574        break;
     2575    default:
     2576        psError(PS_ERR_UNKNOWN, true, "Incorrect polynomial type.  Returning NULL.\n");
     2577        result = false;
     2578        break;
     2579    }
     2580
     2581    // Free psVectors that were created for NULL arguments.
     2582    PS_FREE_TEMP_F64_VECTOR (f, f64);
     2583    PS_FREE_TEMP_F64_VECTOR (x, x64);
     2584    PS_FREE_TEMP_F64_VECTOR (y, y64);
     2585    PS_FREE_TEMP_F64_VECTOR (z, z64);
     2586    PS_FREE_TEMP_F64_VECTOR (t, t64);
     2587    PS_FREE_TEMP_F64_VECTOR (fErr, fErr64);
     2588
     2589    return result;
     2590}
     2591
     2592
     2593bool psVectorClipFitPolynomial4D(
     2594    psPolynomial4D *poly,
     2595    psStats *stats,
     2596    const psVector *mask,
     2597    psVectorMaskType maskValue,
     2598    const psVector *f,
     2599    const psVector *fErr,
     2600    const psVector *x,
     2601    const psVector *y,
     2602    const psVector *z,
     2603    const psVector *t)
     2604{
     2605    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
     2606    PS_ASSERT_POLY_NON_NULL(poly, false);
     2607    PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
     2608    PS_ASSERT_PTR_NON_NULL(stats, false);
     2609    PS_ASSERT_VECTOR_NON_NULL(mask, false);
     2610    PS_ASSERT_VECTOR_NON_NULL(f, false);
     2611    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
     2612
     2613    PS_ASSERT_VECTOR_NON_NULL(x, false);
     2614    PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
     2615    PS_ASSERT_VECTOR_TYPE(x, f->type.type, false);
     2616
     2617    PS_ASSERT_VECTOR_NON_NULL(y, false);
     2618    PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
     2619    PS_ASSERT_VECTOR_TYPE(y, f->type.type, false);
     2620
     2621    PS_ASSERT_VECTOR_NON_NULL(z, false);
     2622    PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
     2623    PS_ASSERT_VECTOR_TYPE(z, f->type.type, false);
     2624
     2625    PS_ASSERT_VECTOR_NON_NULL(t, false);
     2626    PS_ASSERT_VECTORS_SIZE_EQUAL(f, t, false);
     2627    PS_ASSERT_VECTOR_TYPE(t, f->type.type, false);
     2628
     2629    PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
     2630    PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     2631
     2632    if (fErr != NULL) {
     2633        PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
     2634        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
     2635    }
     2636
     2637    // the user supplies one of various stats option pairs,
     2638    // determine the desired mean and stdev STATS options:
     2639    // XXX enforce consistency?
     2640    // XXX psStatsGetValue() probably has inverted precedence
     2641    psStatsOptions meanOption = stats->options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN | PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN);
     2642    psStatsOptions stdevOption = stats->options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV | PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV);
     2643    if (!meanOption) {
     2644        psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
     2645        return false;
     2646    }
     2647    if (!stdevOption) {
     2648        psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
     2649        return false;
     2650    }
     2651
     2652    // clipping range defined by min and max and/or clipSigma
     2653    psF32 minClipSigma;
     2654    psF32 maxClipSigma;
     2655    if (isfinite(stats->max)) {
     2656        maxClipSigma = fabs(stats->max);
     2657    } else {
     2658        maxClipSigma = fabs(stats->clipSigma);
     2659    }
     2660    if (isfinite(stats->min)) {
     2661        minClipSigma = fabs(stats->min);
     2662    } else {
     2663        minClipSigma = fabs(stats->clipSigma);
     2664    }
     2665    psVector *resid = psVectorAlloc(f->n, PS_TYPE_F64);
     2666
     2667    psTrace("psLib.math", 4, "stats->clipIter is %d\n", stats->clipIter);
     2668    psTrace("psLib.math", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
     2669
     2670    for (psS32 N = 0; N < stats->clipIter; N++) {
     2671        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial4D()\n", N);
     2672        psS32 Nkeep = 0;
     2673        if (psTraceGetLevel("psLib.math") >= 6) {
     2674            if (mask != NULL) {
     2675                for (psS32 i = 0 ; i < mask->n ; i++) {
     2676                    psTrace("psLib.math", 6,  "mask[%d] is %d\n", i, mask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
     2677                }
     2678            }
     2679        }
     2680
     2681        if (!psVectorFitPolynomial4D (poly, mask, maskValue, f, fErr, x, y, z, t)) {
     2682            psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the data.  Returning NULL.\n");
     2683            psFree(resid);
     2684            return false;
     2685        }
     2686
     2687        psVector *fit = psPolynomial4DEvalVector (poly, x, y, z, t);
     2688        if (fit == NULL) {
     2689            psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial4DEvalVector().  Returning NULL.\n");
     2690            psFree(resid);
     2691            return false;
     2692        }
     2693        for (psS32 i = 0 ; i < f->n ; i++) {
     2694            if (f->type.type == PS_TYPE_F64) {
     2695                resid->data.F64[i] = f->data.F64[i] - fit->data.F64[i];
     2696            } else {
     2697                resid->data.F64[i] = ((psF64) f->data.F32[i]) - fit->data.F64[i];
     2698            }
     2699        }
     2700
     2701        if (psTraceGetLevel("psLib.math") >= 6) {
     2702            if (mask != NULL) {
     2703                for (psS32 i = 0 ; i < mask->n ; i++) {
     2704                    if (!((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue))) {
     2705                        psTrace("psLib.math", 6,  "(f, fit)[%d] is (%f, %f).  resid is (%f)\n",
     2706                                i, f->data.F32[i], fit->data.F32[i], resid->data.F64[i]);
     2707                    }
     2708                }
     2709            }
     2710        }
     2711
     2712        if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
     2713            psError(PS_ERR_UNKNOWN, false, "Could not compute statistics on the resid vector.  Returning NULL.\n");
     2714            psFree(resid);
     2715            psFree(fit);
     2716            return false;
     2717        }
     2718
     2719        double meanValue = psStatsGetValue (stats, meanOption);
     2720        double stdevValue = psStatsGetValue (stats, stdevOption);
     2721
     2722        psTrace("psLib.math", 5, "Mean is %f\n", meanValue);
     2723        psTrace("psLib.math", 5, "Stdev is %f\n", stdevValue);
     2724        psF32 minClipValue = -minClipSigma*stdevValue;
     2725        psF32 maxClipValue = +maxClipSigma*stdevValue;
     2726
     2727        // set mask if pts are not valid
     2728        // we are masking out any point which is out of range
     2729        // recovery is not allowed with this scheme
     2730        for (psS32 i = 0; i < resid->n; i++) {
     2731            if ((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue)) {
     2732                continue;
     2733            }
     2734
     2735            if ((resid->data.F64[i] - meanValue > maxClipValue) || (resid->data.F64[i] - meanValue < minClipValue)) {
     2736                if (f->type.type == PS_TYPE_F64) {
     2737                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
     2738                            i, fit->data.F64[i], i, resid->data.F64[i]);
     2739                } else {
     2740                    psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
     2741                            i, fit->data.F32[i], i, resid->data.F64[i]);
     2742                }
     2743
     2744                if (mask != NULL) {
     2745                    mask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= 0x01;
     2746                }
     2747                continue;
     2748            }
     2749            Nkeep++;
     2750        }
     2751        psTrace("psLib.math", 6, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
     2752        stats->clippedNvalues = Nkeep;
     2753        psFree (fit);
     2754    }
     2755    // Free local temporary variables
     2756    psFree (resid);
     2757
     2758    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
     2759    return true;
     2760}
     2761
     2762// This function accepts F32 and F64 input vectors.
     2763bool psVectorIRLSFitPolynomial4D(
     2764    psPolynomial4D *poly,
     2765    psStats *stats,
     2766    const psVector *mask,
     2767    psVectorMaskType maskValue,
     2768    const psVector *f,
     2769    const psVector *fErr,
     2770    const psVector *xIn,
     2771    const psVector *yIn,
     2772    const psVector *zIn,
     2773    const psVector *tIn)
     2774{
     2775    psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
     2776
     2777    PS_ASSERT (poly->type == PS_POLYNOMIAL_ORD, false); // XXX for now, only allow ORD
     2778    PS_ASSERT_POLY_NON_NULL(poly, false);
     2779    PS_ASSERT_VECTOR_NON_NULL(f, false);
     2780    PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
     2781    if (mask != NULL) {
     2782        PS_ASSERT_VECTORS_SIZE_EQUAL(mask, f, false);
     2783        PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
     2784    }
     2785    if (fErr != NULL) {
     2786        PS_ASSERT_VECTORS_SIZE_EQUAL(fErr, f, false);
     2787        PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
     2788    }
     2789    if (xIn != NULL) {
     2790        PS_ASSERT_VECTORS_SIZE_EQUAL(xIn, f, false);
     2791        PS_ASSERT_VECTOR_TYPE(xIn, f->type.type, false);
     2792    }
     2793    if (yIn != NULL) {
     2794        PS_ASSERT_VECTORS_SIZE_EQUAL(yIn, f, false);
     2795        PS_ASSERT_VECTOR_TYPE(yIn, f->type.type, false);
     2796    }
     2797    if (zIn != NULL) {
     2798        PS_ASSERT_VECTORS_SIZE_EQUAL(zIn, f, false);
     2799        PS_ASSERT_VECTOR_TYPE(zIn, f->type.type, false);
     2800    }
     2801    if (tIn != NULL) {
     2802        PS_ASSERT_VECTORS_SIZE_EQUAL(tIn, f, false);
     2803        PS_ASSERT_VECTOR_TYPE(tIn, f->type.type, false);
     2804    }
     2805
     2806    // Internal pointers for possibly NULL vectors. 
     2807    psVector *x = (xIn != NULL) ? psMemIncrRefCounter((psVector *) xIn) : psVectorCreate(NULL, 0, f->n, 1, f->type.type);
     2808    psVector *y = (yIn != NULL) ? psMemIncrRefCounter((psVector *) yIn) : psVectorCreate(NULL, 0, f->n, 1, f->type.type);
     2809    psVector *z = (zIn != NULL) ? psMemIncrRefCounter((psVector *) zIn) : psVectorCreate(NULL, 0, f->n, 1, f->type.type);
     2810    psVector *t = (tIn != NULL) ? psMemIncrRefCounter((psVector *) tIn) : psVectorCreate(NULL, 0, f->n, 1, f->type.type);
     2811
     2812    // initial fit with nominal errors
     2813    if (!psVectorFitPolynomial4D(poly, mask, maskValue, f, fErr, x, y, z, t)) {
     2814        psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
     2815        psFree(x);
     2816        psFree(y);
     2817        psFree(z);
     2818        psFree(t);
     2819        return false;
     2820    }
     2821
     2822    // use polyOld to save the last fit
     2823    psPolynomial4D *polyOld = NULL;
     2824
     2825    // use clipIter as max number of iterations
     2826    bool converged = false;
     2827    for (psS32 N = 0; !converged && (N < stats->clipIter); N++) {
     2828        psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial4D()\n", N);
     2829
     2830        // evaluate the fit at the input positions
     2831        psVector *fEval = psPolynomial4DEvalVector (poly, x, y, z, t);
     2832
     2833        // calculate modified errors based on the deviation from the fit
     2834        psVector *modErr = psVector_GetModifiedErrors_Caucy (f, fEval, fErr, mask, maskValue);
     2835        psFree (fEval);
     2836
     2837        // save the last fit (recycle the structure once allocated)
     2838        polyOld = psPolynomial4DCopy (polyOld, poly);
     2839
     2840        // calculate a new fit with modified errors:
     2841        if (!psVectorFitPolynomial4D(poly, mask, maskValue, f, modErr, x, y, z, t)) {
     2842            psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
     2843            psFree(x);
     2844            psFree(y);
     2845            psFree(z);
     2846            psFree(t);
     2847            psFree(modErr);
     2848            return false;
     2849        }
     2850
     2851        // has the solution converged?
     2852        converged = true;
     2853        for (int ix = 0; ix <= poly->nX; ix++) {
     2854            for (int iy = 0; iy <= poly->nY; iy++) {
     2855                for (int iz = 0; iz <= poly->nZ; iz++) {
     2856                    for (int it = 0; it <= poly->nT; it++) {
     2857                        if ((fabs(poly->coeff[ix][iy][iz][it] - polyOld->coeff[ix][iy][iz][it]) > FIT_TOLERANCE * fabs(poly->coeff[ix][iy][iz][it])) &&
     2858                            (fabs(poly->coeff[ix][iy][iz][it] - polyOld->coeff[ix][iy][iz][it]) > FLT_TOLERANCE))
     2859                            converged = false;
     2860                    }
     2861                }
     2862            }
     2863        }
     2864        psFree (modErr);
     2865    }
     2866
     2867    // Free local temporary variables
     2868    psFree(x);
     2869    psFree(y);
     2870    psFree(z);
     2871    psFree(t);
     2872    psFree(polyOld);
     2873
     2874    psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
     2875    return true;
     2876}
     2877
     2878// ######################## utilities ###################
     2879
     2880// Used by IRLS fitting
    10332881// This function assumes the input vectors (f, fEval, fErr) all have the same type
    10342882// This requirement is already enforced in the calling function (psVectorIRLSFitPolynomial1D)
     
    10782926}
    10792927
    1080 // These should probably be tunable:
    1081 # define FIT_TOLERANCE 1e-4
    1082 # define FLT_TOLERANCE 1e-6
    1083 # define WEIGHT_THRESHOLD 0.3
    1084 
    1085 // This function accepts F32 and F64 input vectors.
    1086   //
    1087 bool psVectorIRLSFitPolynomial1D(
    1088     psPolynomial1D *poly,
    1089     psStats *stats,
    1090     const psVector *mask,
    1091     psVectorMaskType maskValue,
    1092     const psVector *f,
    1093     const psVector *fErr,
    1094     const psVector *xIn)
    1095 {
    1096     psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
    1097     PS_ASSERT_POLY_NON_NULL(poly, false);
    1098     PS_ASSERT_VECTOR_NON_NULL(f, false);
    1099     PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
    1100     if (mask != NULL) {
    1101         PS_ASSERT_VECTORS_SIZE_EQUAL(mask, f, false);
    1102         PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
    1103     }
    1104 
    1105     if (fErr != NULL) {
    1106         PS_ASSERT_VECTORS_SIZE_EQUAL(fErr, f, false);
    1107         PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
    1108     }
    1109 
    1110     // Internal pointers for possibly NULL vectors.
    1111     psVector *x = NULL;
    1112     if (xIn != NULL) {
    1113         PS_ASSERT_VECTORS_SIZE_EQUAL(xIn, f, false);
    1114         PS_ASSERT_VECTOR_TYPE(xIn, f->type.type, false);
    1115         x = (psVector *) xIn;
    1116     } else {
    1117         if (poly->type == PS_POLYNOMIAL_ORD) {
    1118             x = psVectorCreate(NULL, 0, f->n, 1, f->type.type);
    1119         } else if (poly->type == PS_POLYNOMIAL_CHEB) {
    1120             if (f->type.type == PS_TYPE_F32) {
    1121                 PS_VECTOR_GEN_CHEBY_INDEX(x, f->n, PS_TYPE_F32);
    1122             } else if (f->type.type == PS_TYPE_F64) {
    1123                 PS_VECTOR_GEN_CHEBY_INDEX(x, f->n, PS_TYPE_F64);
    1124             }
    1125         } else {
    1126             psError(PS_ERR_UNKNOWN, true, "Error, bad poly type.\n");
    1127             return false;
    1128         }
    1129     }
    1130 
    1131     // initial fit with nominal errors
    1132     if (!psVectorFitPolynomial1D(poly, mask, maskValue, f, fErr, x)) {
    1133         psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
    1134         if (xIn == NULL) psFree(x);
    1135         return false;
    1136     }
    1137 
    1138     // use polyOld to save the last fit
    1139     psPolynomial1D *polyOld = NULL;
    1140 
    1141     // use clipIter as max number of iterations
    1142     bool converged = false;
    1143     for (psS32 N = 0; !converged && (N < stats->clipIter); N++) {
    1144         psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial1D()\n", N);
    1145 
    1146         // evaluate the fit at the input positions
    1147         psVector *fEval = psPolynomial1DEvalVector (poly, x);
    1148 
    1149         // calculate modified errors based on the deviation from the fit
    1150         psVector *modErr = psVector_GetModifiedErrors_Caucy (f, fEval, fErr, mask, maskValue);
    1151         psFree (fEval);
    1152 
    1153         // save the last fit (recycle the structure once allocated)
    1154         polyOld = psPolynomial1DCopy (polyOld, poly);
    1155 
    1156         // calculate a new fit with modified errors:
    1157         if (!psVectorFitPolynomial1D(poly, mask, maskValue, f, modErr, x)) {
    1158             psError(PS_ERR_UNKNOWN, false, "Could not fit polynomial.  Returning false.\n");
    1159             if (xIn == NULL) psFree(x);
    1160             psFree(modErr);
    1161             return false;
    1162         }
    1163 
    1164         // has the solution converged?
    1165         converged = true;
    1166         for (int ix = 0; ix <= poly->nX; ix++) {
    1167           if ((fabs(poly->coeff[ix] - polyOld->coeff[ix]) > FIT_TOLERANCE * fabs(poly->coeff[ix])) &&
    1168               (fabs(poly->coeff[ix] - polyOld->coeff[ix]) > FLT_TOLERANCE))
    1169             converged = false;
    1170         }
    1171 
    1172 # if (0)       
    1173         // XXX test:
    1174         FILE *ftest = fopen ("irls.wt.dat", "w");
    1175         for (int i = 0; i < modErr->n; i++) {
    1176             if (modErr->type.type == PS_TYPE_F64) {
    1177                 fprintf (ftest, "%d %f\n", i, modErr->data.F64[i]);
    1178             } else {
    1179                 fprintf (ftest, "%d %f\n", i, modErr->data.F32[i]);
    1180             }
    1181         }
    1182         fclose (ftest);
    1183 # endif
    1184         psFree (modErr);
    1185     }
    1186 
    1187     // Free local temporary variables
    1188     if (xIn == NULL) psFree(x);
    1189     psFree(polyOld);
    1190 
    1191     psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
    1192     return true;
    1193 }
    1194 
    1195 /******************************************************************************
    1196  ******************************************************************************
    1197  2-D Vector Code.
    1198  ******************************************************************************
    1199  *****************************************************************************/
    1200 
    1201 /******************************************************************************
    1202 VectorFitPolynomial2DOrd(myPoly, *mask, maskValue, *f, *fErr, *x, *y): This is
    1203 a private routine which will fit a 2-D polynomial to a set of (x, y)-(f)
    1204 pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
    1205  
    1206  *****************************************************************************/
    1207 static bool VectorFitPolynomial2DOrd(
    1208     psPolynomial2D* myPoly,
    1209     const psVector* mask,
    1210     psVectorMaskType maskValue,
    1211     const psVector *f,
    1212     const psVector *fErr,
    1213     const psVector *x,
    1214     const psVector *y)
    1215 {
    1216     psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    1217     PS_ASSERT_POLY_NON_NULL(myPoly, false);
    1218     PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
    1219     PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
    1220     PS_ASSERT_VECTOR_NON_NULL(f, false);
    1221     PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
    1222     if (fErr != NULL) {
    1223         PS_ASSERT_VECTORS_SIZE_EQUAL(y, fErr, false);
    1224         PS_ASSERT_VECTOR_TYPE(fErr, PS_TYPE_F64, false);
    1225     }
    1226     PS_ASSERT_VECTOR_NON_NULL(x, false);
    1227     PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
    1228     PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
    1229     PS_ASSERT_VECTOR_NON_NULL(y, false);
    1230     PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
    1231     PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
    1232     if (mask != NULL) {
    1233         PS_ASSERT_VECTORS_SIZE_EQUAL(y, mask, false);
    1234         PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
    1235     }
    1236 
    1237     // Number of polynomial terms
    1238     int nXterm = 1 + myPoly->nX;      // Number of terms in x
    1239     int nYterm = 1 + myPoly->nY;      // Number of terms in y
    1240     int nTerm = nXterm * nYterm;            // Total number of terms
    1241 
    1242     psImage *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
    1243     psVector *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
    1244 
    1245     // Initialize data structures.
    1246     if (!psImageInit(A, 0.0) || !psVectorInit(B, 0.0)) {
    1247         psError(PS_ERR_UNKNOWN, false, "Could initialize data structures A, B.  Returning NULL.\n");
    1248         psFree(A);
    1249         psFree(B);
    1250         psTrace("psLib.math", 6, "---- %s() End ----\n", __func__);
    1251         return false;
    1252     }
    1253 
    1254     // Dereference stuff, to make the loop go faster
    1255     psF64 **matrix = A->data.F64;       // Dereference the least-squares matrix
    1256     psF64 *vector = B->data.F64;        // Dereference the least-squares vector
    1257     psMaskType **coeffMask = myPoly->coeffMask;     // Dereference mask for polynomial terms
    1258     psVectorMaskType *dataMask = NULL;              // Dereference mask for data
    1259     if (mask) {
    1260         dataMask = mask->data.PS_TYPE_VECTOR_MASK_DATA;
    1261     }
    1262     psF64 *xData = x->data.F64;         // Dereference x
    1263     psF64 *yData = y->data.F64;         // Dereference y
    1264     psF64 *fData = f->data.F64;         // Dereference f
    1265     psF64 *fErrData = NULL;             // Dereference fErr
    1266     if (fErr) {
    1267         fErrData = fErr->data.F64;
    1268     }
    1269 
    1270     // Build the least-squares matrix and vector
    1271     psImage *xySums = NULL;               // The sums: 1, x, x^2, ... x^(2n+1), y, xy, x^2y, ... x^(2n+1)
    1272     for (int k = 0; k < x->n; k++) {
    1273         if (dataMask && dataMask[k] & maskValue) {
    1274             continue;
    1275         }
    1276         xySums = BuildSums2D(xySums, xData[k], yData[k], nXterm, nYterm);
    1277         psF64 **sums = xySums->data.F64;// Dereference sums
    1278 
    1279         double wt;                      // Weight
    1280         if (!fErrData) {
    1281             wt = 1.0;
    1282         } else {
    1283             // this filters fErr == 0 values
    1284             wt = (fErrData[k] == 0.0) ? 0.0 : 1.0 / PS_SQR(fErrData[k]);
    1285         }
    1286 
    1287         // Iterating over the matrix
    1288         for (int i = 0; i < nTerm; i++) {
    1289             int l = i / nYterm;         // x index
    1290             int m = i % nYterm;         // y index
    1291             if (coeffMask[l][m] & PS_POLY_MASK_SET) {
    1292                 matrix[i][i] = 1.0;
    1293                 continue;
    1294             }
    1295             vector[i] += fData[k] * sums[l][m] * wt;
    1296             matrix[i][i] += sums[2*l][2*m] * wt; // The diagonal entry
    1297             for (int j = i + 1; j < nTerm; j++) { // Doing the upper diagonal only: we will use symmetry
    1298                 int p = j / nYterm;     // x index
    1299                 int q = j % nYterm;     // y index
    1300                 if (coeffMask[p][q] & PS_POLY_MASK_SET) {
    1301                     continue;
    1302                 }
    1303                 double value = sums[l+p][m+q] * wt; // Value to add in
    1304                 matrix[i][j] += value;
    1305                 matrix[j][i] += value;  // Taking advantage of the symmetry
    1306             }
    1307         }
    1308     }
    1309     psFree(xySums);
    1310 
    1311     // elements which are masked for fitting need to be subtracted from the vector
    1312     for (int i = 0; i < nTerm; i++) {
    1313         int ix = i / nYterm;         // x index
    1314         int iy = i % nYterm;         // y index
    1315         if (coeffMask[ix][iy] & PS_POLY_MASK_BOTH) {
    1316             continue;
    1317         }
    1318         for (int j = 0; j < nTerm; j++) { // The upper diagonal only: we will use symmetry
    1319             int jx = j / nYterm;         // x index
    1320             int jy = j % nYterm;         // y index
    1321             if (coeffMask[jx][jy] & PS_POLY_MASK_SET) {
    1322                 continue;
    1323             }
    1324             if (!(coeffMask[jx][jy] & PS_POLY_MASK_FIT)) {
    1325                 continue;
    1326             }
    1327             vector[i] -= matrix[i][j]*myPoly->coeff[jx][jy];
    1328         }
    1329     }
    1330    
    1331     // set the un-fitted and un-set elements to 0 or 1 for pivots
    1332     for (int i = 0; i < nTerm; i++) {
    1333         int ix = i / nYterm;         // x index
    1334         int iy = i % nYterm;         // y index
    1335         if (coeffMask[ix][iy] & PS_POLY_MASK_BOTH) {
    1336             for (int j = 0; j < nTerm; j++) { // The upper diagonal only: we will use symmetry
    1337                 matrix[i][j] = 0.0;
    1338                 matrix[j][i] = 0.0;
    1339             }
    1340             matrix[i][i] = 1.0;
    1341             continue;
    1342         }
    1343     }
    1344 
    1345     if (psTraceGetLevel("psLib.math") >= 4) {
    1346         printf("Least-squares vector:\n");
    1347         for (int i = 0; i < nTerm; i++) {
    1348             printf("%f ", B->data.F64[i]);
    1349         }
    1350         printf("\n");
    1351         printf("Least-squares matrix:\n");
    1352         for (int i = 0; i < nTerm; i++) {
    1353             for (int j = 0; j < nTerm; j++) {
    1354                 printf("%f ", A->data.F64[i][j]);
    1355             }
    1356             printf("\n");
    1357         }
    1358     }
    1359 
    1360     bool status = false;
    1361     if (USE_GAUSS_JORDAN) {
    1362         status = psMatrixGJSolve(A, B);
    1363     } else {
    1364         status = psMatrixLUSolve(A, B);
    1365     }
    1366     if (!status) {
    1367         psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.\n");
    1368         goto escape;
    1369     }
    1370 
    1371     // select the appropriate solution entries (retain the incoming values if masked on the fit)
    1372     for (int i = 0; i < nTerm; i++) {
    1373         int ix = i / nYterm;         // x index
    1374         int iy = i % nYterm;         // y index
    1375         if (coeffMask[ix][iy] & PS_POLY_MASK_FIT) continue;
    1376         myPoly->coeff[ix][iy] = B->data.F64[i];
    1377         myPoly->coeffErr[ix][iy] = sqrt(A->data.F64[i][i]);
    1378     }
    1379     psFree(A);
    1380     psFree(B);
    1381     return true;
    1382 
    1383 escape:
    1384     psFree (A);
    1385     psFree (B);
    1386     return false;
    1387 }
    1388 
    1389 /******************************************************************************
    1390 VectorFitPolynomial2DCheb(myPoly, *mask, maskValue, *f, *fErr, *x, *y): This is
    1391 a private routine which will fit a 2-D polynomial to a set of (x, y)-(f)
    1392 pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
    1393  
    1394  *****************************************************************************/
    1395 static bool VectorFitPolynomial2DCheb(
    1396     psPolynomial2D* myPoly,
    1397     const psVector *f,
    1398     const psVector *x,
    1399     const psVector *y)
    1400 {
    1401     psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    1402     PS_ASSERT_POLY_NON_NULL(myPoly, false);
    1403     PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
    1404     PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
    1405     PS_ASSERT_VECTOR_NON_NULL(f, false);
    1406     PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
    1407     PS_ASSERT_VECTOR_NON_NULL(x, false);
    1408     PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
    1409     PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
    1410     PS_ASSERT_VECTOR_NON_NULL(y, false);
    1411     PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
    1412     PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
    1413 
    1414     // Number of polynomial terms
    1415     int nXterm = 1 + myPoly->nX;      // Number of terms in x
    1416     int nYterm = 1 + myPoly->nY;      // Number of terms in y
    1417     int nTerm = nXterm * nYterm;      // Total number of terms
    1418     if (nXterm > 9) {
    1419         psError(PS_ERR_UNKNOWN, false, "failed 2D chebyshev fit: orders higher than 9 are not yet coded\n");
    1420         return false;
    1421     }
    1422     if (nYterm > 9) {
    1423         psError(PS_ERR_UNKNOWN, false, "failed 2D chebyshev fit: orders higher than 9 are not yet coded\n");
    1424         return false;
    1425     }
    1426 
    1427     // determine scale factors
    1428     if (!psChebyshevSetScale (myPoly, x, 0)) { psError(PS_ERR_UNKNOWN, false, "failed 2D chebyshev fit.\n"); return false; }
    1429     if (!psChebyshevSetScale (myPoly, y, 1)) { psError(PS_ERR_UNKNOWN, false, "failed 2D chebyshev fit.\n"); return false; }
    1430 
    1431     // generate normalized vectors
    1432     psVector *xNorm = psChebyshevNormVector (myPoly, x, 0);
    1433     psVector *yNorm = psChebyshevNormVector (myPoly, y, 1);
    1434 
    1435     // generate the N cheb polynomials based on xNorm, yNorm
    1436     psArray *xPolySet = psArrayAlloc (nXterm);
    1437     for (int i = 0; i < nXterm; i++) {
    1438         xPolySet->data[i] = psChebyshevPolyVector (xNorm, i);
    1439     }
    1440     psArray *yPolySet = psArrayAlloc (nYterm);
    1441     for (int i = 0; i < nYterm; i++) {
    1442         yPolySet->data[i] = psChebyshevPolyVector (yNorm, i);
    1443     }
    1444 
    1445     psF64 *fData = f->data.F64;         // Dereference f
    1446 
    1447     psImage *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
    1448     psVector *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
    1449 
    1450     // Initialize data structures (should not be able to fail)
    1451     psAssert (psImageInit(A, 0.0), "Could initialize data structures A");
    1452     psAssert (psVectorInit(B, 0.0), "Could initialize data structures B");
    1453 
    1454     // Dereference stuff, to make the loop go faster
    1455     psF64 **matrix = A->data.F64;       // Dereference the least-squares matrix
    1456     psF64 *vector = B->data.F64;        // Dereference the least-squares vector
    1457 
    1458     // loop over all elements of the data vector
    1459     for (int k = 0; k < x->n; k++) {
    1460 
    1461         if (!finite(fData[k])) continue;
    1462    
    1463         // XXX can we only calculate the upper diagonal?
    1464         int nelem = 0;
    1465         for (int jx = 0; jx < nXterm; jx++) {
    1466             psVector *jxCheb = xPolySet->data[jx];
    1467             for (int jy = 0; jy < nYterm; jy++) {
    1468                 psVector *jyCheb = yPolySet->data[jy];
    1469                 psF64 chebValue = jxCheb->data.F64[k] * jyCheb->data.F64[k];
    1470                
    1471                 vector[nelem] += fData[k] * chebValue;
    1472 
    1473                 int melem = 0;
    1474                 for (int kx = 0; kx < nXterm; kx++) {
    1475                     psVector *kxCheb = xPolySet->data[kx];
    1476                     for (int ky = 0; ky < nYterm; ky++) {
    1477                         psVector *kyCheb = yPolySet->data[ky];
    1478                         matrix[nelem][melem] += chebValue * kxCheb->data.F64[k]*kyCheb->data.F64[k];
    1479                         melem++;
    1480                     }
    1481                 }
    1482                 nelem++;
    1483             }
    1484         }
    1485     }
    1486 
    1487     if (psTraceGetLevel("psLib.math") >= 4) {
    1488         printf("Least-squares vector:\n");
    1489         for (int i = 0; i < nTerm; i++) {
    1490             printf("%f ", B->data.F64[i]);
    1491         }
    1492         printf("\n");
    1493         printf("Least-squares matrix:\n");
    1494         for (int i = 0; i < nTerm; i++) {
    1495             for (int j = 0; j < nTerm; j++) {
    1496                 printf("%f ", A->data.F64[i][j]);
    1497             }
    1498             printf("\n");
    1499         }
    1500     }
    1501 
    1502     bool status = false;
    1503     if (USE_GAUSS_JORDAN) {
    1504         status = psMatrixGJSolve(A, B);
    1505     } else {
    1506         status = psMatrixLUSolve(A, B);
    1507     }
    1508     if (!status) {
    1509         psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.\n");
    1510         goto escape;
    1511     }
    1512 
    1513     // unroll the result:
    1514     int nelem = 0;
    1515     for (int jx = 0; jx < nXterm; jx++) {
    1516         for (int jy = 0; jy < nYterm; jy++) {
    1517             myPoly->coeff[jx][jy]    = B->data.F64[nelem];
    1518             myPoly->coeffErr[jx][jy] = sqrt(A->data.F64[nelem][nelem]);
    1519             nelem ++;
    1520         }
    1521     }
    1522     psFree(A);
    1523     psFree(B);
    1524 
    1525     psFree (xNorm);
    1526     psFree (yNorm);
    1527     psFree (xPolySet);
    1528     psFree (yPolySet);
    1529 
    1530     return true;
    1531 
    1532 escape:
    1533     psFree (A);
    1534     psFree (B);
    1535     return false;
    1536 }
    1537 
    1538 /******************************************************************************
    1539 psVectorFitPolynomial2D():  This routine fits a 2D polynomial of arbitrary
    1540 degree (specified in poly) to the data points (x, y)-(f) and returns that
    1541 polynomial.  Types F32 and F64 are supported, however, type F32 is done via
    1542 vector conversion only.
    1543  *****************************************************************************/
    1544 bool psVectorFitPolynomial2D(
    1545     psPolynomial2D *poly,
    1546     const psVector *mask,
    1547     psVectorMaskType maskValue,
    1548     const psVector *f,
    1549     const psVector *fErr,
    1550     const psVector *x,
    1551     const psVector *y)
    1552 {
    1553     PS_ASSERT_POLY_NON_NULL(poly, false);
    1554     // PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
    1555 
    1556     PS_ASSERT_VECTOR_NON_NULL(f, false);
    1557     PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
    1558     PS_ASSERT_VECTOR_NON_NULL(x, false);
    1559     PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
    1560     PS_ASSERT_VECTOR_NON_NULL(y, false);
    1561     PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
    1562     if (mask != NULL) {
    1563         PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
    1564         PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
    1565     }
    1566     if (fErr != NULL) {
    1567         PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
    1568         PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, false);
    1569     }
    1570 
    1571     // Convert input vectors to F64 if necessary.
    1572     psVector *f64 = (f->type.type == PS_TYPE_F64) ? (psVector *) f : psVectorCopy(NULL, f, PS_TYPE_F64);
    1573     psVector *x64 = (x->type.type == PS_TYPE_F64) ? (psVector *) x : psVectorCopy(NULL, x, PS_TYPE_F64);
    1574     psVector *y64 = (y->type.type == PS_TYPE_F64) ? (psVector *) y : psVectorCopy(NULL, y, PS_TYPE_F64);
    1575 
    1576     psVector *fErr64 = NULL;
    1577     if (fErr != NULL) {
    1578         fErr64 = (fErr->type.type == PS_TYPE_F64) ? (psVector *) fErr : psVectorCopy(NULL, fErr, PS_TYPE_F64);
    1579     }
    1580 
    1581     bool result = true;
    1582 
    1583     switch (poly->type) {
    1584     case PS_POLYNOMIAL_ORD:
    1585         result = VectorFitPolynomial2DOrd(poly, mask, maskValue, f64, fErr64, x64, y64);
    1586         if (!result) {
    1587             psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
    1588         }
    1589         break;
    1590     case PS_POLYNOMIAL_CHEB:
    1591       if (mask != NULL) {
    1592           psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring mask and maskValue with Chebyshev polynomials.\n");
    1593       }
    1594       if (fErr != NULL) {
    1595           psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring error values for Chebyshev polynomials.\n");
    1596       }
    1597       result = VectorFitPolynomial2DCheb(poly, f64, x64, y64);
    1598       if (!result) {
    1599           psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
    1600       }
    1601       break;
    1602     default:
    1603         psError(PS_ERR_UNKNOWN, true, "Incorrect polynomial type.  Returning NULL.\n");
    1604         result = false;
    1605         break;
    1606     }
    1607 
    1608     // Free psVectors that were created for NULL arguments.
    1609     PS_FREE_TEMP_F64_VECTOR (f, f64);
    1610     PS_FREE_TEMP_F64_VECTOR (x, x64);
    1611     PS_FREE_TEMP_F64_VECTOR (y, y64);
    1612     PS_FREE_TEMP_F64_VECTOR (fErr, fErr64);
    1613 
    1614     return result;
    1615 }
    1616 
    1617 bool psVectorClipFitPolynomial2D(
    1618     psPolynomial2D *poly,
    1619     psStats *stats,
    1620     const psVector *mask,
    1621     psVectorMaskType maskValue,
    1622     const psVector *f,
    1623     const psVector *fErr,
    1624     const psVector *x,
    1625     const psVector *y)
    1626 {
    1627     psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
    1628     PS_ASSERT_POLY_NON_NULL(poly, false);
    1629     PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
    1630     PS_ASSERT_PTR_NON_NULL(stats, false);
    1631     PS_ASSERT_VECTOR_NON_NULL(mask, false);
    1632     PS_ASSERT_VECTOR_NON_NULL(f, false);
    1633     PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
    1634 
    1635     PS_ASSERT_VECTOR_NON_NULL(x, false);
    1636     PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
    1637     PS_ASSERT_VECTOR_TYPE(x, f->type.type, false);
    1638 
    1639     PS_ASSERT_VECTOR_NON_NULL(y, false);
    1640     PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
    1641     PS_ASSERT_VECTOR_TYPE(y, f->type.type, false);
    1642 
    1643     PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
    1644     PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
    1645 
    1646     if (fErr != NULL) {
    1647         PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
    1648         PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
    1649     }
    1650 
    1651     // the user supplies one of various stats option pairs,
    1652     // determine the desired mean and stdev STATS options:
    1653     // XXX enforce consistency?
    1654     // XXX psStatsGetValue() probably has inverted precedence
    1655     psStatsOptions meanOption = stats->options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN | PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN);
    1656     psStatsOptions stdevOption = stats->options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV | PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV);
    1657     if (!meanOption) {
    1658         psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
    1659         return false;
    1660     }
    1661     if (!stdevOption) {
    1662         psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
    1663         return false;
    1664     }
    1665 
    1666     // clipping range defined by min and max and/or clipSigma
    1667     psF32 minClipSigma;
    1668     psF32 maxClipSigma;
    1669     if (isfinite(stats->max)) {
    1670         maxClipSigma = fabs(stats->max);
    1671     } else {
    1672         maxClipSigma = fabs(stats->clipSigma);
    1673     }
    1674     if (isfinite(stats->min)) {
    1675         minClipSigma = fabs(stats->min);
    1676     } else {
    1677         minClipSigma = fabs(stats->clipSigma);
    1678     }
    1679     psVector *resid = psVectorAlloc(f->n, PS_TYPE_F64);
    1680 
    1681     psTrace("psLib.math", 4, "stats->clipIter is %d\n", stats->clipIter);
    1682     psTrace("psLib.math", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
    1683 
    1684     for (psS32 N = 0; N < stats->clipIter; N++) {
    1685         psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial1D()\n", N);
    1686         psS32 Nkeep = 0;
    1687         if (psTraceGetLevel("psLib.math") >= 7) {
    1688             if (mask != NULL) {
    1689                 for (psS32 i = 0 ; i < mask->n ; i++) {
    1690                     psTrace("psLib.math", 7,  "mask[%d] is %d\n", i, mask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
    1691                 }
    1692             }
    1693         }
    1694 
    1695         if (!psVectorFitPolynomial2D(poly, mask, maskValue, f, fErr, x, y)) {
    1696             psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the data.  Returning false.\n");
    1697             psFree(resid);
    1698             return false;
    1699         }
    1700 
    1701         psVector *fit = psPolynomial2DEvalVector(poly, x, y);
    1702         if (fit == NULL) {
    1703             psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial3DEvalVector().  Returning NULL.\n");
    1704             psFree(resid);
    1705             return false;
    1706         }
    1707 
    1708         for (psS32 i = 0 ; i < f->n ; i++) {
    1709             if (f->type.type == PS_TYPE_F64) {
    1710                 resid->data.F64[i] = f->data.F64[i] - fit->data.F64[i];
    1711             } else {
    1712                 resid->data.F64[i] = (psF64) (f->data.F32[i] - fit->data.F32[i]);
    1713             }
    1714         }
    1715 
    1716         if (psTraceGetLevel("psLib.math") >= 7) {
    1717             if (mask != NULL) {
    1718                 for (psS32 i = 0 ; i < mask->n ; i++) {
    1719                     if (!((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue))) {
    1720                         psTrace("psLib.math", 7,  "point %d at %f %f : value, fit : %f  %f resid: %f\n",
    1721                                 i, x->data.F32[i], y->data.F32[i], f->data.F32[i], fit->data.F32[i], resid->data.F64[i]);
    1722                     }
    1723                 }
    1724             }
    1725         }
    1726 
    1727         if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
    1728             psError(PS_ERR_UNKNOWN, false, "Could not compute statistics on the resid vector.  Returning NULL.\n");
    1729             psFree(resid);
    1730             psFree(fit);
    1731             return false;
    1732         }
    1733 
    1734         double meanValue = psStatsGetValue (stats, meanOption);
    1735         double stdevValue = psStatsGetValue (stats, stdevOption);
    1736 
    1737         psTrace("psLib.math", 5, "Mean is %f\n", meanValue);
    1738         psTrace("psLib.math", 5, "Stdev is %f\n", stdevValue);
    1739         psF32 minClipValue = -minClipSigma*stdevValue;
    1740         psF32 maxClipValue = +maxClipSigma*stdevValue;
    1741 
    1742         // set mask if pts are not valid
    1743         // we are masking out any point which is out of range
    1744         // recovery is not allowed with this scheme
    1745         for (psS32 i = 0; i < resid->n; i++) {
    1746             if ((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue)) {
    1747                 continue;
    1748             }
    1749 
    1750             if ((resid->data.F64[i] - meanValue > maxClipValue) || (resid->data.F64[i] - meanValue < minClipValue)) {
    1751                 if (fit->type.type == PS_TYPE_F64) {
    1752                     psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
    1753                             i, fit->data.F64[i], i, resid->data.F64[i]);
    1754                 } else {
    1755                     psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
    1756                             i, fit->data.F32[i], i, resid->data.F64[i]);
    1757                 }
    1758 
    1759                 if (mask != NULL) {
    1760                     mask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= 0x01;
    1761                 }
    1762                 continue;
    1763             }
    1764             Nkeep++;
    1765         }
    1766         psTrace("psLib.math", 4, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
    1767         stats->clippedNvalues = Nkeep;
    1768         psFree(fit);
    1769     }
    1770     // Free local temporary variables
    1771     psFree(resid);
    1772 
    1773     psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
    1774     return true;
    1775 }
    1776 
    1777 
    1778 /******************************************************************************
    1779  ******************************************************************************
    1780  3-D Vector Code.
    1781  ******************************************************************************
    1782  *****************************************************************************/
    1783 
    1784 /******************************************************************************
    1785 VectorFitPolynomial3DOrd(myPoly, *mask, maskValue, *f, *fErr, *x, *y, *z):
    1786 This is a private routine which will fit a 3-D polynomial to a set of (x,
    1787 y, z)-(f) pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
    1788  
    1789  *****************************************************************************/
    1790 static bool VectorFitPolynomial3DOrd(
    1791     psPolynomial3D* myPoly,
    1792     const psVector* mask,
    1793     psVectorMaskType maskValue,
    1794     const psVector *f,
    1795     const psVector *fErr,
    1796     const psVector *x,
    1797     const psVector *y,
    1798     const psVector *z)
    1799 {
    1800     psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    1801     PS_ASSERT_POLY_NON_NULL(myPoly, false);
    1802     PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
    1803     PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
    1804     PS_ASSERT_INT_NONNEGATIVE(myPoly->nZ, false);
    1805 
    1806     PS_ASSERT_VECTOR_NON_NULL(f, false);
    1807     PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
    1808     if (fErr != NULL) {
    1809         PS_ASSERT_VECTORS_SIZE_EQUAL(y, fErr, false);
    1810         PS_ASSERT_VECTOR_TYPE(fErr, PS_TYPE_F64, false);
    1811     }
    1812     PS_ASSERT_VECTOR_NON_NULL(x, false);
    1813     PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
    1814     PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
    1815     PS_ASSERT_VECTOR_NON_NULL(y, false);
    1816     PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
    1817     PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
    1818     PS_ASSERT_VECTOR_NON_NULL(z, false);
    1819     PS_ASSERT_VECTOR_TYPE(z, PS_TYPE_F64, false);
    1820     PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
    1821     if (mask != NULL) {
    1822         PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
    1823         PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
    1824     }
    1825 
    1826     int nXterm = 1 + myPoly->nX;        // Number of x terms
    1827     int nYterm = 1 + myPoly->nY;        // Number of y terms
    1828     int nZterm = 1 + myPoly->nZ;        // Number of z terms
    1829     int nTerm = nXterm * nYterm * nZterm; // Total number of terms
    1830     int nData = x->n;                   // Number of data points
    1831     psImage    *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
    1832     psVector   *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
    1833 
    1834     // Initialize data structures.
    1835     if (!psImageInit(A, 0.0) || !psVectorInit(B, 0.0)) {
    1836         psError(PS_ERR_UNKNOWN, false, "Could initialize data structures A, B.  Returning NULL.\n");
    1837         psFree(A);
    1838         psFree(B);
    1839         psTrace("psLib.math", 4, "---- %s() End ----\n", __func__);
    1840         return false;
    1841     }
    1842 
    1843     // Dereference points for speed in the loop
    1844     psF64 **matrix = A->data.F64;       // Least-squares matrix
    1845     psF64 *vector = B->data.F64;        // Least-squares vector
    1846     psF64 *xData = x->data.F64;         // x
    1847     psF64 *yData = y->data.F64;         // y
    1848     psF64 *zData = z->data.F64;         // z
    1849     psF64 *fData = f->data.F64;         // f
    1850     psF64 *fErrData = NULL;             // Error in f
    1851     if (fErr) {
    1852         fErrData = fErr->data.F64;
    1853     }
    1854     psVectorMaskType *dataMask = NULL;              // Mask for data
    1855     if (mask) {
    1856         dataMask = mask->data.PS_TYPE_VECTOR_MASK_DATA;
    1857     }
    1858     psMaskType ***coeffMask = myPoly->coeffMask;    // Mask for polynomial terms
    1859     int nYZterm = nYterm * nZterm;      // Multiplication of the numbers, to calculate the index
    1860 
    1861     // Build the B and A data structs.
    1862     psF64 ***Sums = NULL;         // Sums look like: 1, x, x^2, ... x^(2n+1), y, xy, x^2y, ... x^(2n+1)*y, ...
    1863     for (int k = 0; k < nData; k++) {
    1864         if (dataMask && dataMask[k] & maskValue) {
    1865             continue;
    1866         }
    1867 
    1868         Sums = BuildSums3D(Sums, xData[k], yData[k], zData[k], nXterm, nYterm, nZterm);
    1869 
    1870         double wt;
    1871         if (fErr == NULL) {
    1872             wt = 1.0;
    1873         } else {
    1874             // this filters fErr == 0 values
    1875             wt = (fErr->data.F64[k] == 0.0) ? 0.0 : 1.0 / PS_SQR(fErrData[k]);
    1876         }
    1877 
    1878         for (int i = 0; i < nTerm; i++) {
    1879             int ix = i / nYZterm; // x index
    1880             int iy = (i % nYZterm) / nZterm; // y index
    1881             int iz = (i % nYZterm) % nZterm; // z index
    1882             if (coeffMask[ix][iy][iz] & PS_POLY_MASK_BOTH) {
    1883                 matrix[i][i] = 1.0;
    1884                 continue;
    1885             }
    1886 
    1887             vector[i] += fData[k] * Sums[ix][iy][iz] * wt;
    1888             matrix[i][i] += Sums[2*ix][2*iy][2*iz] * wt;
    1889             for (int j = i + 1; j < nTerm; j++) {
    1890                 int jx = j / (nYZterm); // x index
    1891                 int jy = (j % nYZterm) / nZterm; // y index
    1892                 int jz = (j % nYZterm) % nZterm; // z index
    1893                 if (coeffMask[jx][jy][jz] & PS_POLY_MASK_BOTH) {
    1894                     continue;
    1895                 }
    1896                 double value = Sums[ix+jx][iy+jy][iz+jz] * wt;
    1897                 matrix[i][j] += value;
    1898                 matrix[j][i] += value;
    1899             }
    1900         }
    1901     }
    1902 
    1903     // Free the sums
    1904     for (psS32 ix = 0; ix < 2*nXterm; ix++) {
    1905         for (psS32 iy = 0; iy < 2*nYterm; iy++) {
    1906             psFree(Sums[ix][iy]);
    1907         }
    1908         psFree(Sums[ix]);
    1909     }
    1910     psFree(Sums);
    1911 
    1912 
    1913     bool status = false;
    1914     if (USE_GAUSS_JORDAN) {
    1915         status = psMatrixGJSolve(A, B);
    1916     } else {
    1917         status = psMatrixLUSolve(A, B);
    1918     }
    1919     if (!status) {
    1920         psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.\n");
    1921         goto escape;
    1922     }
    1923 
    1924     // select the appropriate solution entries
    1925     for (int i = 0; i < nTerm; i++) {
    1926         int ix = i / nYZterm; // x index
    1927         int iy = (i % nYZterm) / nZterm; // y index
    1928         int iz = (i % nYZterm) % nZterm; // z index
    1929         if (coeffMask[ix][iy][iz] & PS_POLY_MASK_FIT) continue;
    1930         myPoly->coeff[ix][iy][iz] = B->data.F64[i];
    1931         myPoly->coeffErr[ix][iy][iz] = sqrt(A->data.F64[i][i]);
    1932     }
    1933     psFree(A);
    1934     psFree(B);
    1935     return true;
    1936 
    1937 escape:
    1938     psFree(A);
    1939     psFree(B);
    1940     return false;
    1941 }
    1942 
    1943 /******************************************************************************
    1944 psVectorFitPolynomial3D():  This routine fits a 3D polynomial of arbitrary
    1945 degree (specified in poly) to the data points (x, y, z)-(f) and returns that
    1946 polynomial.  Types F32 and F64 are supported, however, type F32 is done via
    1947 vector conversion only.
    1948  *****************************************************************************/
    1949 bool psVectorFitPolynomial3D(
    1950     psPolynomial3D *poly,
    1951     const psVector *mask,
    1952     psVectorMaskType maskValue,
    1953     const psVector *f,
    1954     const psVector *fErr,
    1955     const psVector *x,
    1956     const psVector *y,
    1957     const psVector *z)
    1958 {
    1959     PS_ASSERT_POLY_NON_NULL(poly, false);
    1960     PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
    1961 
    1962     PS_ASSERT_VECTOR_NON_NULL(f, false);
    1963     PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
    1964     PS_ASSERT_VECTOR_NON_NULL(x, false);
    1965     PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
    1966     PS_ASSERT_VECTOR_NON_NULL(y, false);
    1967     PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
    1968     PS_ASSERT_VECTOR_NON_NULL(z, false);
    1969     PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
    1970     if (mask != NULL) {
    1971         PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
    1972         PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
    1973     }
    1974     if (fErr != NULL) {
    1975         PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
    1976         PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, false);
    1977     }
    1978 
    1979     // Convert input vectors to F64 if necessary.
    1980     psVector *f64 = (f->type.type == PS_TYPE_F64) ? (psVector *) f : psVectorCopy(NULL, f, PS_TYPE_F64);
    1981     psVector *x64 = (x->type.type == PS_TYPE_F64) ? (psVector *) x : psVectorCopy(NULL, x, PS_TYPE_F64);
    1982     psVector *y64 = (y->type.type == PS_TYPE_F64) ? (psVector *) y : psVectorCopy(NULL, y, PS_TYPE_F64);
    1983     psVector *z64 = (z->type.type == PS_TYPE_F64) ? (psVector *) z : psVectorCopy(NULL, z, PS_TYPE_F64);
    1984 
    1985     psVector *fErr64 = NULL;
    1986     if (fErr != NULL) {
    1987         fErr64 = (fErr->type.type == PS_TYPE_F64) ? (psVector *) fErr : psVectorCopy(NULL, fErr, PS_TYPE_F64);
    1988     }
    1989 
    1990     bool result = true;
    1991 
    1992     switch (poly->type) {
    1993     case PS_POLYNOMIAL_ORD:
    1994         result = VectorFitPolynomial3DOrd(poly, mask, maskValue, f64, fErr64, x64, y64, z64);
    1995         if (!result) {
    1996             psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
    1997         }
    1998         break;
    1999     case PS_POLYNOMIAL_CHEB:
    2000         if (mask != NULL) {
    2001             psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring mask and maskValue with Chebyshev polynomials.\n");
    2002         }
    2003         psError(PS_ERR_UNKNOWN, true, "3-D Chebyshev polynomial vector fitting has not been implemented.  Returning NULL.\n");
    2004         result = false;
    2005         break;
    2006     default:
    2007         psError(PS_ERR_UNKNOWN, true, "Incorrect polynomial type.  Returning NULL.\n");
    2008         result = false;
    2009         break;
    2010     }
    2011 
    2012     // Free psVectors that were created for NULL arguments.
    2013     PS_FREE_TEMP_F64_VECTOR (f, f64);
    2014     PS_FREE_TEMP_F64_VECTOR (x, x64);
    2015     PS_FREE_TEMP_F64_VECTOR (y, y64);
    2016     PS_FREE_TEMP_F64_VECTOR (z, z64);
    2017     PS_FREE_TEMP_F64_VECTOR (fErr, fErr64);
    2018 
    2019     return result;
    2020 }
    2021 
    2022 bool psVectorClipFitPolynomial3D(
    2023     psPolynomial3D *poly,
    2024     psStats *stats,
    2025     const psVector *mask,
    2026     psVectorMaskType maskValue,
    2027     const psVector *f,
    2028     const psVector *fErr,
    2029     const psVector *x,
    2030     const psVector *y,
    2031     const psVector *z)
    2032 {
    2033     psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
    2034     PS_ASSERT_POLY_NON_NULL(poly, false);
    2035     PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
    2036     PS_ASSERT_PTR_NON_NULL(stats, false);
    2037     PS_ASSERT_VECTOR_NON_NULL(mask, false);
    2038     PS_ASSERT_VECTOR_NON_NULL(f, false);
    2039     PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
    2040 
    2041     PS_ASSERT_VECTOR_NON_NULL(x, false);
    2042     PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
    2043     PS_ASSERT_VECTOR_TYPE(x, f->type.type, false);
    2044 
    2045     PS_ASSERT_VECTOR_NON_NULL(y, false);
    2046     PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
    2047     PS_ASSERT_VECTOR_TYPE(y, f->type.type, false);
    2048 
    2049     PS_ASSERT_VECTOR_NON_NULL(z, false);
    2050     PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
    2051     PS_ASSERT_VECTOR_TYPE(z, f->type.type, false);
    2052 
    2053     PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
    2054     PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
    2055 
    2056     if (fErr != NULL) {
    2057         PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
    2058         PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
    2059     }
    2060 
    2061     // the user supplies one of various stats option pairs,
    2062     // determine the desired mean and stdev STATS options:
    2063     // XXX enforce consistency?
    2064     // XXX psStatsGetValue() probably has inverted precedence
    2065     psStatsOptions meanOption = stats->options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN | PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN);
    2066     psStatsOptions stdevOption = stats->options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV | PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV);
    2067     if (!meanOption) {
    2068         psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
    2069         return false;
    2070     }
    2071     if (!stdevOption) {
    2072         psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
    2073         return false;
    2074     }
    2075 
    2076     // clipping range defined by min and max and/or clipSigma
    2077     psF32 minClipSigma;
    2078     psF32 maxClipSigma;
    2079     if (isfinite(stats->max)) {
    2080         maxClipSigma = fabs(stats->max);
    2081     } else {
    2082         maxClipSigma = fabs(stats->clipSigma);
    2083     }
    2084     if (isfinite(stats->min)) {
    2085         minClipSigma = fabs(stats->min);
    2086     } else {
    2087         minClipSigma = fabs(stats->clipSigma);
    2088     }
    2089     psVector *resid = psVectorAlloc(f->n, PS_TYPE_F64);
    2090 
    2091     psTrace("psLib.math", 4, "stats->clipIter is %d\n", stats->clipIter);
    2092     psTrace("psLib.math", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
    2093 
    2094     for (psS32 N = 0; N < stats->clipIter; N++) {
    2095         psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial1D()\n", N);
    2096         psS32 Nkeep = 0;
    2097         if (psTraceGetLevel("psLib.math") >= 6) {
    2098             if (mask != NULL) {
    2099                 for (psS32 i = 0 ; i < mask->n ; i++) {
    2100                     psTrace("psLib.math", 6,  "mask[%d] is %d\n", i, mask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
    2101                 }
    2102             }
    2103         }
    2104 
    2105         if (!psVectorFitPolynomial3D(poly, mask, maskValue, f, fErr, x, y, z)) {
    2106             psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the data.  Returning NULL.\n");
    2107             psFree(resid);
    2108             return false;
    2109         }
    2110         psVector *fit = psPolynomial3DEvalVector(poly, x, y, z);
    2111         if (fit == NULL) {
    2112             psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial3DEvalVector().  Returning NULL.\n");
    2113             psFree(resid);
    2114             return false;
    2115         }
    2116         for (psS32 i = 0 ; i < f->n ; i++) {
    2117             if (f->type.type == PS_TYPE_F64) {
    2118                 resid->data.F64[i] = f->data.F64[i] - fit->data.F64[i];
    2119             } else {
    2120                 resid->data.F64[i] = ((psF64) f->data.F32[i]) - fit->data.F64[i];
    2121             }
    2122         }
    2123 
    2124         if (psTraceGetLevel("psLib.math") >= 6) {
    2125             if (mask != NULL) {
    2126                 for (psS32 i = 0 ; i < mask->n ; i++) {
    2127                     if (!((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue))) {
    2128                         psTrace("psLib.math", 6,  "(f, fit)[%d] is (%f, %f).  resid is (%f)\n",
    2129                                 i, f->data.F32[i], fit->data.F32[i], resid->data.F64[i]);
    2130                     }
    2131                 }
    2132             }
    2133         }
    2134 
    2135         if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
    2136             psError(PS_ERR_UNKNOWN, false, "Could not compute statistics on the resid vector.  Returning NULL.\n");
    2137             psFree(resid);
    2138             psFree(fit);
    2139             return false;
    2140         }
    2141 
    2142         double meanValue = psStatsGetValue (stats, meanOption);
    2143         double stdevValue = psStatsGetValue (stats, stdevOption);
    2144 
    2145         psTrace("psLib.math", 5, "Mean is %f\n", meanValue);
    2146         psTrace("psLib.math", 5, "Stdev is %f\n", stdevValue);
    2147         psF32 minClipValue = -minClipSigma*stdevValue;
    2148         psF32 maxClipValue = +maxClipSigma*stdevValue;
    2149 
    2150         // set mask if pts are not valid
    2151         // we are masking out any point which is out of range
    2152         // recovery is not allowed with this scheme
    2153         for (psS32 i = 0; i < resid->n; i++) {
    2154             if ((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue)) {
    2155                 continue;
    2156             }
    2157 
    2158             if ((resid->data.F64[i] - meanValue > maxClipValue) || (resid->data.F64[i] - meanValue < minClipValue))  {
    2159                 if (f->type.type == PS_TYPE_F64) {
    2160                     psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
    2161                             i, fit->data.F64[i], i, resid->data.F64[i]);
    2162                 } else {
    2163                     psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
    2164                             i, fit->data.F32[i], i, resid->data.F64[i]);
    2165                 }
    2166 
    2167                 if (mask != NULL) {
    2168                     mask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= 0x01;
    2169                 }
    2170                 continue;
    2171             }
    2172             Nkeep++;
    2173         }
    2174         psTrace("psLib.math", 6, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
    2175         stats->clippedNvalues = Nkeep;
    2176         psFree(fit);
    2177     }
    2178     // Free local temporary variables
    2179     psFree(resid);
    2180 
    2181     psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
    2182     return true;
    2183 }
    2184 
    2185 /******************************************************************************
    2186  ******************************************************************************
    2187  4-D Vector Code.
    2188  ******************************************************************************
    2189  *****************************************************************************/
    2190 /******************************************************************************
    2191 VectorFitPolynomial4DOrd(myPoly, *mask, maskValue, *f, *fErr, *x, *y, *z, *t):
    2192 This is a private routine which will fit a 4-D polynomial to a set of (x,
    2193 y, z, t)-(f) pairs.  All non-NULL vectors must be of type PS_TYPE_F64.
    2194  
    2195  *****************************************************************************/
    2196 static bool VectorFitPolynomial4DOrd(
    2197     psPolynomial4D* myPoly,
    2198     const psVector* mask,
    2199     psVectorMaskType maskValue,
    2200     const psVector *f,
    2201     const psVector *fErr,
    2202     const psVector *x,
    2203     const psVector *y,
    2204     const psVector *z,
    2205     const psVector *t)
    2206 {
    2207     psTrace("psLib.math", 4, "---- %s() begin ----\n", __func__);
    2208     PS_ASSERT_POLY_NON_NULL(myPoly, false);
    2209     PS_ASSERT_INT_NONNEGATIVE(myPoly->nX, false);
    2210     PS_ASSERT_INT_NONNEGATIVE(myPoly->nY, false);
    2211     PS_ASSERT_INT_NONNEGATIVE(myPoly->nZ, false);
    2212     PS_ASSERT_INT_NONNEGATIVE(myPoly->nT, false);
    2213     PS_ASSERT_VECTOR_NON_NULL(f, false);
    2214     PS_ASSERT_VECTOR_TYPE(f, PS_TYPE_F64, false);
    2215     if (fErr != NULL) {
    2216         PS_ASSERT_VECTORS_SIZE_EQUAL(y, fErr, false);
    2217         PS_ASSERT_VECTOR_TYPE(fErr, PS_TYPE_F64, false);
    2218     }
    2219     PS_ASSERT_VECTOR_NON_NULL(x, false);
    2220     PS_ASSERT_VECTOR_TYPE(x, PS_TYPE_F64, false);
    2221     PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
    2222     PS_ASSERT_VECTOR_NON_NULL(y, false);
    2223     PS_ASSERT_VECTOR_TYPE(y, PS_TYPE_F64, false);
    2224     PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
    2225     PS_ASSERT_VECTOR_NON_NULL(z, false);
    2226     PS_ASSERT_VECTOR_TYPE(z, PS_TYPE_F64, false);
    2227     PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
    2228     PS_ASSERT_VECTOR_NON_NULL(t, false);
    2229     PS_ASSERT_VECTOR_TYPE(t, PS_TYPE_F64, false);
    2230     PS_ASSERT_VECTORS_SIZE_EQUAL(f, t, false);
    2231     if (mask) {
    2232         PS_ASSERT_VECTORS_SIZE_EQUAL(y, mask, false);
    2233         PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
    2234     }
    2235 
    2236 
    2237     int nXterm = 1 + myPoly->nX;        // Number of x terms
    2238     int nYterm = 1 + myPoly->nY;        // Number of y terms
    2239     int nZterm = 1 + myPoly->nZ;        // Number of z terms
    2240     int nTterm = 1 + myPoly->nT;        // Number of t terms
    2241     int nTerm = nXterm * nYterm * nZterm * nTterm; // Total number of terms
    2242     int nData = x->n;                   // Number of data points
    2243     psImage    *A = psImageAlloc(nTerm, nTerm, PS_TYPE_F64); // Least-squares matrix
    2244     psVector   *B = psVectorAlloc(nTerm, PS_TYPE_F64); // Least-squares vector
    2245 
    2246     // Initialize data structures.
    2247     if (!psImageInit(A, 0.0) || !psVectorInit(B, 0.0)) {
    2248         psError(PS_ERR_UNKNOWN, false, "Could initialize data structures A, B.  Returning NULL.\n");
    2249         psFree(A);
    2250         psFree(B);
    2251         psTrace("psLib.math", 4, "---- %s() End ----\n", __func__);
    2252         return false;
    2253     }
    2254 
    2255     // Dereference points for speed in the loop
    2256     psF64 **matrix = A->data.F64;       // Least-squares matrix
    2257     psF64 *vector = B->data.F64;        // Least-squares vector
    2258     psF64 *xData = x->data.F64;         // x
    2259     psF64 *yData = y->data.F64;         // y
    2260     psF64 *zData = z->data.F64;         // z
    2261     psF64 *tData = t->data.F64;         // t
    2262     psF64 *fData = f->data.F64;         // f
    2263     psF64 *fErrData = NULL;             // Error in f
    2264     if (fErr) {
    2265         fErrData = fErr->data.F64;
    2266     }
    2267     psVectorMaskType *dataMask = NULL;              // Mask for data
    2268     if (mask) {
    2269         dataMask = mask->data.PS_TYPE_VECTOR_MASK_DATA;
    2270     }
    2271     psMaskType ****coeffMask = myPoly->coeffMask;    // Mask for polynomial terms
    2272     int nYZTterm = nYterm * nZterm * nTterm; // Multiplication of the numbers, for calculating the index
    2273     int nZTterm = nZterm * nTterm;      // Multiplication of the numbers, for calculating the index
    2274 
    2275     // Build the B and A data structs.
    2276     psF64 ****Sums = NULL;        // Sums look like: 1, x, x^2, ... x^(2n+1), y, xy, x^2y, ... x^(2n+1)*y, ...
    2277     for (int k = 0; k < nData; k++) {
    2278         if (dataMask && dataMask[k] & maskValue) {
    2279             continue;
    2280         }
    2281 
    2282         Sums = BuildSums4D(Sums, xData[k], yData[k], zData[k], tData[k], nXterm, nYterm, nZterm, nTterm);
    2283 
    2284         double wt;
    2285         if (fErr == NULL) {
    2286             wt = 1.0;
    2287         } else {
    2288             // this filters fErr == 0 values
    2289             wt = (fErr->data.F64[k] == 0.0) ? 0.0 : 1.0 / PS_SQR(fErrData[k]);
    2290         }
    2291 
    2292         for (int i = 0; i < nTerm; i++) {
    2293             int ix = i / (nYZTterm); // x index
    2294             int iy = (i % (nYZTterm)) / (nZTterm); // y index
    2295             int iz = ((i % (nYZTterm)) % (nZTterm)) / nTterm; // z index
    2296             int it = ((i % (nYZTterm)) % (nZTterm)) % nTterm; // t index
    2297             if (coeffMask[ix][iy][iz][it] & PS_POLY_MASK_BOTH) {
    2298                 matrix[i][i] = 1.0;
    2299                 continue;
    2300             }
    2301 
    2302             vector[i] += fData[k] * Sums[ix][iy][iz][it] * wt;
    2303             matrix[i][i] += Sums[2*ix][2*iy][2*iz][2*it] * wt;
    2304             for (int j = i + 1; j < nTerm; j++) {
    2305                 int jx = j / nYZTterm; // x index
    2306                 int jy = (j % nYZTterm) / nZTterm; // y index
    2307                 int jz = ((j % nYZTterm) % nZTterm) / nTterm; // z index
    2308                 int jt = ((j % nYZTterm) % nZTterm) % nTterm; // t index
    2309                 if (coeffMask[jx][jy][jz][jt] & PS_POLY_MASK_BOTH) {
    2310                     continue;
    2311                 }
    2312                 double value = Sums[ix+jx][iy+jy][iz+jz][it+jt] * wt;
    2313                 matrix[i][j] += value;
    2314                 matrix[j][i] += value;
    2315             }
    2316         }
    2317     }
    2318 
    2319     // Free the sums
    2320     if (Sums == NULL) {
    2321         assert (nData == 0);
    2322     } else {
    2323         for (int ix = 0; ix < 2*nXterm; ix++) {
    2324             for (int iy = 0; iy < 2*nYterm; iy++) {
    2325                 for (int iz = 0; iz < 2*nZterm; iz++) {
    2326                     psFree(Sums[ix][iy][iz]);
    2327                 }
    2328                 psFree(Sums[ix][iy]);
    2329             }
    2330             psFree(Sums[ix]);
    2331         }
    2332         psFree(Sums);
    2333     }
    2334 
    2335     bool status = false;
    2336     if (USE_GAUSS_JORDAN) {
    2337         status = psMatrixGJSolve(A, B);
    2338     } else {
    2339         status = psMatrixLUSolve(A, B);
    2340     }
    2341     if (!status) {
    2342         psError(PS_ERR_UNKNOWN, false, "Could not solve linear equations.\n");
    2343         goto escape;
    2344     }
    2345 
    2346     // select the appropriate solution entries
    2347     for (int i = 0; i < nTerm; i++) {
    2348         int ix = i / nYZTterm; // x index
    2349         int iy = (i % nYZTterm) / nZTterm; // y index
    2350         int iz = ((i % nYZTterm) % nZTterm) / nTterm; // z index
    2351         int it = ((i % nYZTterm) % nZTterm) % nTterm; // t index
    2352         if (coeffMask[ix][iy][iz][it] & PS_POLY_MASK_FIT) continue;
    2353         myPoly->coeff[ix][iy][iz][it] = B->data.F64[i];
    2354         myPoly->coeffErr[ix][iy][iz][it] = sqrt(A->data.F64[i][i]);
    2355     }
    2356     psFree(A);
    2357     psFree(B);
    2358     return true;
    2359 
    2360 escape:
    2361     psFree(A);
    2362     psFree(B);
    2363     return false;
    2364 }
    2365 
    2366 /******************************************************************************
    2367 psVectorFitPolynomial4D():  This routine fits a 4D polynomial of arbitrary
    2368 degree (specified in poly) to the data points (x, y, z, t)-(f) and returns
    2369 that polynomial.  Types F32 and F64 are supported, however, type F32 is done
    2370 via vector conversion only.
    2371  *****************************************************************************/
    2372 bool psVectorFitPolynomial4D(
    2373     psPolynomial4D *poly,
    2374     const psVector *mask,
    2375     psVectorMaskType maskValue,
    2376     const psVector *f,
    2377     const psVector *fErr,
    2378     const psVector *x,
    2379     const psVector *y,
    2380     const psVector *z,
    2381     const psVector *t)
    2382 {
    2383     PS_ASSERT_POLY_NON_NULL(poly, false);
    2384     PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
    2385 
    2386     PS_ASSERT_VECTOR_NON_NULL(f, false);
    2387     PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
    2388     PS_ASSERT_VECTOR_NON_NULL(x, false);
    2389     PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
    2390     PS_ASSERT_VECTOR_NON_NULL(y, false);
    2391     PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
    2392     PS_ASSERT_VECTOR_NON_NULL(z, false);
    2393     PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
    2394     PS_ASSERT_VECTOR_NON_NULL(t, false);
    2395     PS_ASSERT_VECTORS_SIZE_EQUAL(f, t, false);
    2396     if (mask) {
    2397         PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
    2398         PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
    2399     }
    2400     if (fErr != NULL) {
    2401         PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
    2402         PS_ASSERT_VECTOR_TYPE_F32_OR_F64(fErr, false);
    2403     }
    2404 
    2405     // Convert input vectors to F64 if necessary.
    2406     psVector *f64 = (f->type.type == PS_TYPE_F64) ? (psVector *) f : psVectorCopy(NULL, f, PS_TYPE_F64);
    2407     psVector *x64 = (x->type.type == PS_TYPE_F64) ? (psVector *) x : psVectorCopy(NULL, x, PS_TYPE_F64);
    2408     psVector *y64 = (y->type.type == PS_TYPE_F64) ? (psVector *) y : psVectorCopy(NULL, y, PS_TYPE_F64);
    2409     psVector *z64 = (z->type.type == PS_TYPE_F64) ? (psVector *) z : psVectorCopy(NULL, z, PS_TYPE_F64);
    2410     psVector *t64 = (t->type.type == PS_TYPE_F64) ? (psVector *) t : psVectorCopy(NULL, t, PS_TYPE_F64);
    2411 
    2412     psVector *fErr64 = NULL;
    2413     if (fErr != NULL) {
    2414         fErr64 = (fErr->type.type == PS_TYPE_F64) ? (psVector *) fErr : psVectorCopy(NULL, fErr, PS_TYPE_F64);
    2415     }
    2416 
    2417     bool result = true;
    2418 
    2419     switch (poly->type) {
    2420     case PS_POLYNOMIAL_ORD:
    2421         result = VectorFitPolynomial4DOrd(poly, mask, maskValue, f64, fErr64, x64, y64, z64, t64);
    2422         if (!result) {
    2423             psError(PS_ERR_UNKNOWN, true, "Could not fit polynomial.  Returning NULL.\n");
    2424         }
    2425         break;
    2426     case PS_POLYNOMIAL_CHEB:
    2427         if (mask != NULL) {
    2428             psLogMsg(__func__, PS_LOG_WARN, "WARNING: ignoring mask and maskValue with Chebyshev polynomials.\n");
    2429         }
    2430         psError(PS_ERR_UNKNOWN, true, "4-D Chebyshev polynomial vector fitting has not been implemented.  Returning NULL.\n");
    2431         result = false;
    2432         break;
    2433     default:
    2434         psError(PS_ERR_UNKNOWN, true, "Incorrect polynomial type.  Returning NULL.\n");
    2435         result = false;
    2436         break;
    2437     }
    2438 
    2439     // Free psVectors that were created for NULL arguments.
    2440     PS_FREE_TEMP_F64_VECTOR (f, f64);
    2441     PS_FREE_TEMP_F64_VECTOR (x, x64);
    2442     PS_FREE_TEMP_F64_VECTOR (y, y64);
    2443     PS_FREE_TEMP_F64_VECTOR (z, z64);
    2444     PS_FREE_TEMP_F64_VECTOR (t, t64);
    2445     PS_FREE_TEMP_F64_VECTOR (fErr, fErr64);
    2446 
    2447     return result;
    2448 }
    2449 
    2450 
    2451 bool psVectorClipFitPolynomial4D(
    2452     psPolynomial4D *poly,
    2453     psStats *stats,
    2454     const psVector *mask,
    2455     psVectorMaskType maskValue,
    2456     const psVector *f,
    2457     const psVector *fErr,
    2458     const psVector *x,
    2459     const psVector *y,
    2460     const psVector *z,
    2461     const psVector *t)
    2462 {
    2463     psTrace("psLib.math", 3, "---- %s() begin ----\n", __func__);
    2464     PS_ASSERT_POLY_NON_NULL(poly, false);
    2465     PS_ASSERT_POLY_TYPE(poly, PS_POLYNOMIAL_ORD, false);
    2466     PS_ASSERT_PTR_NON_NULL(stats, false);
    2467     PS_ASSERT_VECTOR_NON_NULL(mask, false);
    2468     PS_ASSERT_VECTOR_NON_NULL(f, false);
    2469     PS_ASSERT_VECTOR_TYPE_F32_OR_F64(f, false);
    2470 
    2471     PS_ASSERT_VECTOR_NON_NULL(x, false);
    2472     PS_ASSERT_VECTORS_SIZE_EQUAL(f, x, false);
    2473     PS_ASSERT_VECTOR_TYPE(x, f->type.type, false);
    2474 
    2475     PS_ASSERT_VECTOR_NON_NULL(y, false);
    2476     PS_ASSERT_VECTORS_SIZE_EQUAL(f, y, false);
    2477     PS_ASSERT_VECTOR_TYPE(y, f->type.type, false);
    2478 
    2479     PS_ASSERT_VECTOR_NON_NULL(z, false);
    2480     PS_ASSERT_VECTORS_SIZE_EQUAL(f, z, false);
    2481     PS_ASSERT_VECTOR_TYPE(z, f->type.type, false);
    2482 
    2483     PS_ASSERT_VECTOR_NON_NULL(t, false);
    2484     PS_ASSERT_VECTORS_SIZE_EQUAL(f, t, false);
    2485     PS_ASSERT_VECTOR_TYPE(t, f->type.type, false);
    2486 
    2487     PS_ASSERT_VECTORS_SIZE_EQUAL(f, mask, false);
    2488     PS_ASSERT_VECTOR_TYPE(mask, PS_TYPE_VECTOR_MASK, false);
    2489 
    2490     if (fErr != NULL) {
    2491         PS_ASSERT_VECTORS_SIZE_EQUAL(f, fErr, false);
    2492         PS_ASSERT_VECTOR_TYPE(fErr, f->type.type, false);
    2493     }
    2494 
    2495     // the user supplies one of various stats option pairs,
    2496     // determine the desired mean and stdev STATS options:
    2497     // XXX enforce consistency?
    2498     // XXX psStatsGetValue() probably has inverted precedence
    2499     psStatsOptions meanOption = stats->options & (PS_STAT_SAMPLE_MEAN | PS_STAT_SAMPLE_MEDIAN | PS_STAT_ROBUST_MEDIAN | PS_STAT_CLIPPED_MEAN | PS_STAT_FITTED_MEAN | PS_STAT_FITTED_MEAN);
    2500     psStatsOptions stdevOption = stats->options & (PS_STAT_SAMPLE_STDEV | PS_STAT_ROBUST_STDEV | PS_STAT_CLIPPED_STDEV | PS_STAT_FITTED_STDEV | PS_STAT_FITTED_STDEV);
    2501     if (!meanOption) {
    2502         psError(PS_ERR_UNKNOWN, true, "no valid mean stats option selected");
    2503         return false;
    2504     }
    2505     if (!stdevOption) {
    2506         psError(PS_ERR_UNKNOWN, true, "no valid stdev stats option selected");
    2507         return false;
    2508     }
    2509 
    2510     // clipping range defined by min and max and/or clipSigma
    2511     psF32 minClipSigma;
    2512     psF32 maxClipSigma;
    2513     if (isfinite(stats->max)) {
    2514         maxClipSigma = fabs(stats->max);
    2515     } else {
    2516         maxClipSigma = fabs(stats->clipSigma);
    2517     }
    2518     if (isfinite(stats->min)) {
    2519         minClipSigma = fabs(stats->min);
    2520     } else {
    2521         minClipSigma = fabs(stats->clipSigma);
    2522     }
    2523     psVector *resid = psVectorAlloc(f->n, PS_TYPE_F64);
    2524 
    2525     psTrace("psLib.math", 4, "stats->clipIter is %d\n", stats->clipIter);
    2526     psTrace("psLib.math", 4, "(minClipSigma, maxClipSigma) is (%.2f, %.2f)\n", minClipSigma, maxClipSigma);
    2527 
    2528     for (psS32 N = 0; N < stats->clipIter; N++) {
    2529         psTrace("psLib.math", 6, "Loop iteration %d.  Calling psVectorFitPolynomial4D()\n", N);
    2530         psS32 Nkeep = 0;
    2531         if (psTraceGetLevel("psLib.math") >= 6) {
    2532             if (mask != NULL) {
    2533                 for (psS32 i = 0 ; i < mask->n ; i++) {
    2534                     psTrace("psLib.math", 6,  "mask[%d] is %d\n", i, mask->data.PS_TYPE_VECTOR_MASK_DATA[i]);
    2535                 }
    2536             }
    2537         }
    2538 
    2539         if (!psVectorFitPolynomial4D (poly, mask, maskValue, f, fErr, x, y, z, t)) {
    2540             psError(PS_ERR_UNKNOWN, false, "Could not fit a polynomial to the data.  Returning NULL.\n");
    2541             psFree(resid);
    2542             return false;
    2543         }
    2544 
    2545         psVector *fit = psPolynomial4DEvalVector (poly, x, y, z, t);
    2546         if (fit == NULL) {
    2547             psError(PS_ERR_UNKNOWN, false, "Could not call psPolynomial4DEvalVector().  Returning NULL.\n");
    2548             psFree(resid);
    2549             return false;
    2550         }
    2551         for (psS32 i = 0 ; i < f->n ; i++) {
    2552             if (f->type.type == PS_TYPE_F64) {
    2553                 resid->data.F64[i] = f->data.F64[i] - fit->data.F64[i];
    2554             } else {
    2555                 resid->data.F64[i] = ((psF64) f->data.F32[i]) - fit->data.F64[i];
    2556             }
    2557         }
    2558 
    2559         if (psTraceGetLevel("psLib.math") >= 6) {
    2560             if (mask != NULL) {
    2561                 for (psS32 i = 0 ; i < mask->n ; i++) {
    2562                     if (!((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue))) {
    2563                         psTrace("psLib.math", 6,  "(f, fit)[%d] is (%f, %f).  resid is (%f)\n",
    2564                                 i, f->data.F32[i], fit->data.F32[i], resid->data.F64[i]);
    2565                     }
    2566                 }
    2567             }
    2568         }
    2569 
    2570         if (!psVectorStats(stats, resid, NULL, mask, maskValue)) {
    2571             psError(PS_ERR_UNKNOWN, false, "Could not compute statistics on the resid vector.  Returning NULL.\n");
    2572             psFree(resid);
    2573             psFree(fit);
    2574             return false;
    2575         }
    2576 
    2577         double meanValue = psStatsGetValue (stats, meanOption);
    2578         double stdevValue = psStatsGetValue (stats, stdevOption);
    2579 
    2580         psTrace("psLib.math", 5, "Mean is %f\n", meanValue);
    2581         psTrace("psLib.math", 5, "Stdev is %f\n", stdevValue);
    2582         psF32 minClipValue = -minClipSigma*stdevValue;
    2583         psF32 maxClipValue = +maxClipSigma*stdevValue;
    2584 
    2585         // set mask if pts are not valid
    2586         // we are masking out any point which is out of range
    2587         // recovery is not allowed with this scheme
    2588         for (psS32 i = 0; i < resid->n; i++) {
    2589             if ((mask != NULL) && (mask->data.PS_TYPE_VECTOR_MASK_DATA[i] & maskValue)) {
    2590                 continue;
    2591             }
    2592 
    2593             if ((resid->data.F64[i] - meanValue > maxClipValue) || (resid->data.F64[i] - meanValue < minClipValue)) {
    2594                 if (f->type.type == PS_TYPE_F64) {
    2595                     psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
    2596                             i, fit->data.F64[i], i, resid->data.F64[i]);
    2597                 } else {
    2598                     psTrace("psLib.math", 6, "Masking element %d (%f).  resid->data.F64[%d] is %f\n",
    2599                             i, fit->data.F32[i], i, resid->data.F64[i]);
    2600                 }
    2601 
    2602                 if (mask != NULL) {
    2603                     mask->data.PS_TYPE_VECTOR_MASK_DATA[i] |= 0x01;
    2604                 }
    2605                 continue;
    2606             }
    2607             Nkeep++;
    2608         }
    2609         psTrace("psLib.math", 6, "keeping %d of %ld pts for fit\n", Nkeep, x->n);
    2610         stats->clippedNvalues = Nkeep;
    2611         psFree (fit);
    2612     }
    2613     // Free local temporary variables
    2614     psFree (resid);
    2615 
    2616     psTrace("psLib.math", 3, "---- %s() end ----\n", __func__);
    2617     return true;
    2618 }
  • branches/eam_branches/ipp-20230313/psLib/src/math/psMinimizePolyFit.h

    r42492 r42506  
    144144);
    145145
     146bool psVectorIRLSFitPolynomial2D(
     147    psPolynomial2D *poly,
     148    psStats *stats,
     149    const psVector *mask,
     150    psVectorMaskType maskValue,
     151    const psVector *f,
     152    const psVector *fErr,
     153    const psVector *x,
     154    const psVector *y
     155);
     156
     157bool psVectorIRLSFitPolynomial3D(
     158    psPolynomial3D *poly,
     159    psStats *stats,
     160    const psVector *mask,
     161    psVectorMaskType maskValue,
     162    const psVector *f,
     163    const psVector *fErr,
     164    const psVector *x,
     165    const psVector *y,
     166    const psVector *z
     167);
     168
     169bool psVectorIRLSFitPolynomial4D(
     170    psPolynomial4D *poly,
     171    psStats *stats,
     172    const psVector *mask,
     173    psVectorMaskType maskValue,
     174    const psVector *f,
     175    const psVector *fErr,
     176    const psVector *x,
     177    const psVector *y,
     178    const psVector *z,
     179    const psVector *t
     180);
     181
    146182/// @}
    147183#endif // #ifndef PS_MINIMIZE_POLYFIT_H
  • branches/eam_branches/ipp-20230313/psLib/src/math/psPolynomial.c

    r42492 r42506  
    944944    bool match = true;
    945945    match &= (poly->type == type);
    946     match &= (poly->nX == type);
     946    match &= (poly->nX   == nX);
    947947
    948948    if (!match) {
    949         psFree (poly->coeff);
    950         psFree (poly->coeffErr);
    951         psFree (poly->coeffMask);
     949        polynomial1DFree (poly); // frees the coeffs
    952950
    953951        poly->type = type;
    954         poly->nX = nX;
     952        poly->nX   = nX;
    955953
    956954        poly->coeff = psAlloc((1 + nX) * sizeof(psF64));
     
    976974    bool match = true;
    977975    match &= (poly->type == type);
    978     match &= (poly->nX == type);
    979     match &= (poly->nY == type);
     976    match &= (poly->nX ==   nX);
     977    match &= (poly->nY ==   nY);
    980978
    981979    if (!match) {
    982         for (int i = 0; i < poly->nX + 1; i++) {
    983             psFree (poly->coeff[i]);
    984             psFree (poly->coeffErr[i]);
    985             psFree (poly->coeffMask[i]);
    986         }
    987         psFree (poly->coeff);
    988         psFree (poly->coeffErr);
    989         psFree (poly->coeffMask);
     980        polynomial2DFree (poly); // frees the coeffs
    990981
    991982        poly->type = type;
    992         poly->nX = nX;
    993         poly->nY = nY;
     983        poly->nX   = nX;
     984        poly->nY   = nY;
    994985
    995986        poly->coeff = psAlloc((1 + nX) * sizeof(psF64 *));
     
    10121003}
    10131004
    1014 // XXX 3D, 4D versions
     1005bool psPolynomial3DRecycle(psPolynomial3D *poly,
     1006                           psPolynomialType type,
     1007                           unsigned int nX,
     1008                           unsigned int nY,
     1009                           unsigned int nZ)
     1010{
     1011    PS_ASSERT_INT_NONNEGATIVE(nX, NULL);
     1012    PS_ASSERT_INT_NONNEGATIVE(nY, NULL);
     1013    PS_ASSERT_INT_NONNEGATIVE(nZ, NULL);
     1014
     1015    bool match = true;
     1016    match &= (poly->type == type);
     1017    match &= (poly->nX   == nX);
     1018    match &= (poly->nY   == nY);
     1019    match &= (poly->nZ   == nZ);
     1020
     1021    if (!match) {
     1022        polynomial3DFree (poly); // frees the coeffs
     1023
     1024        poly->type = type;
     1025        poly->nX = nX;
     1026        poly->nY = nY;
     1027        poly->nZ = nZ;
     1028
     1029        poly->coeff = psAlloc((nX + 1) * sizeof(psF64 **));
     1030        poly->coeffErr = psAlloc((nX + 1) * sizeof(psF64 **));
     1031        poly->coeffMask = (psMaskType ***)psAlloc((nX + 1) * sizeof(psMaskType **));
     1032        for (int ix = 0; ix < (1 + nX); ix++) {
     1033            poly->coeff[ix] = psAlloc((nY + 1) * sizeof(psF64 *));
     1034            poly->coeffErr[ix] = psAlloc((nY + 1) * sizeof(psF64 *));
     1035            poly->coeffMask[ix] = (psMaskType **)psAlloc((nY + 1) * sizeof(psMaskType *));
     1036            for (int iy = 0; iy < (nY + 1); iy++) {
     1037                poly->coeff[ix][iy] = psAlloc((nZ + 1) * sizeof(psF64));
     1038                poly->coeffErr[ix][iy] = psAlloc((nZ + 1) * sizeof(psF64));
     1039                poly->coeffMask[ix][iy] = (psMaskType *)psAlloc((nZ + 1) * sizeof(psMaskType));
     1040            }
     1041        }
     1042    }
     1043    for (int ix = 0; ix < (1 + nX); ix++) {
     1044        for (int iy = 0; iy < (1 + nY); iy++) {
     1045            for (int iz = 0; iz < (1 + nZ); iz++) {
     1046                poly->coeff[ix][iy][iz]     = 0.0;
     1047                poly->coeffErr[ix][iy][iz]  = 0.0;
     1048                poly->coeffMask[ix][iy][iz] = PS_POLY_MASK_NONE;
     1049            }
     1050        }
     1051    }
     1052    return(true);
     1053}
     1054
     1055bool psPolynomial4DRecycle(psPolynomial4D *poly,
     1056                           psPolynomialType type,
     1057                           unsigned int nX,
     1058                           unsigned int nY,
     1059                           unsigned int nZ,
     1060                           unsigned int nT)
     1061{
     1062    PS_ASSERT_INT_NONNEGATIVE(nX, NULL);
     1063    PS_ASSERT_INT_NONNEGATIVE(nY, NULL);
     1064    PS_ASSERT_INT_NONNEGATIVE(nZ, NULL);
     1065    PS_ASSERT_INT_NONNEGATIVE(nT, NULL);
     1066
     1067    bool match = true;
     1068    match &= (poly->type == type);
     1069    match &= (poly->nX   == nX);
     1070    match &= (poly->nY   == nY);
     1071    match &= (poly->nZ   == nZ);
     1072    match &= (poly->nT   == nT);
     1073
     1074    if (!match) {
     1075        polynomial4DFree (poly); // frees the coeffs
     1076
     1077        poly->type = type;
     1078        poly->nX = nX;
     1079        poly->nY = nY;
     1080        poly->nZ = nZ;
     1081        poly->nT = nT;
     1082
     1083        poly->coeff = psAlloc((nX + 1) * sizeof(psF64 ***));
     1084        poly->coeffErr = psAlloc((nX + 1) * sizeof(psF64 ***));
     1085        poly->coeffMask = (psMaskType ****)psAlloc((nX + 1) * sizeof(psMaskType ***));
     1086        for (int ix = 0; ix < (nX + 1); ix++) {
     1087            poly->coeff[ix] = psAlloc((nY + 1) * sizeof(psF64 **));
     1088            poly->coeffErr[ix] = psAlloc((nY + 1) * sizeof(psF64 **));
     1089            poly->coeffMask[ix] = (psMaskType ***)psAlloc((nY + 1) * sizeof(psMaskType **));
     1090            for (int iy = 0; iy < (nY + 1); iy++) {
     1091                poly->coeff[ix][iy] = psAlloc((nZ + 1) * sizeof(psF64 *));
     1092                poly->coeffErr[ix][iy] = psAlloc((nZ + 1) * sizeof(psF64 *));
     1093                poly->coeffMask[ix][iy] = (psMaskType **)psAlloc((nZ + 1) * sizeof(psMaskType *));
     1094                for (int iz = 0; iz < (nZ + 1); iz++) {
     1095                    poly->coeff[ix][iy][iz] = psAlloc((nT + 1) * sizeof(psF64));
     1096                    poly->coeffErr[ix][iy][iz] = psAlloc((nT + 1) * sizeof(psF64));
     1097                    poly->coeffMask[ix][iy][iz] = (psMaskType *)psAlloc((nT + 1) * sizeof(psMaskType));
     1098                }
     1099            }
     1100        }
     1101    }
     1102    for (int ix = 0; ix < (1 + nX); ix++) {
     1103        for (int iy = 0; iy < (1 + nY); iy++) {
     1104            for (int iz = 0; iz < (1 + nZ); iz++) {
     1105                for (int it = 0; it < (1 + nT); it++) {
     1106                    poly->coeff[ix][iy][iz][it]     = 0.0;
     1107                    poly->coeffErr[ix][iy][iz][it]  = 0.0;
     1108                    poly->coeffMask[ix][iy][iz][it] = PS_POLY_MASK_NONE;
     1109                }
     1110            }
     1111        }
     1112    }
     1113    return(true);
     1114}
     1115
     1116// ######## Copy polynomials ########
    10151117psPolynomial1D *psPolynomial1DCopy(psPolynomial1D *out,
    1016                                    psPolynomial1D *poly)
     1118                                   psPolynomial1D *poly)
    10171119{
    10181120    if (out == NULL) {
    1019         out = psPolynomial1DAlloc (poly->type, poly->nX);
     1121        out = psPolynomial1DAlloc (poly->type, poly->nX);
    10201122    } else {
    1021         psPolynomial1DRecycle (out, poly->type, poly->nX);
     1123        psPolynomial1DRecycle (out, poly->type, poly->nX);
    10221124    }
    10231125
     
    10301132}
    10311133psPolynomial2D *psPolynomial2DCopy(psPolynomial2D *out,
    1032                                    psPolynomial2D *poly)
     1134                                   psPolynomial2D *poly)
    10331135{
    10341136    if (out == NULL) {
    1035         out = psPolynomial2DAlloc (poly->type, poly->nX, poly->nY);
     1137        out = psPolynomial2DAlloc (poly->type, poly->nX, poly->nY);
    10361138    } else {
    1037         psPolynomial2DRecycle (out, poly->type, poly->nX, poly->nY);
     1139        psPolynomial2DRecycle (out, poly->type, poly->nX, poly->nY);
    10381140    }
    10391141
    10401142    for (int i = 0; i < (1 + poly->nX); i++) {
    1041         for (int j = 0; j < (1 + poly->nY); j++) {
    1042             out->coeff[i][j] = poly->coeff[i][j];
    1043             out->coeffErr[i][j] = poly->coeffErr[i][j];
    1044             out->coeffMask[i][j] = poly->coeffMask[i][j];
    1045         }
     1143        for (int j = 0; j < (1 + poly->nY); j++) {
     1144            out->coeff[i][j] = poly->coeff[i][j];
     1145            out->coeffErr[i][j] = poly->coeffErr[i][j];
     1146            out->coeffMask[i][j] = poly->coeffMask[i][j];
     1147        }
     1148    }
     1149    return(out);
     1150}
     1151psPolynomial3D *psPolynomial3DCopy(psPolynomial3D *out,
     1152                                   psPolynomial3D *poly)
     1153{
     1154    if (out == NULL) {
     1155        out = psPolynomial3DAlloc (poly->type, poly->nX, poly->nY, poly->nZ);
     1156    } else {
     1157        psPolynomial3DRecycle (out, poly->type, poly->nX, poly->nY, poly->nZ);
     1158    }
     1159
     1160    for (int ix = 0; ix < (1 + poly->nX); ix++) {
     1161        for (int iy = 0; iy < (1 + poly->nY); iy++) {
     1162            for (int iz = 0; iz < (1 + poly->nZ); iz++) {
     1163                out->coeff[ix][iy][iz] = poly->coeff[ix][iy][iz];
     1164                out->coeffErr[ix][iy][iz] = poly->coeffErr[ix][iy][iz];
     1165                out->coeffMask[ix][iy][iz] = poly->coeffMask[ix][iy][iz];
     1166            }
     1167        }
     1168    }
     1169    return(out);
     1170}
     1171psPolynomial4D *psPolynomial4DCopy(psPolynomial4D *out,
     1172                                   psPolynomial4D *poly)
     1173{
     1174    if (out == NULL) {
     1175        out = psPolynomial4DAlloc (poly->type, poly->nX, poly->nY, poly->nZ, poly->nT);
     1176    } else {
     1177        psPolynomial4DRecycle (out, poly->type, poly->nX, poly->nY, poly->nZ, poly->nT);
     1178    }
     1179
     1180    for (int ix = 0; ix < (1 + poly->nX); ix++) {
     1181        for (int iy = 0; iy < (1 + poly->nY); iy++) {
     1182            for (int iz = 0; iz < (1 + poly->nZ); iz++) {
     1183                for (int it = 0; it < (1 + poly->nT); it++) {
     1184                    out->coeff[ix][iy][iz][it] = poly->coeff[ix][iy][iz][it];
     1185                    out->coeffErr[ix][iy][iz][it] = poly->coeffErr[ix][iy][iz][it];
     1186                    out->coeffMask[ix][iy][iz][it] = poly->coeffMask[ix][iy][iz][it];
     1187                }
     1188            }
     1189        }
    10461190    }
    10471191    return(out);
     
    10801224
    10811225    switch (x->type.type) {
    1082     case PS_TYPE_F64:
     1226      case PS_TYPE_F64:
    10831227        tmp = psVectorAlloc(x->n, PS_TYPE_F64);
    10841228        for (unsigned int i=0;i<x->n;i++) {
     
    10861230        }
    10871231        break;
    1088     case PS_TYPE_F32:
     1232      case PS_TYPE_F32:
    10891233        tmp = psVectorAlloc(x->n, PS_TYPE_F32);
    10901234        for (unsigned int i=0;i<x->n;i++) {
     
    10921236        }
    10931237        break;
    1094     default:
     1238      default:
    10951239        psError(PS_ERR_UNKNOWN, false, "invalid input data type.\n");
    10961240        return (NULL);
  • branches/eam_branches/ipp-20230313/psLib/src/math/psPolynomial.h

    r42492 r42506  
    164164                           psPolynomialType type,
    165165                           unsigned int nX);
    166 
    167166bool psPolynomial2DRecycle(psPolynomial2D *poly,
    168167                           psPolynomialType type,
    169168                           unsigned int nX,
    170169                           unsigned int nY);
     170bool psPolynomial3DRecycle(psPolynomial3D *poly,
     171                           psPolynomialType type,
     172                           unsigned int nX,
     173                           unsigned int nY,
     174                           unsigned int nZ);
     175bool psPolynomial4DRecycle(psPolynomial4D *poly,
     176                           psPolynomialType type,
     177                           unsigned int nX,
     178                           unsigned int nY,
     179                           unsigned int nZ,
     180                           unsigned int nT);
    171181
    172182psPolynomial1D *psPolynomial1DCopy(psPolynomial1D *out,
    173183                                   psPolynomial1D *poly);
    174 
    175184psPolynomial2D *psPolynomial2DCopy(psPolynomial2D *out,
    176185                                   psPolynomial2D *poly);
     186psPolynomial3D *psPolynomial3DCopy(psPolynomial3D *out,
     187                                   psPolynomial3D *poly);
     188psPolynomial4D *psPolynomial4DCopy(psPolynomial4D *out,
     189                                   psPolynomial4D *poly);
    177190
    178191/** Evaluates a 1-D polynomial at specific coordinates.
  • branches/eam_branches/ipp-20230313/psLib/test/math

    • Property svn:ignore
      •  

        old new  
        5656tap_psMinimizeLMM_Alt
        5757tap_psMinimizeLMM_Trail
        58 tap_psPolyFit_IRLS
         58tap_psPolyFit_IRLS_1D
         59tap_psPolyFit_IRLS_2D
        5960tap_psPolynomialMD_sampleDark
        6061test-suite.log
Note: See TracChangeset for help on using the changeset viewer.