Logo Search packages:      
Sourcecode: udunits version File versions  Download package

converter.c

/*
 * Copyright 2008, 2009 University Corporation for Atmospheric Research
 *
 * This file is part of the UDUNITS-2 package.  See the file LICENSE
 * in the top-level source-directory of the package for copying and
 * redistribution conditions.
 */
/*
 * Value converters for the udunits(3) library.
 */

/*LINTLIBRARY*/

#ifndef     _XOPEN_SOURCE
#   define _XOPEN_SOURCE 500
#endif

#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "converter.h"        /* this module's API */

00026 typedef struct {
    cv_converter* (*clone)(cv_converter*);
    double        (*convertDouble)
      (const cv_converter*, double);
    float*        (*convertFloats)
      (const cv_converter*, const float*, size_t, float*);
    double*       (*convertDoubles)
      (const cv_converter*, const double*, size_t, double*);
    int                 (*getExpression)
      (const cv_converter*, char* buf, size_t, const char*);
    void          (*free)(cv_converter*);
} ConverterOps;

00039 typedef struct {
    ConverterOps* ops;
} ReciprocalConverter;

00043 typedef struct {
    ConverterOps* ops;
    double        value;
} ScaleConverter;

00048 typedef struct {
    ConverterOps* ops;
    double        value;
} OffsetConverter;

00053 typedef struct {
    ConverterOps* ops;
    double        slope;
    double        intercept;
} GalileanConverter;

00059 typedef struct {
    ConverterOps* ops;
    double        logE;
} LogConverter;

00064 typedef struct {
    ConverterOps* ops;
    double        base;
} ExpConverter;

00069 typedef struct {
    ConverterOps* ops;
    cv_converter* first;
    cv_converter* second;
} CompositeConverter;

00075 union cv_converter {
    ConverterOps* ops;
    ScaleConverter      scale;
    OffsetConverter     offset;
    GalileanConverter   galilean;
    LogConverter  log;
    ExpConverter  exp;
    CompositeConverter  composite;
};

#define CV_CLONE(conv)        ((conv)->ops->clone(conv))

#define IS_TRIVIAL(conv)      ((conv)->ops == &trivialOps)
#define IS_RECIPROCAL(conv)   ((conv)->ops == &reciprocalOps)
#define IS_SCALE(conv)        ((conv)->ops == &scaleOps)
#define IS_OFFSET(conv)       ((conv)->ops == &offsetOps)
#define IS_GALILEAN(conv)     ((conv)->ops == &galileanOps)
#define IS_LOG(conv)          ((conv)->ops == &logOps)


static void
nonFree(
    cv_converter* const conv)
{
}


static void
cvSimpleFree(
    cv_converter* const conv)
{
    free(conv);
}


static int
cvNeedsParentheses(
    const char* const   string)
{
    return strpbrk(string, " \t") != NULL &&
      (string[0] != '(' || string[strlen(string)-1] != ')');
}


/*******************************************************************************
 * Trivial Converter:
 ******************************************************************************/

static cv_converter*
trivialClone(
    cv_converter* const conv)
{
    return cv_get_trivial();
}


static double
trivialConvertDouble(
    const cv_converter* const conv,
    const double        value)
{
    return value;
}


static float*
trivialConvertFloats(
    const cv_converter* const conv,
    const float* const        in,
    const size_t        count,
    float*              out)
{
    if (in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      (void)memmove(out, in, count*sizeof(float));
    }

    return out;
}


static double*
trivialConvertDoubles(
    const cv_converter* const conv,
    const double* const       in,
    const size_t        count,
    double*                   out)
{
    if (in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      (void)memmove(out, in, count*sizeof(double));
    }

    return out;
}


static int
trivialGetExpression(
    const cv_converter* const conv,
    char* const               buf,
    const size_t        max,
    const char* const         variable)
{
    return snprintf(buf, max, "%s", variable);
}


