/*
******** 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;
}
Exemple #3
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;
}