/* ******** airSinglePrintf ** ** a complete stand-in for {f|s}printf(), as long as the given format ** string contains exactly one conversion sequence. The utility of ** this is to standardize the printing of IEEE 754 special values: ** QNAN, SNAN -> "NaN" ** POS_INF -> "+inf" ** NEG_INF -> "-inf" ** The format string can contain other things besides just the ** conversion sequence: airSingleFprintf(f, " (%f)\n", AIR_NAN) ** will be the same as fprintf(f, " (%s)\n", "NaN"); ** ** To get fprintf behavior, pass "str" as NULL ** to get sprintf bahavior, pass "file" as NULL ** ** Someday I'll find/write a complete {f|s|}printf replacement ... */ int airSinglePrintf(FILE *file, char *str, const char *_fmt, ...) { char *fmt, buff[AIR_STRLEN_LARGE]; double val=0, gVal, fVal; int ret, isF, isD, cls; char *conv=NULL, *p0, *p1, *p2, *p3, *p4, *p5; va_list ap; va_start(ap, _fmt); fmt = airStrdup(_fmt); /* this is needlessly complicated; the "l" modifier is a no-op */ p0 = strstr(fmt, "%e"); p1 = strstr(fmt, "%f"); p2 = strstr(fmt, "%g"); p3 = strstr(fmt, "%le"); p4 = strstr(fmt, "%lf"); p5 = strstr(fmt, "%lg"); isF = p0 || p1 || p2; isD = p3 || p4 || p5; /* the code here says "isF" and "isD" as if it means "is float" or "is double". It really should be "is2" or "is3", as in, "is 2-character conversion sequence, or "is 3-character..." */ if (isF) { conv = p0 ? p0 : (p1 ? p1 : p2); } if (isD) { conv = p3 ? p3 : (p4 ? p4 : p5); } if (isF || isD) { /* use "double" instead of "float" because var args are _always_ subject to old-style C type promotions: float promotes to double */ val = va_arg(ap, double); cls = airFPClass_d(val); switch (cls) { case airFP_SNAN: case airFP_QNAN: case airFP_POS_INF: case airFP_NEG_INF: if (isF) { memcpy(conv, "%s", 2); } else { /* this sneakiness allows us to replace a 3-character conversion sequence for a double (such as %lg) with a 3-character conversion for a string, which we know has at most 4 characters */ memcpy(conv, "%4s", 3); } break; } #define PRINT(F, S, C, V) ((F) ? fprintf((F),(C),(V)) : sprintf((S),(C),(V))) switch (cls) { case airFP_SNAN: case airFP_QNAN: ret = PRINT(file, str, fmt, "NaN"); break; case airFP_POS_INF: ret = PRINT(file, str, fmt, "+inf"); break; case airFP_NEG_INF: ret = PRINT(file, str, fmt, "-inf"); break; default: if (p2 || p5) { /* got "%g" or "%lg", see if it would be better to use "%f" */ sprintf(buff, "%f", val); sscanf(buff, "%lf", &fVal); sprintf(buff, "%g", val); sscanf(buff, "%lf", &gVal); if (fVal != gVal) { /* using %g (or %lg) lost precision!! Use %f (or %lf) instead */ if (p2) { memcpy(conv, "%f", 2); } else { memcpy(conv, "%lf", 3); } } } ret = PRINT(file, str, fmt, val); break; } } else {
/* ******** nrrdApply1DIrregMap() ** ** Linear interpolation between irregularly spaced control points. ** Obviously, the location of the control point has to be given ** explicitly. The map nrrd must have dimension 2, and each ** control point is represented by a scanline along axis 0. The ** first value is the position of the control point, and the remaining ** value(s) are linearly weighted according to the position of the ** input value among the control point locations. ** ** To allow "coloring" of non-existant values -inf, NaN, and +inf, if ** the very first value of the map (the location of the first control ** point) is non-existant, then the first three control point locations ** must be -inf, NaN, and +inf, in that order, and the information ** about these points will be used for corresponding input values. ** Doing this makes everything slower, however, because airFPClass_f() ** is called on every single value. ** ** This assumes that nrrd1DIrregMapCheck has been called on "nmap", ** and that nrrd1DIrregAclCheck has been called on "nacl" (if it is ** non-NULL). */ int nrrdApply1DIrregMap(Nrrd *nout, const Nrrd *nin, const NrrdRange *_range, const Nrrd *nmap, const Nrrd *nacl, int typeOut, int rescale) { char me[]="nrrdApply1DIrregMap", err[BIFF_STRLEN]; size_t N, I; int i, *acl, entLen, posLen, aclLen, mapIdx, aclIdx, entSize, colSize, inSize, lo, hi, baseI; double val, *pos, domMin, domMax, mapIdxFrac, (*mapLup)(const void *v, size_t I), (*inLoad)(const void *v), (*outInsert)(void *v, size_t I, double d); char *inData, *outData, *entData0, *entData1; NrrdRange *range; airArray *mop; if (!(nout && nmap && nin)) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } mop = airMopNew(); if (_range) { range = nrrdRangeCopy(_range); nrrdRangeSafeSet(range, nin, nrrdBlind8BitRangeState); } else { range = nrrdRangeNewSet(nin, nrrdBlind8BitRangeState); } airMopAdd(mop, range, (airMopper)nrrdRangeNix, airMopAlways); if (_nrrdApply1DSetUp(nout, nin, range, nmap, kindImap, typeOut, rescale, AIR_FALSE)) { sprintf(err, "%s:", me); biffAdd(NRRD, err); airMopError(mop); return 1; } if (nacl && nrrd1DIrregAclCheck(nacl)) { sprintf(err, "%s: given acl isn't valid", me); biffAdd(NRRD, err); airMopError(mop); return 1; } if (nacl) { acl = (int *)nacl->data; aclLen = nacl->axis[1].size; } else { acl = NULL; aclLen = 0; } pos = _nrrd1DIrregMapDomain(&posLen, &baseI, nmap); if (!pos) { sprintf(err, "%s: couldn't determine domain", me); biffAdd(NRRD, err); airMopError(mop); return 1; } airMopAdd(mop, pos, airFree, airMopAlways); mapLup = nrrdDLookup[nmap->type]; inData = (char *)nin->data; inLoad = nrrdDLoad[nin->type]; inSize = nrrdElementSize(nin); mapLup = nrrdDLookup[nmap->type]; entLen = nmap->axis[0].size; /* entLen is really 1 + entry length */ entSize = entLen*nrrdElementSize(nmap); colSize = (entLen-1)*nrrdTypeSize[typeOut]; outData = (char *)nout->data; outInsert = nrrdDInsert[nout->type]; domMin = pos[0]; domMax = pos[posLen-1]; N = nrrdElementNumber(nin); for (I=0; I<N; I++, inData += inSize, outData += colSize) { val = inLoad(inData); /* _VV = ( (AIR_EXISTS(val) && (21 == (int)(-val))) || 22400 < I ); */ /* if (_VV) fprintf(stderr, "##%s: (%d) val = % 31.15f\n", me, (int)I, val); */ if (!AIR_EXISTS(val)) { /* got a non-existant value */ if (baseI) { /* and we know how to deal with them */ switch (airFPClass_d(val)) { case airFP_NEG_INF: mapIdx = 0; break; case airFP_SNAN: case airFP_QNAN: mapIdx = 1; break; case airFP_POS_INF: mapIdx = 2; break; default: mapIdx = 0; fprintf(stderr, "%s: PANIC: non-existant value/class %g/%d " "not handled\n", me, val, airFPClass_d(val)); exit(1); } entData0 = (char*)(nmap->data) + mapIdx*entSize; for (i=1; i<entLen; i++) { outInsert(outData, i-1, mapLup(entData0, i)); } continue; /* we're done! (with this value) */ } else { /* we don't know how to properly deal with this non-existant value: we use the first entry, and then fall through to code below */ mapIdx = 0; mapIdxFrac = 0.0; } } else { /* we have an existant value */ if (rescale) { val = AIR_AFFINE(range->min, val, range->max, domMin, domMax); /* if (_VV) fprintf(stderr, " rescaled --> % 31.15f\n", val); */ } val = AIR_CLAMP(domMin, val, domMax); if (acl) { aclIdx = airIndex(domMin, val, domMax, aclLen); lo = acl[0 + 2*aclIdx]; hi = acl[1 + 2*aclIdx]; } else { lo = 0; hi = posLen-2; } if (lo < hi) { mapIdx = _nrrd1DIrregFindInterval(pos, val, lo, hi); } else { /* acl did its job ==> lo == hi */ mapIdx = lo; } } mapIdxFrac = AIR_AFFINE(pos[mapIdx], val, pos[mapIdx+1], 0.0, 1.0); /* if (_VV) fprintf(stderr, "##%s: val=\n% 31.15f --> " "mapIdx,frac = %d,\n% 31.15f\n", me, val, mapIdx, mapIdxFrac); */ entData0 = (char*)(nmap->data) + (baseI+mapIdx)*entSize; entData1 = (char*)(nmap->data) + (baseI+mapIdx+1)*entSize; /* if (_VV) fprintf(stderr, "##%s: 2; %d/\n% 31.15f --> entLen=%d " "baseI=%d -->\n", me, mapIdx, mapIdxFrac, entLen, baseI); */ for (i=1; i<entLen; i++) { val = ((1-mapIdxFrac)*mapLup(entData0, i) + mapIdxFrac*mapLup(entData1, i)); /* if (_VV) fprintf(stderr, "% 31.15f\n", val); */ outInsert(outData, i-1, val); } /* if (_VV) fprintf(stderr, "##%s: 3\n", me); */ } airMopOkay(mop); return 0; }
/* ******** airSanity() ** ** Does run-time checks to see if the compile-time constants are correct. ** Returns a value from the airInsane* enum; airInsane_not means all ** the checks came back without detecting any problems. */ int airSanity(void) { double nanValue, pinf, ninf; float nanF, pinfF, ninfF; unsigned int sign, expvalue, mant; int tmpI; char endian; unsigned char uc0, uc1; static int _airSanity=0; if (_airSanity) { return airInsane_not; } /* now that there is no more compile-time endian info, this is merely double checking that airMyEndian() works, and returns the constants (either 1234, pronounced "little endian", or 4321, "big endian") that are defined in air.h */ tmpI = 1; endian = !(*((char*)(&tmpI))); if (endian) { /* big endian */ if (4321 != airMyEndian()) { return airInsane_endian; } } else { if (1234 != airMyEndian()) { return airInsane_endian; } } /* checks on sizes of uchar, float, int, double, airLLong */ uc0 = 255; uc1 = AIR_CAST(unsigned char, AIR_INT(uc0) + 1); /* want to overflow */ if (!( 255 == uc0 && 0 == uc1 )) { return airInsane_UCSize; } /* these justify the AIR_EXISTS_F and AIR_EXISTS_D macros */ if (!( (sizeof(float) == sizeof(int)) && (4 == sizeof(int)) )) { return airInsane_FISize; } if (!( (sizeof(double) == sizeof(airLLong)) && (8 == sizeof(airLLong)) )) { return airInsane_DLSize; } /* run-time NaN checks */ pinf = DBL_MAX; pinf = _airSanityHelper(pinf); pinf = _airSanityHelper(pinf); pinf = _airSanityHelper(pinf); if (AIR_EXISTS(pinf)) { return airInsane_pInfExists; } ninf = -pinf; if (AIR_EXISTS(ninf)) { return airInsane_nInfExists; } nanValue = pinf / pinf; if (AIR_EXISTS(nanValue)) { return airInsane_NaNExists; } nanF = (float)nanValue; pinfF = (float)pinf; ninfF = (float)ninf; airFPValToParts_f(&sign, &expvalue, &mant, nanF); mant >>= 22; if (AIR_QNANHIBIT != (int)mant) { return airInsane_QNaNHiBit; } if (!( airFP_QNAN == airFPClass_f(AIR_NAN) && airFP_QNAN == airFPClass_f(AIR_QNAN) /* As of July 4 2012 GLK decides that the signalling NaN tests are more trouble than they're worth: the signal-ness of the NaN is not preserved in double-float conversion for some platforms (so airFP_SNAN == airFPClass_d(AIR_SNAN) has never been enforced), and there are more platforms for which (apparently) passing AIR_SNAN to airFPClass_d changes it to a quiet NaN, which defeats the purpose of the test. To summarize, given that: ** AIR_NAN and AIR_QNAN are checked here to be quiet NaN, after casting to both float and double, ** quiet NaN "hi bit" is tested above, and that ** quiet and signalling NaN are mutually exclusive, skipping the signalling NaN tests is unlikely to undermine knowing the correctness of the compile-time representation of NaNs. So the following line is now commented out for all platforms. */ /* && airFP_SNAN == airFPClass_f(AIR_SNAN) */ && airFP_QNAN == airFPClass_d(AIR_NAN) && airFP_QNAN == airFPClass_d(AIR_QNAN) )) { return airInsane_AIR_NAN; } if (!(airFP_QNAN == airFPClass_f(nanF) && airFP_POS_INF == airFPClass_f(pinfF) && airFP_NEG_INF == airFPClass_f(ninfF))) { /* really, this is verifying that assigning from a double to a float maintains the FPClass for non-existent values */ return airInsane_FltDblFPClass; } /* just make sure AIR_DIO is reasonably set (actually, this should be done by include/teemDio.h) */ switch (AIR_DIO) { case 0: break; case 1: break; default: return airInsane_dio; } _airSanity = 1; return airInsane_not; }
/* ******** nrrd1DIrregMapCheck() ** ** return zero only for the valid forms of 1D irregular map. ** imap must be 2D, both sizes >= 2, non-block-type, no non-existant ** values in range. If the first point's position is non-existant, ** than the first three points positions must be -inf, NaN, and +inf, ** and none of the other points locations can be non-existant, and ** they must increase monotonically. There must be at least two ** points with existant positions. */ int nrrd1DIrregMapCheck(const Nrrd *nmap) { char me[]="nrrd1DIrregMapCheck", err[BIFF_STRLEN]; double (*mapLup)(const void *v, size_t I); int i, entLen, mapLen, baseI; size_t min[2], max[2]; Nrrd *nrange; if (!nmap) { sprintf(err, "%s: got NULL pointer", me); biffAdd(NRRD, err); return 1; } if (nrrdCheck(nmap)) { sprintf(err, "%s: ", me); biffAdd(NRRD, err); return 1; } if (nrrdTypeBlock == nmap->type) { sprintf(err, "%s: map is %s type, need scalar", me, airEnumStr(nrrdType, nrrdTypeBlock)); biffAdd(NRRD, err); return 1; } if (2 != nmap->dim) { sprintf(err, "%s: map needs to have dimension 2, not %d", me, nmap->dim); biffAdd(NRRD, err); return 1; } entLen = nmap->axis[0].size; mapLen = nmap->axis[1].size; if (!( entLen >= 2 && mapLen >= 2 )) { sprintf(err, "%s: both map's axes sizes should be >= 2 (not %d,%d)", me, entLen, mapLen); biffAdd(NRRD, err); return 1; } min[0] = 1; max[0] = nmap->axis[0].size-1; min[1] = 0; max[1] = nmap->axis[1].size-1; if (nrrdCrop(nrange=nrrdNew(), nmap, min, max)) { sprintf(err, "%s: couldn't crop to isolate range of map", me); biffAdd(NRRD, err); nrrdNuke(nrange); return 1; } if (nrrdHasNonExist(nrange)) { sprintf(err, "%s: map has non-existent values in its range", me); biffAdd(NRRD, err); nrrdNuke(nrange); return 1; } nrrdNuke(nrange); mapLup = nrrdDLookup[nmap->type]; if (AIR_EXISTS(mapLup(nmap->data, 0))) { baseI = 0; } else { baseI = 3; if (!( mapLen >= 5 )) { sprintf(err, "%s: length of map w/ non-existant locations must " "be >= 5 (not %d)", me, mapLen); biffAdd(NRRD, err); return 1; } if (!( airFP_NEG_INF == airFPClass_d(mapLup(nmap->data, 0*entLen)) && airFP_QNAN == airFPClass_d(mapLup(nmap->data, 1*entLen)) && airFP_POS_INF == airFPClass_d(mapLup(nmap->data, 2*entLen)) )) { sprintf(err, "%s: 1st entry's position non-existant, but position " "of 1st three entries not -inf, NaN, and +inf", me); biffAdd(NRRD, err); return 1; } } for (i=baseI; i<mapLen; i++) { if (!AIR_EXISTS(mapLup(nmap->data, i*entLen))) { sprintf(err, "%s: entry %d has non-existant position", me, i); biffAdd(NRRD, err); return 1; } } for (i=baseI; i<mapLen-1; i++) { if (!( mapLup(nmap->data, i*entLen) < mapLup(nmap->data, (i+1)*entLen) )) { sprintf(err, "%s: map entry %d pos (%g) not < entry %d pos (%g)", me, i, mapLup(nmap->data, i*entLen), i+1, mapLup(nmap->data, (i+1)*entLen)); biffAdd(NRRD, err); return 1; } } return 0; }