static ConverterOps     trivialOps = {
    trivialClone,
    trivialConvertDouble,
    trivialConvertFloats,
    trivialConvertDoubles,
    trivialGetExpression,
    nonFree};

static cv_converter     trivialConverter = {&trivialOps};


/*******************************************************************************
 * Reciprocal Converter:
 ******************************************************************************/

static cv_converter*
reciprocalClone(
    cv_converter* const conv)
{
    return cv_get_inverse();
}

static double
reciprocalConvertDouble(
    const cv_converter* const conv,
    const double        value)
{
    return 1.0 / value;
}


static float*
reciprocalConvertFloats(
    const cv_converter* const conv,
    const float* const        in,
    const size_t        count,
    float*              out)
{
    if (in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = (float)(1.0f / in[i]);
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = (float)(1.0f / in[i]);
      }
    }

    return out;
}


static double*
reciprocalConvertDoubles(
    const cv_converter* const conv,
    const double* const       in,
    const size_t        count,
    double*                   out)
{
    if (in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = 1.0 / in[i];
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = 1.0 / in[i];
      }
    }

    return out;
}


static int
reciprocalGetExpression(
    const cv_converter* const conv,
    char* const               buf,
    const size_t        max,
    const char* const         variable)
{
    return
      cvNeedsParentheses(variable)
      ? snprintf(buf, max, "1/(%s)", variable)
      : snprintf(buf, max, "1/%s", variable);
}


static ConverterOps     reciprocalOps = {
    reciprocalClone,
    reciprocalConvertDouble,
    reciprocalConvertFloats,
    reciprocalConvertDoubles,
    reciprocalGetExpression,
    nonFree};

static cv_converter     reciprocalConverter = {&reciprocalOps};


/*******************************************************************************
 * Scale Converter:
 ******************************************************************************/

static cv_converter*
scaleClone(
    cv_converter* const conv)
{
    return cv_get_scale(conv->scale.value);
}


static double
scaleConvertDouble(
    const cv_converter* const conv,
    const double        value)
{
    return conv->scale.value * value;
}


static float*
scaleConvertFloats(
    const cv_converter* const conv,
    const float* const        in,
    const size_t        count,
    float*              out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = (float)(conv->scale.value * in[i]);
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = (float)(conv->scale.value * in[i]);
      }
    }

    return out;
}


static double*
scaleConvertDoubles(
    const cv_converter* const conv,
    const double* const       in,
    const size_t        count,
    double*                   out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = conv->scale.value * in[i];
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = conv->scale.value * in[i];
      }
    }

    return out;
}


static int
scaleGetExpression(
    const cv_converter* const conv,
    char* const               buf,
    const size_t        max,
    const char* const         variable)
{
    return
      cvNeedsParentheses(variable)
      ? snprintf(buf, max, "%g*(%s)", conv->scale.value, variable)
      : snprintf(buf, max, "%g*%s", conv->scale.value, variable);
}


static ConverterOps     scaleOps = {
    scaleClone,
    scaleConvertDouble,
    scaleConvertFloats,
    scaleConvertDoubles,
    scaleGetExpression,
    cvSimpleFree};


/*******************************************************************************
 * Offset Converter:
 ******************************************************************************/

static cv_converter*
offsetClone(
    cv_converter* const conv)
{
    return cv_get_offset(conv->offset.value);
}


static double
offsetConvertDouble(
    const cv_converter* const conv,
    const double        value)
{
    return conv->offset.value + value;
}


static float*
offsetConvertFloats(
    const cv_converter* const conv,
    const float* const        in,
    const size_t        count,
    float*              out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = (float)(conv->offset.value + in[i]);
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = (float)(conv->offset.value + in[i]);
      }
    }

    return out;
}


static double*
offsetConvertDoubles(
    const cv_converter* const conv,
    const double* const       in,
    const size_t        count,
    double*                   out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = conv->offset.value + in[i];
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = conv->offset.value + in[i];
      }
    }

    return out;
}


