"Fossies" - the Fresh Open Source Software Archive

Member "udunits-2.2.28/lib/converter.c" (8 Dec 2020, 29063 Bytes) of package /linux/privat/udunits-2.2.28.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "converter.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.2.26_vs_2.2.28.

    1 /*
    2  * Copyright 2020 University Corporation for Atmospheric Research
    3  *
    4  * This file is part of the UDUNITS-2 package.  See the file COPYRIGHT
    5  * in the top-level source-directory of the package for copying and
    6  * redistribution conditions.
    7  */
    8 /*
    9  * Value converters for the udunits(3) library.
   10  */
   11 
   12 /*LINTLIBRARY*/
   13 
   14 #include "config.h"
   15 
   16 #include "udunits2.h" // Accommodates Windows & includes "converter.h"
   17 
   18 #include <math.h>
   19 #include <stddef.h>
   20 #include <stdio.h>
   21 #include <stdlib.h>
   22 #include <string.h>
   23 
   24 typedef struct {
   25     cv_converter*   (*clone)(cv_converter*);
   26     double      (*convertDouble)
   27     (const cv_converter*, double);
   28     float*      (*convertFloats)
   29     (const cv_converter*, const float*, size_t, float*);
   30     double*     (*convertDoubles)
   31     (const cv_converter*, const double*, size_t, double*);
   32     int         (*getExpression)
   33     (const cv_converter*, char* buf, size_t, const char*);
   34     void        (*free)(cv_converter*);
   35 } ConverterOps;
   36 
   37 typedef struct {
   38     ConverterOps*   ops;
   39 } ReciprocalConverter;
   40 
   41 typedef struct {
   42     ConverterOps*   ops;
   43     double      value;
   44 } ScaleConverter;
   45 
   46 typedef struct {
   47     ConverterOps*   ops;
   48     double      value;
   49 } OffsetConverter;
   50 
   51 typedef struct {
   52     ConverterOps*   ops;
   53     double      slope;
   54     double      intercept;
   55 } GalileanConverter;
   56 
   57 typedef struct {
   58     ConverterOps*   ops;
   59     double      logE;
   60 } LogConverter;
   61 
   62 typedef struct {
   63     ConverterOps*   ops;
   64     double      base;
   65 } ExpConverter;
   66 
   67 typedef struct {
   68     ConverterOps*   ops;
   69     cv_converter*   first;
   70     cv_converter*   second;
   71 } CompositeConverter;
   72 
   73 union cv_converter {
   74     ConverterOps*   ops;
   75     ScaleConverter  scale;
   76     OffsetConverter offset;
   77     GalileanConverter   galilean;
   78     LogConverter    log;
   79     ExpConverter    exp;
   80     CompositeConverter  composite;
   81 };
   82 
   83 #define CV_CLONE(conv)      ((conv)->ops->clone(conv))
   84 
   85 #define IS_TRIVIAL(conv)    ((conv)->ops == &trivialOps)
   86 #define IS_RECIPROCAL(conv) ((conv)->ops == &reciprocalOps)
   87 #define IS_SCALE(conv)      ((conv)->ops == &scaleOps)
   88 #define IS_OFFSET(conv)     ((conv)->ops == &offsetOps)
   89 #define IS_GALILEAN(conv)   ((conv)->ops == &galileanOps)
   90 #define IS_LOG(conv)        ((conv)->ops == &logOps)
   91 
   92 
   93 static void
   94 nonFree(
   95     cv_converter* const conv)
   96 {
   97 }
   98 
   99 
  100 static void
  101 cvSimpleFree(
  102     cv_converter* const conv)
  103 {
  104     free(conv);
  105 }
  106 
  107 
  108 static int
  109 cvNeedsParentheses(
  110     const char* const   string)
  111 {
  112     return strpbrk(string, " \t") != NULL &&
  113     (string[0] != '(' || string[strlen(string)-1] != ')');
  114 }
  115 
  116 
  117 /*******************************************************************************
  118  * Trivial Converter:
  119  ******************************************************************************/
  120 
  121 static cv_converter*
  122 trivialClone(
  123     cv_converter* const conv)
  124 {
  125     return cv_get_trivial();
  126 }
  127 
  128 
  129 static double
  130 trivialConvertDouble(
  131     const cv_converter* const   conv,
  132     const double        value)
  133 {
  134     return value;
  135 }
  136 
  137 
  138 static float*
  139 trivialConvertFloats(
  140     const cv_converter* const   conv,
  141     const float* const      in,
  142     const size_t        count,
  143     float*          out)
  144 {
  145     if (in == NULL || out == NULL) {
  146     out = NULL;
  147     }
  148     else {
  149     (void)memmove(out, in, count*sizeof(float));
  150     }
  151 
  152     return out;
  153 }
  154 
  155 
  156 static double*
  157 trivialConvertDoubles(
  158     const cv_converter* const   conv,
  159     const double* const     in,
  160     const size_t        count,
  161     double*             out)
  162 {
  163     if (in == NULL || out == NULL) {
  164     out = NULL;
  165     }
  166     else {
  167     (void)memmove(out, in, count*sizeof(double));
  168     }
  169 
  170     return out;
  171 }
  172 
  173 
  174 static int
  175 trivialGetExpression(
  176     const cv_converter* const   conv,
  177     char* const         buf,
  178     const size_t        max,
  179     const char* const       variable)
  180 {
  181     return snprintf(buf, max, "%s", variable);
  182 }
  183 
  184 
  185 static ConverterOps trivialOps = {
  186     trivialClone,
  187     trivialConvertDouble,
  188     trivialConvertFloats,
  189     trivialConvertDoubles,
  190     trivialGetExpression,
  191     nonFree};
  192 
  193 static cv_converter trivialConverter = {&trivialOps};
  194 
  195 
  196 /*******************************************************************************
  197  * Reciprocal Converter:
  198  ******************************************************************************/
  199 
  200 static cv_converter*
  201 reciprocalClone(
  202     cv_converter* const conv)
  203 {
  204     return cv_get_inverse();
  205 }
  206 
  207 static double
  208 reciprocalConvertDouble(
  209     const cv_converter* const   conv,
  210     const double        value)
  211 {
  212     return 1.0 / value;
  213 }
  214 
  215 
  216 static float*
  217 reciprocalConvertFloats(
  218     const cv_converter* const   conv,
  219     const float* const      in,
  220     const size_t        count,
  221     float*          out)
  222 {
  223     if (in == NULL || out == NULL) {
  224     out = NULL;
  225     }
  226     else {
  227     size_t  i;
  228 
  229     if (in < out) {
  230         for (i = count; i-- > 0;)
  231         out[i] = (float)(1.0f / in[i]);
  232     }
  233     else {
  234         for (i = 0; i < count; i++)
  235         out[i] = (float)(1.0f / in[i]);
  236     }
  237     }
  238 
  239     return out;
  240 }
  241 
  242 
  243 static double*
  244 reciprocalConvertDoubles(
  245     const cv_converter* const   conv,
  246     const double* const     in,
  247     const size_t        count,
  248     double*             out)
  249 {
  250     if (in == NULL || out == NULL) {
  251     out = NULL;
  252     }
  253     else {
  254     size_t  i;
  255 
  256     if (in < out) {
  257         for (i = count; i-- > 0;)
  258         out[i] = 1.0 / in[i];
  259     }
  260     else {
  261         for (i = 0; i < count; i++)
  262         out[i] = 1.0 / in[i];
  263     }
  264     }
  265 
  266     return out;
  267 }
  268 
  269 
  270 static int
  271 reciprocalGetExpression(
  272     const cv_converter* const   conv,
  273     char* const         buf,
  274     const size_t        max,
  275     const char* const       variable)
  276 {
  277     return
  278     cvNeedsParentheses(variable)
  279     ? snprintf(buf, max, "1/(%s)", variable)
  280     : snprintf(buf, max, "1/%s", variable);
  281 }
  282 
  283 
  284 static ConverterOps reciprocalOps = {
  285     reciprocalClone,
  286     reciprocalConvertDouble,
  287     reciprocalConvertFloats,
  288     reciprocalConvertDoubles,
  289     reciprocalGetExpression,
  290     nonFree};
  291 
  292 static cv_converter reciprocalConverter = {&reciprocalOps};
  293 
  294 
  295 /*******************************************************************************
  296  * Scale Converter:
  297  ******************************************************************************/
  298 
  299 static cv_converter*
  300 scaleClone(
  301     cv_converter* const conv)
  302 {
  303     return cv_get_scale(conv->scale.value);
  304 }
  305 
  306 
  307 static double
  308 scaleConvertDouble(
  309     const cv_converter* const   conv,
  310     const double        value)
  311 {
  312     return conv->scale.value * value;
  313 }
  314 
  315 
  316 static float*
  317 scaleConvertFloats(
  318     const cv_converter* const   conv,
  319     const float* const      in,
  320     const size_t        count,
  321     float*          out)
  322 {
  323     if (conv == NULL || in == NULL || out == NULL) {
  324     out = NULL;
  325     }
  326     else {
  327     size_t  i;
  328 
  329     if (in < out) {
  330         for (i = count; i-- > 0;)
  331         out[i] = (float)(conv->scale.value * in[i]);
  332     }
  333     else {
  334         for (i = 0; i < count; i++)
  335         out[i] = (float)(conv->scale.value * in[i]);
  336     }
  337     }
  338 
  339     return out;
  340 }
  341 
  342 
  343 static double*
  344 scaleConvertDoubles(
  345     const cv_converter* const   conv,
  346     const double* const     in,
  347     const size_t        count,
  348     double*             out)
  349 {
  350     if (conv == NULL || in == NULL || out == NULL) {
  351     out = NULL;
  352     }
  353     else {
  354     size_t  i;
  355 
  356     if (in < out) {
  357         for (i = count; i-- > 0;)
  358         out[i] = conv->scale.value * in[i];
  359     }
  360     else {
  361         for (i = 0; i < count; i++)
  362         out[i] = conv->scale.value * in[i];
  363     }
  364     }
  365 
  366     return out;
  367 }
  368 
  369 
  370 static int
  371 scaleGetExpression(
  372     const cv_converter* const   conv,
  373     char* const         buf,
  374     const size_t        max,
  375     const char* const       variable)
  376 {
  377     return
  378     cvNeedsParentheses(variable)
  379     ? snprintf(buf, max, "%g*(%s)", conv->scale.value, variable)
  380     : snprintf(buf, max, "%g*%s", conv->scale.value, variable);
  381 }
  382 
  383 
  384 static ConverterOps scaleOps = {
  385     scaleClone,
  386     scaleConvertDouble,
  387     scaleConvertFloats,
  388     scaleConvertDoubles,
  389     scaleGetExpression,
  390     cvSimpleFree};
  391 
  392 
  393 /*******************************************************************************
  394  * Offset Converter:
  395  ******************************************************************************/
  396 
  397 static cv_converter*
  398 offsetClone(
  399     cv_converter* const conv)
  400 {
  401     return cv_get_offset(conv->offset.value);
  402 }
  403 
  404 
  405 static double
  406 offsetConvertDouble(
  407     const cv_converter* const   conv,
  408     const double        value)
  409 {
  410     return conv->offset.value + value;
  411 }
  412 
  413 
  414 static float*
  415 offsetConvertFloats(
  416     const cv_converter* const   conv,
  417     const float* const      in,
  418     const size_t        count,
  419     float*          out)
  420 {
  421     if (conv == NULL || in == NULL || out == NULL) {
  422     out = NULL;
  423     }
  424     else {
  425     size_t  i;
  426 
  427     if (in < out) {
  428         for (i = count; i-- > 0;)
  429         out[i] = (float)(conv->offset.value + in[i]);
  430     }
  431     else {
  432         for (i = 0; i < count; i++)
  433         out[i] = (float)(conv->offset.value + in[i]);
  434     }
  435     }
  436 
  437     return out;
  438 }
  439 
  440 
  441 static double*
  442 offsetConvertDoubles(
  443     const cv_converter* const   conv,
  444     const double* const     in,
  445     const size_t        count,
  446     double*             out)
  447 {
  448     if (conv == NULL || in == NULL || out == NULL) {
  449     out = NULL;
  450     }
  451     else {
  452     size_t  i;
  453 
  454     if (in < out) {
  455         for (i = count; i-- > 0;)
  456         out[i] = conv->offset.value + in[i];
  457     }
  458     else {
  459         for (i = 0; i < count; i++)
  460         out[i] = conv->offset.value + in[i];
  461     }
  462     }
  463 
  464     return out;
  465 }
  466 
  467 
  468 static int
  469 offsetGetExpression(
  470     const cv_converter* const   conv,
  471     char* const         buf,
  472     const size_t        max,
  473     const char* const       variable)
  474 {
  475     const int   oper = conv->offset.value < 0 ? '-' : '+';
  476 
  477     return
  478     cvNeedsParentheses(variable)
  479         ? snprintf(buf, max, "(%s) %c %g", variable, oper,
  480         fabs(conv->offset.value))
  481         : snprintf(buf, max, "%s %c %g", variable, oper,
  482         fabs(conv->offset.value));
  483 }
  484 
  485 
  486 static ConverterOps offsetOps = {
  487     offsetClone,
  488     offsetConvertDouble,
  489     offsetConvertFloats,
  490     offsetConvertDoubles,
  491     offsetGetExpression,
  492     cvSimpleFree};
  493 
  494 
  495 /*******************************************************************************
  496  * Galilean Converter:
  497  ******************************************************************************/
  498 
  499 static cv_converter*
  500 cvGalileanClone(
  501     cv_converter* const conv)
  502 {
  503     return cv_get_galilean(conv->galilean.slope, conv->galilean.intercept);
  504 }
  505 
  506 
  507 static double
  508 galileanConvertDouble(
  509     const cv_converter* const   conv,
  510     const double        value)
  511 {
  512     return conv->galilean.slope * value + conv->galilean.intercept;
  513 }
  514 
  515 
  516 static float*
  517 galileanConvertFloats(
  518     const cv_converter* const   conv,
  519     const float* const      in,
  520     const size_t        count,
  521     float*          out)
  522 {
  523     if (conv == NULL || in == NULL || out == NULL) {
  524     out = NULL;
  525     }
  526     else {
  527     size_t  i;
  528 
  529     if (in < out) {
  530         for (i = count; i-- > 0;)
  531         out[i] = (float)(conv->galilean.slope * in[i] +
  532             conv->galilean.intercept);
  533     }
  534     else {
  535         for (i = 0; i < count; i++)
  536         out[i] = (float)(conv->galilean.slope * in[i] +
  537             conv->galilean.intercept);
  538     }
  539     }
  540 
  541     return out;
  542 }
  543 
  544 
  545 static double*
  546 galileanConvertDoubles(
  547     const cv_converter* const   conv,
  548     const double* const     in,
  549     const size_t        count,
  550     double*             out)
  551 {
  552     if (conv == NULL || in == NULL || out == NULL) {
  553     out = NULL;
  554     }
  555     else {
  556     size_t  i;
  557 
  558     if (in < out) {
  559         for (i = count; i-- > 0;)
  560         out[i] = conv->galilean.slope * in[i] +
  561             conv->galilean.intercept;
  562     }
  563     else {
  564         for (i = 0; i < count; i++)
  565         out[i] = conv->galilean.slope * in[i] +
  566             conv->galilean.intercept;
  567     }
  568     }
  569 
  570     return out;
  571 }
  572 
  573 
  574 static int
  575 galileanGetExpression(
  576     const cv_converter* const   conv,
  577     char* const         buf,
  578     const size_t        max,
  579     const char* const       variable)
  580 {
  581     const int   oper = conv->galilean.intercept < 0 ? '-' : '+';
  582 
  583     return
  584     cvNeedsParentheses(variable)
  585         ? snprintf(buf, max, "%g*(%s) %c %g", conv->galilean.slope,
  586         variable, oper, fabs(conv->galilean.intercept))
  587         : snprintf(buf, max, "%g*%s %c %g", conv->galilean.slope, variable,
  588         oper, fabs(conv->galilean.intercept));
  589 }
  590 
  591 
  592 static ConverterOps galileanOps = {
  593     cvGalileanClone,
  594     galileanConvertDouble,
  595     galileanConvertFloats,
  596     galileanConvertDoubles,
  597     galileanGetExpression,
  598     cvSimpleFree};
  599 
  600 
  601 /*******************************************************************************
  602  * Logarithmic Converter:
  603  ******************************************************************************/
  604 
  605 static cv_converter*
  606 cvLogClone(
  607     cv_converter* const conv)
  608 {
  609     return
  610         cv_get_log(
  611             conv->log.logE == M_LOG2E
  612                 ? 2
  613                 : conv->log.logE == 1
  614                     ? M_E
  615                     : conv->log.logE == M_LOG10E
  616                         ? 10
  617                         : exp(conv->log.logE));
  618 }
  619 
  620 
  621 static double
  622 logConvertDouble(
  623     const cv_converter* const   conv,
  624     const double        value)
  625 {
  626     return log(value) * conv->log.logE;
  627 }
  628 
  629 
  630 static float*
  631 logConvertFloats(
  632     const cv_converter* const   conv,
  633     const float* const      in,
  634     const size_t        count,
  635     float*          out)
  636 {
  637     if (conv == NULL || in == NULL || out == NULL) {
  638     out = NULL;
  639     }
  640     else {
  641     size_t  i;
  642 
  643     if (in < out) {
  644         for (i = count; i-- > 0;)
  645         out[i] = (float)(log(in[i]) * conv->log.logE);
  646     }
  647     else {
  648         for (i = 0; i < count; i++)
  649         out[i] = (float)(log(in[i]) * conv->log.logE);
  650     }
  651     }
  652 
  653     return out;
  654 }
  655 
  656 
  657 static double*
  658 logConvertDoubles(
  659     const cv_converter* const   conv,
  660     const double* const     in,
  661     const size_t        count,
  662     double*             out)
  663 {
  664     if (conv == NULL || in == NULL || out == NULL) {
  665     out = NULL;
  666     }
  667     else {
  668     size_t  i;
  669 
  670     if (in < out) {
  671         for (i = count; i-- > 0;)
  672         out[i] = log(in[i]) * conv->log.logE;
  673     }
  674     else {
  675         for (i = 0; i < count; i++)
  676         out[i] = log(in[i]) * conv->log.logE;
  677     }
  678     }
  679 
  680     return out;
  681 }
  682 
  683 
  684 static int
  685 logGetExpression(
  686     const cv_converter* const   conv,
  687     char* const         buf,
  688     const size_t        max,
  689     const char* const       variable)
  690 {
  691     return
  692         conv->log.logE == M_LOG2E
  693             ? snprintf(buf, max, "lb(%s)", variable)
  694             : conv->log.logE == 1
  695                 ? snprintf(buf, max, "ln(%s)", variable)
  696                 : conv->log.logE == M_LOG10E
  697                     ? snprintf(buf, max, "lg(%s)", variable)
  698                     : snprintf(buf, max, "%g*ln(%s)", conv->log.logE, variable);
  699 }
  700 
  701 
  702 static ConverterOps logOps = {
  703     cvLogClone,
  704     logConvertDouble,
  705     logConvertFloats,
  706     logConvertDoubles,
  707     logGetExpression,
  708     cvSimpleFree};
  709 
  710 
  711 /*******************************************************************************
  712  * Exponential Converter:
  713  ******************************************************************************/
  714 
  715 static cv_converter*
  716 expClone(
  717     cv_converter* const conv)
  718 {
  719     return cv_get_pow(conv->exp.base);
  720 }
  721 
  722 
  723 static double
  724 expConvertDouble(
  725     const cv_converter* const   conv,
  726     const double        value)
  727 {
  728     return pow(conv->exp.base, value);
  729 }
  730 
  731 static float*
  732 expConvertFloats(
  733     const cv_converter* const   conv,
  734     const float* const      in,
  735     const size_t        count,
  736     float*          out)
  737 {
  738     if (conv == NULL || in == NULL || out == NULL) {
  739     out = NULL;
  740     }
  741     else {
  742     size_t  i;
  743 
  744     if (in < out) {
  745         for (i = count; i-- > 0;)
  746         out[i] = (float)(pow(conv->exp.base, in[i]));
  747     }
  748     else {
  749         for (i = 0; i < count; i++)
  750         out[i] = (float)(pow(conv->exp.base, in[i]));
  751     }
  752     }
  753 
  754     return out;
  755 }
  756 
  757 
  758 static double*
  759 expConvertDoubles(
  760     const cv_converter* const   conv,
  761     const double* const     in,
  762     const size_t        count,
  763     double*             out)
  764 {
  765     if (conv == NULL || in == NULL || out == NULL) {
  766     out = NULL;
  767     }
  768     else {
  769     size_t  i;
  770 
  771     if (in < out) {
  772         for (i = count; i-- > 0;)
  773         out[i] = pow(conv->exp.base, in[i]);
  774     }
  775     else {
  776         for (i = 0; i < count; i++)
  777         out[i] = pow(conv->exp.base, in[i]);
  778     }
  779     }
  780 
  781     return out;
  782 }
  783 
  784 
  785 static int
  786 expGetExpression(
  787     const cv_converter* const   conv,
  788     char* const         buf,
  789     const size_t        max,
  790     const char* const       variable)
  791 {
  792     return
  793     cvNeedsParentheses(variable)
  794         ? snprintf(buf, max, "pow(%g, (%s))", conv->exp.base, variable)
  795         : snprintf(buf, max, "pow(%g, %s)", conv->exp.base, variable);
  796 }
  797 
  798 
  799 static ConverterOps expOps = {
  800     expClone,
  801     expConvertDouble,
  802     expConvertFloats,
  803     expConvertDoubles,
  804     expGetExpression,
  805     cvSimpleFree};
  806 
  807 
  808 /*******************************************************************************
  809  * Composite Converter:
  810  ******************************************************************************/
  811 
  812 static cv_converter*
  813 compositeClone(
  814     cv_converter* const conv)
  815 {
  816     return cv_combine(conv->composite.first, conv->composite.second);
  817 }
  818 
  819 
  820 static double
  821 compositeConvertDouble(
  822     const cv_converter* const   conv,
  823     const double        value)
  824 {
  825     return
  826     cv_convert_double(conv->composite.second,
  827         cv_convert_double(((CompositeConverter*)conv)->first, value));
  828 }
  829 
  830 
  831 static float*
  832 compositeConvertFloats(
  833     const cv_converter* const   conv,
  834     const float* const      in,
  835     const size_t        count,
  836     float*          out)
  837 {
  838     if (conv == NULL || in == NULL || out == NULL) {
  839     out = NULL;
  840     }
  841     else {
  842     out =
  843         cv_convert_floats(
  844         conv->composite.second,
  845         cv_convert_floats(conv->composite.first, in, count, out),
  846         count,
  847         out);
  848     }
  849 
  850     return out;
  851 }
  852 
  853 
  854 static double*
  855 compositeConvertDoubles(
  856     const cv_converter* const   conv,
  857     const double* const     in,
  858     const size_t        count,
  859     double*             out)
  860 {
  861     if (conv == NULL || in == NULL || out == NULL) {
  862     out = NULL;
  863     }
  864     else {
  865     out =
  866         cv_convert_doubles(
  867         conv->composite.second,
  868         cv_convert_doubles(conv->composite.first, in, count, out),
  869         count,
  870         out);
  871     }
  872 
  873     return out;
  874 }
  875 
  876 
  877 static void
  878 compositeFree(
  879     cv_converter* const conv)
  880 {
  881     cv_free(conv->composite.first);
  882     cv_free(conv->composite.second);
  883     free(conv);
  884 }
  885 
  886 
  887 static int
  888 compositeGetExpression(
  889     const cv_converter* const   conv,
  890     char* const         buf,
  891     const size_t        max,
  892     const char* const       variable)
  893 {
  894     char    tmpBuf[132];
  895     int     nchar = cv_get_expression(conv->composite.first, buf, max,
  896     variable);
  897 
  898     if (nchar >= 0) {
  899     buf[max-1] = 0;
  900 
  901     if (cvNeedsParentheses(buf)) {
  902         (void)snprintf(tmpBuf, sizeof(tmpBuf), "(%s)", buf);
  903     }
  904     else {
  905         (void)strncpy(tmpBuf, buf, sizeof(tmpBuf));
  906 
  907         tmpBuf[sizeof(tmpBuf)-1] = 0;
  908     }
  909 
  910     nchar = cv_get_expression(conv->composite.second, buf, max, tmpBuf);
  911     }
  912 
  913     return nchar;
  914 }
  915 
  916 
  917 static ConverterOps compositeOps = {
  918     compositeClone,
  919     compositeConvertDouble,
  920     compositeConvertFloats,
  921     compositeConvertDoubles,
  922     compositeGetExpression,
  923     compositeFree};
  924 
  925 
  926 /*******************************************************************************
  927  * Public API:
  928  ******************************************************************************/
  929 
  930 /*
  931  * Returns the trivial converter (i.e., y = x).
  932  * When finished with the converter, the client should pass the converter to
  933  * cv_free().
  934  *
  935  * Returns:
  936  *  The trivial converter.
  937  */
  938 cv_converter*
  939 cv_get_trivial()
  940 {
  941     return &trivialConverter;
  942 }
  943 
  944 
  945 /*
  946  * Returns the reciprocal converter (i.e., y = 1/x).
  947  * When finished with the converter, the client should pass the converter to
  948  * cv_free().
  949  *
  950  * Returns:
  951  *  The reciprocal converter.
  952  */
  953 cv_converter*
  954 cv_get_inverse()
  955 {
  956     return &reciprocalConverter;
  957 }
  958 
  959 
  960 /*
  961  * Returns a converter that multiplies values by a number (i.e., y = ax).
  962  * When finished with the converter, the client should pass the converter to
  963  * cv_free().
  964  *
  965  * Arguments:
  966  *  slope   The number by which to multiply values.
  967  * Returns:
  968  *  NULL    Necessary memory couldn't be allocated.
  969  *  else    A converter that will multiply values by the given number.
  970  */
  971 cv_converter*
  972 cv_get_scale(
  973     const double    slope)
  974 {
  975     cv_converter*   conv;
  976 
  977     if (slope == 1) {
  978     conv = &trivialConverter;
  979     }
  980     else {
  981     conv = malloc(sizeof(*conv));
  982 
  983     if (conv != NULL) {
  984         conv->ops = &scaleOps;
  985         conv->scale.value = slope;
  986     }
  987     }
  988 
  989     return conv;
  990 }
  991 
  992 
  993 /*
  994  * Returns a converter that adds a number to values (i.e., y = x + b).
  995  * When finished with the converter, the client should pass the converter to
  996  * cv_free().
  997  *
  998  * Arguments:
  999  *  offset  The number to be added.
 1000  * Returns:
 1001  *  NULL    Necessary memory couldn't be allocated.
 1002  *  else    A converter that adds the given number to values.
 1003  */
 1004 cv_converter*
 1005 cv_get_offset(
 1006     const double    offset)
 1007 {
 1008     cv_converter*   conv;
 1009 
 1010     if (offset == 0) {
 1011     conv = &trivialConverter;
 1012     }
 1013     else {
 1014     conv = malloc(sizeof(*conv));
 1015 
 1016     if (conv != NULL) {
 1017         conv->ops = &offsetOps;
 1018         conv->offset.value = offset;
 1019     }
 1020     }
 1021 
 1022     return conv;
 1023 }
 1024 
 1025 
 1026 /*
 1027  * Returns a Galilean converter (i.e., y = ax + b).
 1028  * When finished with the converter, the client should pass the converter to
 1029  * cv_free().
 1030  *
 1031  * Arguments:
 1032  *  slope   The number by which to multiply values.
 1033  *  offset  The number to be added.
 1034  * Returns:
 1035  *  NULL    Necessary memory couldn't be allocated.
 1036  *  else    A Galilean converter corresponding to the inputs.
 1037  */
 1038 cv_converter*
 1039 cv_get_galilean(
 1040     const double    slope,
 1041     const double    intercept)
 1042 {
 1043     cv_converter*   conv;
 1044 
 1045     if (slope == 1) {
 1046     conv = cv_get_offset(intercept);
 1047     }
 1048     else if (intercept == 0) {
 1049     conv = cv_get_scale(slope);
 1050     }
 1051     else {
 1052     conv = malloc(sizeof(*conv));
 1053 
 1054     if (conv != NULL) {
 1055         conv->ops = &galileanOps;
 1056         conv->galilean.slope = slope;
 1057         conv->galilean.intercept = intercept;
 1058     }
 1059     }
 1060 
 1061     return conv;
 1062 }
 1063 
 1064 
 1065 /*
 1066  * Returns a logarithmic converter (i.e., y = log(x/x0) in some base).
 1067  * When finished with the converter, the client should pass the converter to
 1068  * cv_free().
 1069  *
 1070  * Arguments:
 1071  *  base        The logarithmic base (e.g., 2, M_E, 10).  Must be
 1072  *                      greater than one.
 1073  * Returns:
 1074  *  NULL        "base" is invalid or necessary memory couldn't be
 1075  *          allocated.
 1076  *  else        A logarithmic converter corresponding to the inputs.
 1077  */
 1078 cv_converter*
 1079 cv_get_log(
 1080     const double    base)
 1081 {
 1082     cv_converter*   conv;
 1083 
 1084     if (base <= 1) {
 1085     conv = NULL;
 1086     }
 1087     else {
 1088     conv = malloc(sizeof(*conv));
 1089 
 1090     if (conv != NULL) {
 1091         conv->ops = &logOps;
 1092         conv->log.logE =
 1093                 base == 2
 1094                     ? M_LOG2E
 1095                     : base == M_E
 1096                         ? 1
 1097                         : base == 10
 1098                             ? M_LOG10E
 1099                             : 1/log(base);
 1100     }
 1101     }
 1102 
 1103     return conv;
 1104 }
 1105 
 1106 
 1107 /*
 1108  * Returns an exponential converter (i.e., y = pow(b, x) in some base "b").
 1109  * When finished with the converter, the client should pass the converter to
 1110  * cv_free().
 1111  *
 1112  * Arguments:
 1113  *  base        The desired base.  Must be positive.
 1114  * Returns:
 1115  *  NULL        "base" is invalid or necessary memory couldn't be
 1116  *          allocated.
 1117  *  else        An exponential converter corresponding to the inputs.
 1118  */
 1119 cv_converter*
 1120 cv_get_pow(
 1121     const double    base)
 1122 {
 1123     cv_converter*   conv;
 1124 
 1125     if (base <= 0) {
 1126     conv = NULL;
 1127     }
 1128     else {
 1129     conv = malloc(sizeof(*conv));
 1130 
 1131     if (conv != NULL) {
 1132         conv->ops = &expOps;
 1133         conv->exp.base = base;
 1134     }
 1135     }
 1136 
 1137     return conv;
 1138 }
 1139 
 1140 
 1141 /*
 1142  * Returns a converter corresponding to the sequential application of two
 1143  * other converters.  The returned converter should be passed to cv_free() when
 1144  * it is no longer needed.
 1145  *
 1146  * Arguments:
 1147  *  first   The converter to be applied first.  May be passed to cv_free()
 1148  *      upon return.
 1149  *  second  The converter to be applied second.  May be passed to cv_free()
 1150  *      upon return.
 1151  * Returns:
 1152  *  NULL    Either "first" or "second" is NULL or necessary memory couldn't
 1153  *      be allocated.
 1154  *      else    A converter corresponding to the sequential application of the
 1155  *              given converters.  If one of the input converters is the trivial
 1156  *              converter, then the returned converter will be the other input
 1157  *              converter.
 1158  */
 1159 cv_converter*
 1160 cv_combine(
 1161     cv_converter* const first,
 1162     cv_converter* const second)
 1163 {
 1164     cv_converter*   conv;
 1165 
 1166     if (first == NULL || second == NULL) {
 1167     conv = NULL;
 1168     }
 1169     else if (IS_TRIVIAL(first)) {
 1170     conv = CV_CLONE(second);
 1171     }
 1172     else if (IS_TRIVIAL(second)) {
 1173     conv = CV_CLONE(first);
 1174     }
 1175     else {
 1176     conv = NULL;
 1177 
 1178     if (IS_RECIPROCAL(first)) {
 1179         if (IS_RECIPROCAL(second)) {
 1180         conv = cv_get_trivial();
 1181         }
 1182     }
 1183     else if (IS_SCALE(first)) {
 1184         if (IS_SCALE(second)) {
 1185         conv = cv_get_scale(first->scale.value * second->scale.value);
 1186         }
 1187         else if (IS_OFFSET(second)) {
 1188         conv = cv_get_galilean(first->scale.value, second->offset.value);
 1189         }
 1190         else if (IS_GALILEAN(second)) {
 1191         conv = cv_get_galilean(
 1192             first->scale.value * second->galilean.slope,
 1193             second->galilean.intercept);
 1194         }
 1195     }
 1196     else if (IS_OFFSET(first)) {
 1197         if (IS_SCALE(second)) {
 1198         conv = cv_get_galilean(second->scale.value,
 1199             first->offset.value * second->scale.value);
 1200         }
 1201         else if (IS_OFFSET(second)) {
 1202         conv = cv_get_offset(first->offset.value + second->offset.value);
 1203         }
 1204         else if (IS_GALILEAN(second)) {
 1205         conv = cv_get_galilean(second->galilean.slope,
 1206             first->offset.value * second->galilean.slope +
 1207             second->galilean.intercept);
 1208         }
 1209     }
 1210     else if (IS_GALILEAN(first)) {
 1211         if (IS_SCALE(second)) {
 1212         conv = cv_get_galilean(
 1213             second->scale.value * first->galilean.slope,
 1214             second->scale.value * first->galilean.intercept);
 1215         }
 1216         else if (IS_OFFSET(second)) {
 1217         conv = cv_get_galilean(first->galilean.slope,
 1218             first->galilean.intercept + second->offset.value);
 1219         }
 1220         else if (IS_GALILEAN(second)) {
 1221         conv = cv_get_galilean(
 1222             second->galilean.slope * first->galilean.slope,
 1223             second->galilean.slope * first->galilean.intercept +
 1224             second->galilean.intercept);
 1225         }
 1226     }
 1227 
 1228     if (conv == NULL) {
 1229         /*
 1230          * General case: create a composite converter.
 1231          */
 1232         cv_converter*   c1 = CV_CLONE(first);
 1233             int                 error = 1;
 1234 
 1235             if (c1 != NULL) {
 1236                 cv_converter*   c2 = CV_CLONE(second);
 1237 
 1238                 if (c2 != NULL) {
 1239                     conv = malloc(sizeof(*conv));
 1240 
 1241                     if (conv != NULL) {
 1242                         conv->composite.ops = &compositeOps;
 1243                         conv->composite.first = c1;
 1244                         conv->composite.second = c2;
 1245                         error = 0;
 1246                     }                   /* "conv" allocated */
 1247 
 1248                     if (error)
 1249                         cv_free(c2);
 1250                 }                       /* "c2" allocated */
 1251 
 1252                 if (error)
 1253                     cv_free(c1);
 1254             }                           /* "c1" allocated */
 1255     }                               /* "conv != NULL" */
 1256     }                                   /* "first" & "second" not trivial */
 1257 
 1258     return conv;
 1259 }
 1260 
 1261 
 1262 /*
 1263  * Frees resources associated with a converter.  Use of the converter argument
 1264  * subsequent to this function may result in undefined behavior.
 1265  *
 1266  * Arguments:
 1267  *  conv    The converter to have its resources freed or NULL.  The
 1268  *      converter must have been returned by this module.
 1269  */
 1270 void
 1271 cv_free(
 1272     cv_converter* const conv)
 1273 {
 1274     if (conv != NULL) {
 1275     conv->ops->free((cv_converter*)conv);
 1276     }
 1277 }
 1278 
 1279 
 1280 /*
 1281  * Converts a float.
 1282  *
 1283  * Arguments:
 1284  *  converter   Pointer to the converter.
 1285  *  value       The value to be converted.
 1286  * Returns:
 1287  *  The converted value.
 1288  */
 1289 float
 1290 cv_convert_float(
 1291     const cv_converter* converter,
 1292     const float     value)
 1293 {
 1294     return (float)converter->ops->convertDouble(converter, value);
 1295 }
 1296 
 1297 
 1298 /*
 1299  * Converts a double.
 1300  *
 1301  * Arguments:
 1302  *  converter   Pointer to the converter.
 1303  *  value       The value to be converted.
 1304  * Returns:
 1305  *  The converted value.
 1306  */
 1307 double
 1308 cv_convert_double(
 1309     const cv_converter* converter,
 1310     const double    value)
 1311 {
 1312     return converter->ops->convertDouble(converter, value);
 1313 }
 1314 
 1315 
 1316 /*
 1317  * Converts an array of floats.
 1318  *
 1319  * Arguments:
 1320  *  converter   Pointer to the converter.
 1321  *  in      Pointer to the values to be converted.  The array may
 1322  *          overlap "out".
 1323  *  count       The number of values to be converted.
 1324  *  out     Pointer to the output array for the converted values.
 1325  *          The array may overlap "in".
 1326  * Returns:
 1327  *  NULL        "converter", "in", or "out" is NULL.
 1328  *  else        Pointer to the output array, "out".
 1329  */
 1330 float*
 1331 cv_convert_floats(
 1332     const cv_converter* converter,
 1333     const float* const  in,
 1334     const size_t    count,
 1335     float*      out)
 1336 {
 1337     if (converter == NULL || in == NULL || out == NULL) {
 1338     out = NULL;
 1339     }
 1340     else {
 1341         out = converter->ops->convertFloats(converter, in, count, out);
 1342     }
 1343 
 1344     return out;
 1345 }
 1346 
 1347 
 1348 /*
 1349  * Converts an array of doubles.
 1350  *
 1351  * Arguments:
 1352  *  converter   Pointer to the converter.
 1353  *  in      Pointer to the values to be converted.  The array may
 1354  *          overlap "out".
 1355  *  count       The number of values to be converted.
 1356  *  out     Pointer to the output array for the converted values.
 1357  *          The array may overlap "in".
 1358  * Returns:
 1359  *  NULL        "converter", "in", or "out" is NULL.
 1360  *  else        Pointer to the output array, "out".
 1361  */
 1362 double*
 1363 cv_convert_doubles(
 1364     const cv_converter* converter,
 1365     const double* const in,
 1366     const size_t    count,
 1367     double*     out)
 1368 {
 1369     if (converter == NULL || in == NULL || out == NULL) {
 1370     out = NULL;
 1371     }
 1372     else {
 1373         out = converter->ops->convertDoubles(converter, in, count, out);
 1374     }
 1375 
 1376     return out;
 1377 }
 1378 
 1379 
 1380 /*
 1381  * Returns a string expression representation of a converter.
 1382  *
 1383  * Arguments:
 1384  *  conv        The converter.
 1385  *  buf     The buffer into which to write the expression.
 1386  *  max     The size of the buffer.
 1387  *  variable    The string to be used as the input value for the
 1388  *          converter.
 1389  * RETURNS
 1390  *  <0  An error was encountered.
 1391  *  else    The number of bytes formatted excluding the terminating null.
 1392  */
 1393 int
 1394 cv_get_expression(
 1395     const cv_converter* const   conv,
 1396     char* const         buf,
 1397     size_t          max,
 1398     const char* const       variable)
 1399 {
 1400     return conv->ops->getExpression(conv, buf, max, variable);
 1401 }