static int
offsetGetExpression(
    const cv_converter* const conv,
    char* const               buf,
    const size_t        max,
    const char* const         variable)
{
    const int     oper = conv->offset.value < 0 ? '-' : '+';

    return
      cvNeedsParentheses(variable)
          ? snprintf(buf, max, "(%s) %c %g", variable, oper, 
            fabs(conv->offset.value))
          : snprintf(buf, max, "%s %c %g", variable, oper, 
            fabs(conv->offset.value));
}


static ConverterOps     offsetOps = {
    offsetClone,
    offsetConvertDouble,
    offsetConvertFloats,
    offsetConvertDoubles,
    offsetGetExpression,
    cvSimpleFree};


/*******************************************************************************
 * Galilean Converter:
 ******************************************************************************/

static cv_converter*
cvGalileanClone(
    cv_converter* const conv)
{
    return cv_get_galilean(conv->galilean.slope, conv->galilean.intercept);
}


static double
galileanConvertDouble(
    const cv_converter* const conv,
    const double        value)
{
    return conv->galilean.slope * value + conv->galilean.intercept;
}


static float*
galileanConvertFloats(
    const cv_converter* const conv,
    const float* const        in,
    const size_t        count,
    float*              out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = (float)(conv->galilean.slope * in[i] +
                conv->galilean.intercept);
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = (float)(conv->galilean.slope * in[i] +
                conv->galilean.intercept);
      }
    }

    return out;
}


static double*
galileanConvertDoubles(
    const cv_converter* const conv,
    const double* const       in,
    const size_t        count,
    double*                   out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = conv->galilean.slope * in[i] + 
                conv->galilean.intercept;
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = conv->galilean.slope * in[i] + 
                conv->galilean.intercept;
      }
    }

    return out;
}


static int
galileanGetExpression(
    const cv_converter* const conv,
    char* const               buf,
    const size_t        max,
    const char* const         variable)
{
    const int     oper = conv->galilean.intercept < 0 ? '-' : '+';

    return
      cvNeedsParentheses(variable)
          ? snprintf(buf, max, "%g*(%s) %c %g", conv->galilean.slope,
            variable, oper, fabs(conv->galilean.intercept))
          : snprintf(buf, max, "%g*%s %c %g", conv->galilean.slope, variable,
            oper, fabs(conv->galilean.intercept));
}


static ConverterOps     galileanOps = {
    cvGalileanClone,
    galileanConvertDouble,
    galileanConvertFloats,
    galileanConvertDoubles,
    galileanGetExpression,
    cvSimpleFree};


/*******************************************************************************
 * Logarithmic Converter:
 ******************************************************************************/

static cv_converter*
cvLogClone(
    cv_converter* const conv)
{
    return
        cv_get_log(
            conv->log.logE == M_LOG2E
                ? 2
                : conv->log.logE == 1
                    ? M_E
                    : conv->log.logE == M_LOG10E
                        ? 10
                        : exp(conv->log.logE));
}


static double
logConvertDouble(
    const cv_converter* const conv,
    const double        value)
{
    return log(value) * conv->log.logE;
}


static float*
logConvertFloats(
    const cv_converter* const conv,
    const float* const        in,
    const size_t        count,
    float*              out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = (float)(log(in[i]) * conv->log.logE);
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = (float)(log(in[i]) * conv->log.logE);
      }
    }

    return out;
}


static double*
logConvertDoubles(
    const cv_converter* const conv,
    const double* const       in,
    const size_t        count,
    double*                   out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = log(in[i]) * conv->log.logE;
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = log(in[i]) * conv->log.logE;
      }
    }

    return out;
}


static int
logGetExpression(
    const cv_converter* const conv,
    char* const               buf,
    const size_t        max,
    const char* const         variable)
{
    return 
        conv->log.logE == M_LOG2E
            ? snprintf(buf, max, "lb(%s)", variable)
            : conv->log.logE == 1
                ? snprintf(buf, max, "ln(%s)", variable)
                : conv->log.logE == M_LOG10E
                    ? snprintf(buf, max, "lg(%s)", variable)
                    : snprintf(buf, max, "%g*ln(%s)", conv->log.logE, variable);
}


static ConverterOps     logOps = {
    cvLogClone,
    logConvertDouble,
    logConvertFloats,
    logConvertDoubles,
    logGetExpression,
    cvSimpleFree};


/*******************************************************************************
 * Exponential Converter:
 ******************************************************************************/

static cv_converter*
expClone(
    cv_converter* const conv)
{
    return cv_get_pow(conv->exp.base);
}


static double
expConvertDouble(
    const cv_converter* const conv,
    const double        value)
{
    return pow(conv->exp.base, value);
}

static float*
expConvertFloats(
    const cv_converter* const conv,
    const float* const        in,
    const size_t        count,
    float*              out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = (float)(pow(conv->exp.base, in[i]));
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = (float)(pow(conv->exp.base, in[i]));
      }
    }

    return out;
}


static double*
expConvertDoubles(
    const cv_converter* const conv,
    const double* const       in,
    const size_t        count,
    double*                   out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      size_t      i;

      if (in < out) {
          for (i = count; i-- > 0;)
            out[i] = pow(conv->exp.base, in[i]);
      }
      else {
          for (i = 0; i < count; i++)
            out[i] = pow(conv->exp.base, in[i]);
      }
    }

    return out;
}


static int
expGetExpression(
    const cv_converter* const conv,
    char* const               buf,
    const size_t        max,
    const char* const         variable)
{
    return 
      cvNeedsParentheses(variable)
          ? snprintf(buf, max, "pow(%g, (%s))", conv->exp.base, variable)
          : snprintf(buf, max, "pow(%g, %s)", conv->exp.base, variable);
}


static ConverterOps     expOps = {
    expClone,
    expConvertDouble,
    expConvertFloats,
    expConvertDoubles,
    expGetExpression,
    cvSimpleFree};


/*******************************************************************************
 * Composite Converter:
 ******************************************************************************/

static cv_converter*
compositeClone(
    cv_converter* const conv)
{
    return cv_combine(conv->composite.first, conv->composite.second);
}


static double
compositeConvertDouble(
    const cv_converter* const conv,
    const double        value)
{
    return
      cv_convert_double(conv->composite.second,
          cv_convert_double(((CompositeConverter*)conv)->first, value));
}


static float*
compositeConvertFloats(
    const cv_converter* const conv,
    const float* const        in,
    const size_t        count,
    float*              out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      out =
          cv_convert_floats(
            conv->composite.second,
            cv_convert_floats(conv->composite.first, in, count, out),
            count,
            out);
    }

    return out;
}


static double*
compositeConvertDoubles(
    const cv_converter* const conv,
    const double* const       in,
    const size_t        count,
    double*                   out)
{
    if (conv == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      out = 
          cv_convert_doubles(
            conv->composite.second,
            cv_convert_doubles(conv->composite.first, in, count, out),
            count,
            out);
    }

    return out;
}


static void
compositeFree(
    cv_converter* const conv)
{
    cv_free(conv->composite.first);
    cv_free(conv->composite.second);
    free(conv);
}


static int
compositeGetExpression(
    const cv_converter* const conv,
    char* const               buf,
    const size_t        max,
    const char* const         variable)
{
    char    tmpBuf[132];
    int           nchar = cv_get_expression(conv->composite.first, buf, max,
      variable);

    if (nchar >= 0) {
      buf[max-1] = 0;

      if (cvNeedsParentheses(buf)) {
          nchar = snprintf(tmpBuf, sizeof(tmpBuf), "(%s)", buf);
      }
      else {
          (void)strncpy(tmpBuf, buf, sizeof(tmpBuf));

          tmpBuf[sizeof(tmpBuf)-1] = 0;
      }

      nchar = cv_get_expression(conv->composite.second, buf, max, tmpBuf);
    }

    return nchar;
}


static ConverterOps     compositeOps = {
    compositeClone,
    compositeConvertDouble,
    compositeConvertFloats,
    compositeConvertDoubles,
    compositeGetExpression,
    compositeFree};


/*******************************************************************************
 * Public API:
 ******************************************************************************/

/*
 * Returns the trivial converter (i.e., y = x).
 * When finished with the converter, the client should pass the converter to
 * cv_free().
 *
 * Returns:
 *    The trivial converter.
 */
cv_converter*
cv_get_trivial()
{
    return &trivialConverter;
}


/*
 * Returns the reciprocal converter (i.e., y = 1/x).
 * When finished with the converter, the client should pass the converter to
 * cv_free().
 *
 * Returns:
 *    The reciprocal converter.
 */
cv_converter*
cv_get_inverse()
{
    return &reciprocalConverter;
}


/*
 * Returns a converter that multiplies values by a number (i.e., y = ax).
 * When finished with the converter, the client should pass the converter to
 * cv_free().
 *
 * Arguments:
 *    slope The number by which to multiply values.
 * Returns:
 *    NULL  Necessary memory couldn't be allocated.
 *    else  A converter that will multiply values by the given number.
 */
cv_converter*
cv_get_scale(
    const double  slope)
{
    cv_converter* conv;

    if (slope == 1) {
      conv = &trivialConverter;
    }
    else {
      conv = malloc(sizeof(ScaleConverter));

      if (conv != NULL) {
          conv->ops = &scaleOps;
          conv->scale.value = slope;
      }
    }

    return conv;
}


/*
 * Returns a converter that adds a number to values (i.e., y = x + b).
 * When finished with the converter, the client should pass the converter to
 * cv_free().
 *
 * Arguments:
 *    offset      The number to be added.
 * Returns:
 *    NULL  Necessary memory couldn't be allocated.
 *    else  A converter that adds the given number to values.
 */
cv_converter*
cv_get_offset(
    const double  offset)
{
    cv_converter* conv;

    if (offset == 0) {
      conv = &trivialConverter;
    }
    else {
      conv = malloc(sizeof(OffsetConverter));

      if (conv != NULL) {
          conv->ops = &offsetOps;
          conv->offset.value = offset;
      }
    }

    return conv;
}


/*
 * Returns a Galilean converter (i.e., y = ax + b).
 * When finished with the converter, the client should pass the converter to
 * cv_free().
 *
 * Arguments:
 *    slope The number by which to multiply values.
 *    offset      The number to be added.
 * Returns:
 *    NULL  Necessary memory couldn't be allocated.
 *    else  A Galilean converter corresponding to the inputs.
 */
cv_converter*
cv_get_galilean(
    const double  slope,
    const double  intercept)
{
    cv_converter* conv;

    if (slope == 1) {
      conv = cv_get_offset(intercept);
    }
    else if (intercept == 0) {
      conv = cv_get_scale(slope);
    }
    else {
      cv_converter*     tmp = malloc(sizeof(GalileanConverter));

      if (tmp != NULL) {
          tmp->ops = &galileanOps;
          tmp->galilean.slope = slope;
          tmp->galilean.intercept = intercept;
          conv = tmp;
      }
    }

    return conv;
}


/*
 * Returns a logarithmic converter (i.e., y = log(x/x0) in some base).
 * When finished with the converter, the client should pass the converter to
 * cv_free().
 *
 * Arguments:
 *    base        The logarithmic base (e.g., 2, M_E, 10).  Must be
 *                      greater than one.
 * Returns:
 *    NULL        "base" is invalid or necessary memory couldn't be
 *                allocated.
 *    else        A logarithmic converter corresponding to the inputs.
 */
cv_converter*
cv_get_log(
    const double  base)
{
    cv_converter* conv;

    if (base <= 1) {
      conv = NULL;
    }
    else {
      conv = malloc(sizeof(LogConverter));

      if (conv != NULL) {
          conv->ops = &logOps;
          conv->log.logE = 
                base == 2
                    ? M_LOG2E
                    : base == M_E
                        ? 1
                        : base == 10
                            ? M_LOG10E
                            : 1/log(base);
      }
    }

    return conv;
}


/*
 * Returns an exponential converter (i.e., y = pow(b, x) in some base "b").
 * When finished with the converter, the client should pass the converter to
 * cv_free().
 *
 * Arguments:
 *    base        The desired base.  Must be positive.
 * Returns:
 *    NULL        "base" is invalid or necessary memory couldn't be
 *                allocated.
 *    else        An exponential converter corresponding to the inputs.
 */
cv_converter*
cv_get_pow(
    const double  base)
{
    cv_converter* conv;

    if (base <= 0) {
      conv = NULL;
    }
    else {
      conv = malloc(sizeof(ExpConverter));

      if (conv != NULL) {
          conv->ops = &expOps;
          conv->exp.base = base;
      }
    }

    return conv;
}


/*
 * Returns a converter corresponding to the sequential application of two
 * other converters.  The returned converter should be passed to cv_free() when
 * it is no longer needed.
 *
 * Arguments:
 *    first The converter to be applied first.  May be passed to cv_free()
 *          upon return.
 *    second      The converter to be applied second.  May be passed to cv_free()
 *          upon return.
 * Returns:
 *    NULL  Either "first" or "second" is NULL or necessary memory couldn't
 *          be allocated.
 *      else    A converter corresponding to the sequential application of the
 *              given converters.  If one of the input converters is the trivial
 *              converter, then the returned converter will be the other input
 *              converter.
 */
cv_converter*
cv_combine(
    cv_converter* const first,
    cv_converter* const second)
{
    cv_converter* conv;

    if (first == NULL || second == NULL) {
      conv = NULL;
    }
    else if (IS_TRIVIAL(first)) {
      conv = CV_CLONE(second);
    }
    else if (IS_TRIVIAL(second)) {
      conv = CV_CLONE(first);
    }
    else {
      conv = NULL;

      if (IS_RECIPROCAL(first)) {
          if (IS_RECIPROCAL(second)) {
            conv = cv_get_trivial();
          }
      }
      else if (IS_SCALE(first)) {
          if (IS_SCALE(second)) {
            conv = cv_get_scale(first->scale.value * second->scale.value);
          }
          else if (IS_OFFSET(second)) {
            conv = cv_get_galilean(first->scale.value, second->offset.value);
          }
          else if (IS_GALILEAN(second)) {
            conv = cv_get_galilean(
                first->scale.value * second->galilean.slope, 
                second->galilean.intercept);
          }
      }
      else if (IS_OFFSET(first)) {
          if (IS_SCALE(second)) {
            conv = cv_get_galilean(second->scale.value, 
                first->offset.value * second->scale.value);
          }
          else if (IS_OFFSET(second)) {
            conv = cv_get_offset(first->offset.value + second->offset.value);
          }
          else if (IS_GALILEAN(second)) {
            conv = cv_get_galilean(second->galilean.slope, 
                first->offset.value * second->galilean.slope +
                  second->galilean.intercept);
          }
      }
      else if (IS_GALILEAN(first)) {
          if (IS_SCALE(second)) {
            conv = cv_get_galilean(
                second->scale.value * first->galilean.slope,
                second->scale.value * first->galilean.intercept);
          }
          else if (IS_OFFSET(second)) {
            conv = cv_get_galilean(first->galilean.slope,
                first->galilean.intercept + second->offset.value);
          }
          else if (IS_GALILEAN(second)) {
            conv = cv_get_galilean(
                second->galilean.slope * first->galilean.slope,
                second->galilean.slope * first->galilean.intercept +
                  second->galilean.intercept);
          }
      }

      if (conv == NULL) {
          /*
           * General case: create a composite converter.
           */
          cv_converter* c1 = CV_CLONE(first);
            int                 error = 1;

            if (c1 != NULL) {
                cv_converter* c2 = CV_CLONE(second);

                if (c2 != NULL) {
                    conv = malloc(sizeof(CompositeConverter));

                    if (conv != NULL) {
                        conv->composite.ops = &compositeOps;
                        conv->composite.first = c1;
                        conv->composite.second = c2;
                        error = 0;
                    }                   /* "conv" allocated */

                    if (error)
                        cv_free(c2);
                }                       /* "c2" allocated */

                if (error)
                    cv_free(c1);
            }                           /* "c1" allocated */
      }                               /* "conv != NULL" */
    }                                   /* "first" & "second" not trivial */

    return conv;
}


/*
 * Frees resources associated with a converter.  Use of the converter argument
 * subsequent to this function may result in undefined behavior.
 *
 * Arguments:
 *    conv  The converter to have its resources freed or NULL.  The
 *          converter must have been returned by this module.
 */
void
cv_free(
    cv_converter* const conv)
{
    if (conv != NULL) {
      conv->ops->free((cv_converter*)conv);
    }
}


/*
 * Converts a float.
 *
 * Arguments:
 *    converter   Pointer to the converter.
 *    value       The value to be converted.
 * Returns:
 *    The converted value.
 */
float
cv_convert_float(
    const cv_converter* converter,
    const float         value)
{
    return (float)converter->ops->convertDouble(converter, value);
}


/*
 * Converts a double.
 *
 * Arguments:
 *    converter   Pointer to the converter.
 *    value       The value to be converted.
 * Returns:
 *    The converted value.
 */
double
cv_convert_double(
    const cv_converter* converter,
    const double  value)
{
    return converter->ops->convertDouble(converter, value);
}


/*
 * Converts an array of floats.
 *
 * Arguments:
 *    converter   Pointer to the converter.
 *    in          Pointer to the values to be converted.  The array may
 *                overlap "out".
 *    count       The number of values to be converted.
 *    out         Pointer to the output array for the converted values.
 *                The array may overlap "in".
 * Returns:
 *    NULL        "converter", "in", or "out" is NULL.
 *    else        Pointer to the output array, "out".
 */
float*
cv_convert_floats(
    const cv_converter* converter,
    const float* const  in,
    const size_t  count,
    float*        out)
{
    if (converter == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
        out = converter->ops->convertFloats(converter, in, count, out);
    }

    return out;
}


/*
 * Converts an array of doubles.
 *
 * Arguments:
 *    converter   Pointer to the converter.
 *    in          Pointer to the values to be converted.  The array may
 *                overlap "out".
 *    count       The number of values to be converted.
 *    out         Pointer to the output array for the converted values.
 *                The array may overlap "in".
 * Returns:
 *    NULL        "converter", "in", or "out" is NULL.
 *    else        Pointer to the output array, "out".
 */
double*
cv_convert_doubles(
    const cv_converter* converter,
    const double* const in,
    const size_t  count,
    double*       out)
{
    if (converter == NULL || in == NULL || out == NULL) {
      out = NULL;
    }
    else {
      out = converter->ops->convertDoubles(converter, in, count, out);
    }

    return out;
}


/*
 * Returns a string expression representation of a converter.
 *
 * Arguments:
 *    conv        The converter.
 *    buf         The buffer into which to write the expression.
 *    max         The size of the buffer.
 *    variable    The string to be used as the input value for the
 *                converter.
 * RETURNS
 *    <0    An error was encountered.
 *    else  The number of bytes formatted excluding the terminating null.
 */
int
cv_get_expression(
    const cv_converter* const conv,
    char* const               buf,
    size_t              max,
    const char* const         variable)
{
    return conv->ops->getExpression(conv, buf, max, variable);
}

Generated by  Doxygen 1.6.0   Back to index