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

unitcore.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.
 */
/*
 * Unit creation and manipulation routines for the udunits(3) library.
 *
 * The following data-structures exist in this module:
 *    BasicUnit   Like an ISO "base unit" but also for dimensionless
 *                units (e.g., "radian").
 *    ProductUnit A unit that, when it is created, contains all the
 *                BasicUnit-s that exist at the time, each raised
 *                to an integral power (that can be zero).
 *    GalileanUnit      A unit whose value is related to another unit by a 
 *                Galilean transformation (y = ax + b).  Examples include
 *                "yard" and "degrees Fahrenheit".
 *    LogUnit           A unit that is related to another unit by a logarithmic
 *                transformation (y = a*log(x)).  The "Bel" is an example.
 *    TimestampUnit     A wrong-headed unit that shouldn't exist but does for
 *                backward compatibility.  It was intended to provide 
 *                similar functionality as the GalileanUnit, but for time
 *                units (e.g., "seconds since the epoch").  Unfortunately,
 *                people try to use it for more than it is capable (e.g.,
 *                days since some time on an imaginary world with only 360
 *                days per year).
 *    ut_unit           A data-structure that encapsulates ProductUnit, 
 *                GalileanUnit, LogUnit, and TimestampUnit.
 *
 * This module is thread-compatible but not thread-safe: multi-thread access to
 * this module must be externally synchronized.
 */

/*LINTLIBRARY*/

#ifndef     _XOPEN_SOURCE
#   define _XOPEN_SOURCE 500
#endif

#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <float.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <search.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>

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

typedef enum {
    PRODUCT_EQUAL = 0,        /* The units are equal -- ignoring dimensionless
                         * basic-units */
    PRODUCT_INVERSE,          /* The units are reciprocals of each other */
    PRODUCT_UNCONVERTIBLE,    /* The units have incompatible dimensionality */
    PRODUCT_UNKNOWN           /* The relationship is unknown */
} ProductRelationship;

typedef struct BasicUnit      BasicUnit;
typedef struct ProductUnit    ProductUnit;

00070 struct ut_system {
    ut_unit*            second;
    ut_unit*            one;        /* the dimensionless-unit one */
    BasicUnit**         basicUnits;
    int                 basicCount;
};

00077 typedef struct {
    ProductUnit*  (*getProduct)(const ut_unit*);
    ut_unit*            (*clone)(const ut_unit*);
    void          (*free)(ut_unit*);
    /*
     * The following comparison function is called if and only if the two units
     * belong to the same unit system.
     */
    int                 (*compare)(const ut_unit*, const ut_unit*);
    ut_unit*            (*multiply)(const ut_unit*, const ut_unit*);
    ut_unit*            (*raise)(const ut_unit*, const int power);
    ut_unit*            (*root)(const ut_unit*, const int root);
    int                 (*initConverterToProduct)(ut_unit*);
    int                 (*initConverterFromProduct)(ut_unit*);
    ut_status           (*acceptVisitor)(const ut_unit*, const ut_visitor*,
                      void*);
} UnitOps;

typedef enum {
    BASIC,
    PRODUCT,
    GALILEAN,
    LOG,
    TIMESTAMP
} UnitType;

#undef      ABS
#define     ABS(a)            ((a) < 0 ? -(a) : (a))
#undef      MIN
#define     MIN(a,b)    ((a) < (b) ? (a) : (b))
#undef      MAX
#define     MAX(a,b)    ((a) > (b) ? (a) : (b))

#define GET_PRODUCT(unit) \
                  ((unit)->common.ops->getProduct(unit))
#define CLONE(unit)     ((unit)->common.ops->clone(unit))
#define MULTIPLY(unit1, unit2) \
                  ((unit1)->common.ops->multiply(unit1, unit2))
#define RAISE(unit, power) \
                  ((unit)->common.ops->raise(unit, power))
#define ROOT(unit, root) \
                  ((unit)->common.ops->root(unit, root))
#define FREE(unit)      ((unit)->common.ops->free(unit))
#define COMPARE(unit1, unit2) \
                  ((unit1)->common.ops->compare(unit1, unit2))
#define ENSURE_CONVERTER_TO_PRODUCT(unit) \
                  ((unit)->common.toProduct != NULL || \
                  (unit)->common.ops->initConverterToProduct(unit) == 0)
#define ENSURE_CONVERTER_FROM_PRODUCT(unit) \
                  ((unit)->common.fromProduct != NULL || \
                  (unit)->common.ops->initConverterFromProduct(unit) == 0)
#define ACCEPT_VISITOR(unit, visitor, arg) \
                  ((unit)->common.ops->acceptVisitor(unit, visitor, arg))

00131 typedef struct {
    ut_system*          system;
    const UnitOps*      ops;
    UnitType            type;
    cv_converter* toProduct;
    cv_converter* fromProduct;
} Common;

00139 struct BasicUnit {
    Common        common;
    ProductUnit*  product;          /* equivalent product-unit */
    int                 index;                  /* system->basicUnits index */
    int                 isDimensionless;
};

00146 struct ProductUnit {
    Common        common;
    short*        indexes;
    short*        powers;
    int                 count;
};

00153 typedef struct {
    Common        common;
    ut_unit*            unit;
    double        scale;
    double        offset;
} GalileanUnit;

00160 typedef struct {
    Common        common;
    ut_unit*            unit;
    double        origin;
} TimestampUnit;

00166 typedef struct {
    Common        common;
    ut_unit*            reference;
    double        base;
} LogUnit;

00172 union ut_unit {
    Common        common;
    BasicUnit           basic;
    ProductUnit         product;
    GalileanUnit  galilean;
    TimestampUnit timestamp;
    LogUnit       log;
};

#define IS_BASIC(unit)        ((unit)->common.type == BASIC)
#define IS_PRODUCT(unit)      ((unit)->common.type == PRODUCT)
#define IS_GALILEAN(unit)     ((unit)->common.type == GALILEAN)
#define IS_LOG(unit)          ((unit)->common.type == LOG)
#define IS_TIMESTAMP(unit)    ((unit)->common.type == TIMESTAMP)

/*
 * The following function are declared here because they are used in the
 * basic-unit section  before they are defined in the product-unit section.
 */
static ProductUnit*     productNew(
    ut_system* const          system,
    const short* const        indexes,
    const short* const        powers,
    const int                 count);
static void       productFree(
    ut_unit* const            unit);
static ut_unit*         productMultiply(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2);
static ut_unit*         productRaise(
    const ut_unit* const      unit,
    const int                 power);
static ut_unit*         productRoot(
    const ut_unit* const      unit,
    const int                 root);


/*
 * The following two functions convert between Julian day number and
 * Gregorian/Julian dates (Julian dates are used prior to October 15,
 * 1582; Gregorian dates are used after that).  Julian day number 0 is
 * midday, January 1, 4713 BCE.  The Gregorian calendar was adopted
 * midday, October 15, 1582.
 *
 * Author: Robert Iles, March 1994
 *
 * C Porter: Steve Emmerson, October 1995
 *
 * Original: http://www.nag.co.uk:70/nagware/Examples/calendar.f90
 *
 * There is no warranty on this code.
 */


/*
 * Convert a Julian day number to a Gregorian/Julian date.
 */
void
julianDayToGregorianDate(julday, year, month, day)
    long    julday;           /* Julian day number to convert */
    int           *year;            /* Gregorian year (out) */
    int           *month;           /* Gregorian month (1-12) (out) */
    int           *day;       /* Gregorian day (1-31) (out) */
{
    long    ja, jb, jd;
    int           jc;
    int           je, iday, imonth, iyear;
    double  xc;

    if (julday < 2299161)
      ja = julday;
    else
    {
      int   ia = (int)(((julday - 1867216) - 0.25) / 36524.25);

      ja = julday + 1 + ia - (int)(0.25 * ia);
    }

    jb = ja + 1524;
    xc = ((jb - 2439870) - 122.1) / 365.25;
    jc = (int)(6680.0 + xc);
    jd = 365 * jc + (int)(0.25 * jc);
    je = (int)((jb - jd) / 30.6001);

    iday = (int)(jb - jd - (int)(30.6001 * je));

    imonth = je - 1;
    if (imonth > 12)
      imonth -= 12;

    iyear = jc - 4715;
    if (imonth > 2)
      iyear -= 1;
    if (iyear <= 0)
      iyear -= 1;

    *year = iyear;
    *month = imonth;
    *day = iday;
}


/*
 * Convert a Gregorian/Julian date to a Julian day number.
 *
 * The Gregorian calendar was adopted midday, October 15, 1582.
 */
long
gregorianDateToJulianDay(year, month, day)
    int           year; /* Gregorian year */
    int           month;      /* Gregorian month (1-12) */
    int           day;  /* Gregorian day (1-31) */
{
    int32_t igreg = 15 + 31 * (10 + (12 * 1582));
    int32_t iy;   /* signed, origin 0 year */
    int32_t ja;   /* Julian century */
    int32_t jm;   /* Julian month */
    int32_t jy;   /* Julian year */
    long    julday;     /* returned Julian day number */

    /*
     * Because there is no 0 BC or 0 AD, assume the user wants the start of 
     * the common era if they specify year 0.
     */
    if (year == 0)
      year = 1;

    iy = year;
    if (year < 0)
      iy++;
    if (month > 2)
    {
      jy = iy;
      jm = month + 1;
    }
    else
    {
      jy = iy - 1;
      jm = month + 13;
    }

    /*
     *  Note: SLIGHTLY STRANGE CONSTRUCTIONS REQUIRED TO AVOID PROBLEMS WITH
     *        OPTIMISATION OR GENERAL ERRORS UNDER VMS!
     */
    julday = day + (int)(30.6001 * jm);
    if (jy >= 0)
    {
      julday += 365 * jy;
      julday += 0.25 * jy;
    }
    else
    {
      double      xi = 365.25 * jy;

      if ((int)xi != xi)
          xi -= 1;
      julday += (int)xi;
    }
    julday += 1720995;

    if (day + (31* (month + (12 * iy))) >= igreg)
    {
      ja = jy/100;
      julday -= ja;
      julday += 2;
      julday += ja/4;
    }

    return julday;
}

/*
 * Returns the Julian day number that is the origin of all things temporal in
 * this module.
 *
 * Returns:
 *      The Julian day number that is the origin for time in this module.
 */
static long
getJuldayOrigin()
{
    static long juldayOrigin;

    if (juldayOrigin == 0)
      juldayOrigin = gregorianDateToJulianDay(2001, 1, 1);

    return juldayOrigin;
}


/*
 * Encodes a time as a double-precision value.
 *
 * Arguments:
 *    hours       The number of hours (0 = midnight).
 *    minutes           The number of minutes.
 *    seconds           The number of seconds.
 * Returns:
 *    The clock-time encoded as a scalar value.
 */
double
ut_encode_clock(
    int           hours,
    int           minutes,
    double  seconds)
{
    return (hours*60 + minutes)*60 + seconds;
}


/*
 * Decompose a value into a set of values accounting for uncertainty.
 */
static void
decompose(value, uncer, nbasis, basis, count)
    double  value;
    double  uncer;            /* >= 0 */
    int           nbasis;
    double  *basis;           /* all values > 0 */
    double  *count;
{
    int           i;

    for (i = 0; i < nbasis; i++)
    {
      double      r = fmod(value, basis[i]);    /* remainder */

      /* Adjust remainder to minimum magnitude. */
      if (ABS(2*r) > basis[i])
          r += r > 0
                ? -basis[i]
                :  basis[i];

      if (ABS(r) <= uncer)
      {
          /* The value equals a basis multiple within the uncertainty. */
          double  half = value < 0 ? -basis[i]/2 : basis[i]/2;
          modf((value+half)/basis[i], count+i);
          break;
      }

      value = basis[i] * modf(value/basis[i], count+i);
    }

    if (i >= nbasis) {
      count[--i] += value;
    }
    else {
      for (i++; i < nbasis; i++)
          count[i] = 0;
    }
}


/*
 * Encodes a date as a double-precision value.
 *
 * Arguments:
 *    year        The year.
 *    month       The month.
 *    day         The day (1 = the first of the month).
 * Returns:
 *    The date encoded as a scalar value.
 */
double
ut_encode_date(
    int           year,
    int           month,
    int           day)
{
    return 86400.0 *
      (gregorianDateToJulianDay(year, month, day) - getJuldayOrigin());
}


/*
 * Encodes a time as a double-precision value.  The convenience function is
 * equivalent to "ut_encode_date(year,month,day) + 
 * ut_encode_clock(hour,minute,second)"
 *
 * Arguments:
 *    year  The year.
 *    month The month.
 *    day   The day.
 *    hour  The hour.
 *    minute      The minute.
 *    second      The second.
 * Returns:
 *    The time encoded as a scalar value.
 */
double
ut_encode_time(
    const int           year,
    const int           month,
    const int           day,
    const int           hour,
    const int           minute,
    const double  second)
{
    return ut_encode_date(year, month, day) + ut_encode_clock(hour, minute, second);
}


/*
 * Decodes a time from a double-precision value.
 *
 * Arguments:
 *      value           The value to be decoded.
 *      year            Pointer to the variable to be set to the year.
 *      month           Pointer to the variable to be set to the month.
 *      day             Pointer to the variable to be set to the day.
 *      hour            Pointer to the variable to be set to the hour.
 *      minute          Pointer to the variable to be set to the minute.
 *      second          Pointer to the variable to be set to the second.
 *      resolution      Pointer to the variable to be set to the resolution
 *                      of the decoded time in seconds.
 */
void
ut_decode_time(
    double  value,
    int           *year,
    int           *month,
    int           *day,
    int           *hour,
    int           *minute,
    double  *second,
    double  *resolution)
{
    int                 days;
    int                 hours;
    int                 minutes;
    double        seconds;
    double        uncer;            /* uncertainty of input value */
    typedef union
    {
      double          vec[7];
      struct
      {
          double  days;
          double  hours12;
          double  hours;
          double  minutes10;
          double  minutes;
          double  seconds10;
          double  seconds;
      }         ind;
    } Basis;
    Basis         counts;
    static const Basis  basis = {86400, 43200, 3600, 600, 60, 10, 1};

    uncer = ldexp(value < 0 ? -value : value, -DBL_MANT_DIG);

    days = (int)floor(value/basis.ind.days);
    value -= days * basis.ind.days;       /* make positive excess */

    decompose(value, uncer, (int)(sizeof(basis.vec)/sizeof(basis.vec[0])),
         basis.vec, counts.vec);

    days += counts.ind.days;
    hours = (int)counts.ind.hours12 * 12 + (int)counts.ind.hours;
    minutes = (int)counts.ind.minutes10 * 10 + (int)counts.ind.minutes;
    seconds = (int)counts.ind.seconds10 * 10 + counts.ind.seconds;

    if (seconds >= 60) {
      seconds -= 60;
      if (++minutes >= 60) {
          minutes -= 60;
          if (++hours >= 24) {
            hours -= 24;
            days++;
          }
      }
    }

    *second = seconds;
    *minute = minutes;
    *hour = hours;
    *resolution = uncer;

    julianDayToGregorianDate(getJuldayOrigin() + days, year, month, day);
}


/******************************************************************************
 * Parameters common to all types of units:
 ******************************************************************************/


/*
 * Arguments:
 *    common      Pointer to unit common-area.
 *    ops   Pointer to unit-specific function-structure.
 *    system      Pointer to unit-system.
 *    type  The type of unit.
 * Returns:
 *     0    Success.
 */
static int
commonInit(
    Common* const       common,
    const UnitOps* const      ops,
    const ut_system* const    system,
    const UnitType            type)
{
    assert(system != NULL);
    assert(common != NULL);
    assert(ops != NULL);

    common->system = (ut_system*)system;
    common->ops = ops;
    common->type = type;
    common->toProduct = NULL;
    common->fromProduct = NULL;

    return 0;
}


/******************************************************************************
 * Basic-Unit:
 ******************************************************************************/


static UnitOps    basicOps;


/*
 * Returns a new instance of a basic-unit.
 *
 * Arguments:
 *    system            The unit-system to be associated with the new instance.
 *    isDimensionless   Whether or not the unit is dimensionless (e.g., 
 *                "radian").
 *    index       The index of the basic-unit in "system".
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_OS   Operating-system error.  See "errno".
 *    else  Pointer to newly-allocated basic-unit.
 */
static BasicUnit*
basicNew(
    ut_system* const    system,
    const int           isDimensionless,
    const int           index)
{
    BasicUnit*          basicUnit = NULL; /* failure */
    int                 error = 1;
    short         power = 1;
    short         shortIndex = (short)index;
    ProductUnit*  product;

    assert(system != NULL);

    product = productNew(system, &shortIndex, &power, 1);

    if (product == NULL) {
      ut_set_status(UT_OS);
      ut_handle_error_message(
          "basicNew(): Couldn't create new product-unit");
    }
    else {
      basicUnit = malloc(sizeof(BasicUnit));

      if (basicUnit == NULL) {
          ut_set_status(UT_OS);
          ut_handle_error_message(strerror(errno));
          ut_handle_error_message(
            "basicNew(): Couldn't allocate %lu-byte basic-unit",
            sizeof(BasicUnit));
      }
      else if (commonInit(&basicUnit->common, &basicOps, system,
            BASIC) == 0) {
          basicUnit->index = index;
          basicUnit->isDimensionless = isDimensionless;
          basicUnit->product = product;
          error = 0;
      }                       /* "basicUnit" allocated */

      if (error)
          productFree((ut_unit*)product);
    }                   /* "product" allocated */

    return basicUnit;
}


static ProductUnit*
basicGetProduct(
    const ut_unit* const      unit)
{
    assert(IS_BASIC(unit));

    return unit->basic.product;
}


static ut_unit*
basicClone(
    const ut_unit* const      unit)
{
    assert(IS_BASIC(unit));

    return (ut_unit*)basicNew(unit->common.system, unit->basic.isDimensionless,
      unit->basic.index);
}


static void
basicFree(
    ut_unit* const      unit)
{
    if (unit != NULL) {
      assert(IS_BASIC(unit));
      productFree((ut_unit*)unit->basic.product);
      unit->basic.product = NULL;
      free(unit);
    }
}


static int
basicCompare(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    int     cmp;

    assert(unit1 != NULL);
    assert(IS_BASIC(unit1));
    assert(unit2 != NULL);

    if (IS_PRODUCT(unit2)) {
      cmp = -COMPARE(unit2, unit1);
    }
    else if (!IS_BASIC(unit2)) {
      int   diff = unit1->common.type - unit2->common.type;

      cmp = diff < 0 ? -1 : diff == 0 ? 0 : 1;
    }
    else {
      int   index1 = unit1->basic.index;
      int   index2 = unit2->basic.index;

      cmp = index1 < index2 ? -1 : index1 == index2 ? 0 : 1;
    }

    return cmp;
}


/*
 * Multiplies a basic-unit by another unit.
 *
 * Arguments:
 *    unit1 The basic-unit.
 *    unit2 The other unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
basicMultiply(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    assert(unit1 != NULL);
    assert(unit2 != NULL);
    assert(IS_BASIC(unit1));

    return productMultiply((const ut_unit*)unit1->basic.product, unit2);
}


/*
 * Returns the result of raising a basic-unit to a power.
 *
 * Arguments:
 *    unit  The basic-unit.
 *    power The power.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given unit is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
basicRaise(
    const ut_unit* const      unit,
    const int                 power)
{
    assert(unit != NULL);
    assert(IS_BASIC(unit));
    assert(power != 0);
    assert(power != 1);

    return productRaise((ut_unit*)unit->basic.product, power);
}


/*
 * Returns the result of taking a root of a basic-unit.
 *
 * Arguments:
 *    unit  The basic-unit.
 *    root  The root to take.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given unit is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
basicRoot(
    const ut_unit* const      unit,
    const int                 root)
{
    assert(unit != NULL);
    assert(IS_BASIC(unit));
    assert(root > 1);

    return productRoot((ut_unit*)unit->basic.product, root);
}


/*
 * Initializes the converter of numeric from the given product-unit to the
 * underlying product-unit (i.e., to itself).
 *
 * Arguments:
 *    unit  The product unit.
 * Returns:
 *     0    Success.
 */
static int
basicInitConverterToProduct(
    ut_unit* const      unit)
{
    assert(unit != NULL);
    assert(IS_BASIC(unit));

    if (unit->common.toProduct == NULL)
      unit->common.toProduct = cv_get_trivial();

    return 0;
}


/*
 * Initializes the converter of numeric to the given product-unit from the
 * underlying product-unit (i.e., to itself).
 *
 * Arguments:
 *    unit  The product unit.
 * Returns:
 *     0    Success.
 */
static int
basicInitConverterFromProduct(
    ut_unit* const      unit)
{
    assert(unit != NULL);
    assert(IS_BASIC(unit));

    if (unit->common.fromProduct == NULL)
      unit->common.fromProduct = cv_get_trivial();

    return 0;
}


static ut_status
basicAcceptVisitor(
    const ut_unit* const            unit,
    const ut_visitor* const   visitor,
    void* const               arg)
{
    assert(unit != NULL);
    assert(IS_BASIC(unit));
    assert(visitor != NULL);

    return visitor->visit_basic(unit, arg);
}


static UnitOps    basicOps = {
    basicGetProduct,
    basicClone,
    basicFree,
    basicCompare,
    basicMultiply,
    basicRaise,
    basicRoot,
    basicInitConverterToProduct,
    basicInitConverterFromProduct,
    basicAcceptVisitor
};


/******************************************************************************
 * Product Unit:
 ******************************************************************************/

static UnitOps    productOps;


/*
 * Arguments:
 *    system      The unit-system for the new unit.
 *    indexes     Pointer to array of indexes of basic-units.  May be freed upon
 *          return.
 *    powers      Pointer to array of powers.  Client may free upon return.
 *    count The number of elements in "indexes" and "powers".  May be zero.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_OS   Operating-system error.  See "errno".
 *    else  The newly-allocated, product-unit.
 */
static ProductUnit*
productNew(
    ut_system* const    system,
    const short* const  indexes,
    const short* const  powers,
    const int           count)
{
    ProductUnit*  productUnit;

    assert(system != NULL);
    assert(count >= 0);
    assert(count == 0 || (indexes != NULL && powers != NULL));

    productUnit = malloc(sizeof(ProductUnit));

    if (productUnit == NULL) {
      ut_set_status(UT_OS);
      ut_handle_error_message(strerror(errno));
      ut_handle_error_message(
          "productNew(): Couldn't allocate %d-byte product-unit",
          sizeof(ProductUnit));
    }
    else {
      int   error = 1;

      if (commonInit(&productUnit->common, &productOps, system, PRODUCT)
            == 0) {
            if (count == 0) {
                productUnit->count = count;
                productUnit->indexes = NULL;
                productUnit->powers = NULL;
                error = 0;
            }
            else {
                size_t  nbytes = sizeof(short)*count;
                short*  newIndexes = malloc(nbytes*2);

                if (count > 0 && newIndexes == NULL) {
                    ut_set_status(UT_OS);
                    ut_handle_error_message(strerror(errno));
                    ut_handle_error_message("productNew(): "
                        "Couldn't allocate %d-element index array", count);
                }
                else {
                    short*    newPowers = newIndexes + count;

                    productUnit->count = count;
                    productUnit->indexes = memcpy(newIndexes, indexes, nbytes);
                    productUnit->powers = memcpy(newPowers, powers, nbytes);
                    error = 0;
                }
          }                           /* "count > 0" */
      }                               /* "productUnit->common" initialized */

      if (error) {
          free(productUnit);
          productUnit = NULL;
      }
    }                           /* "productUnit" allocated */

    return productUnit;
}


static ProductUnit*
productGetProduct(
    const ut_unit* const      unit)
{
    assert(unit != NULL);
    assert(IS_PRODUCT(unit));

    return (ProductUnit*)&unit->product;
}


static ut_unit*
productClone(
    const ut_unit* const      unit)
{
    ut_unit*            clone;

    assert(unit != NULL);
    assert(IS_PRODUCT(unit));

    if (unit == unit->common.system->one) {
      clone = unit->common.system->one;
    }
    else {
      clone = (ut_unit*)productNew(unit->common.system,
          unit->product.indexes, unit->product.powers,
          unit->product.count);
    }

    return clone;
}


static int
productCompare(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    int     cmp;

    assert(unit1 != NULL);
    assert(IS_PRODUCT(unit1));
    assert(unit2 != NULL);

    if (IS_BASIC(unit2)) {
      cmp = productCompare(unit1, (ut_unit*)unit2->basic.product);
    }
    else if (!IS_PRODUCT(unit2)) {
      int   diff = unit1->common.type - unit2->common.type;

      cmp = diff < 0 ? -1 : diff == 0 ? 0 : 1;
    }
    else {
      const ProductUnit* const      product1 = &unit1->product;
      const ProductUnit* const      product2 = &unit2->product;

        cmp = product1->count - product2->count;

        if (cmp == 0) {
          const short* const  indexes1 = product1->indexes;
          const short* const  indexes2 = product2->indexes;
          const short* const  powers1     = product1->powers;
          const short* const  powers2     = product2->powers;
          int                 i;

          for (i = 0; i < product1->count; ++i) {
            cmp = indexes1[i] - indexes2[i];

            if (cmp == 0)
                cmp = powers1[i] - powers2[i];

            if (cmp != 0)
                break;
          }
      }
    }

    return cmp;
}


static void
productReallyFree(
    ut_unit* const      unit)
{
    if (unit != NULL) {
      assert(IS_PRODUCT(unit));
      free(unit->product.indexes);
      unit->product.indexes = NULL;
      cv_free(unit->common.toProduct);
      unit->common.toProduct = NULL;
      cv_free(unit->common.fromProduct);
      unit->common.fromProduct = NULL;
      free(unit);
    }
}


static void
productFree(
    ut_unit* const      unit)
{
    if (unit != unit->common.system->one)
      productReallyFree(unit);
}


/*
 * Multiplies a product-unit by another unit.
 *
 * Arguments:
 *    unit1 The product unit.
 *    unit2 The other unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *              UT_OS         Operating-system failure.  See "errno".
 *    else  The resulting unit.
 */
static ut_unit*
productMultiply(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    ut_unit*            result = NULL;    /* failure */

    assert(unit1 != NULL);
    assert(unit2 != NULL);
    assert(IS_PRODUCT(unit1));

    if (!IS_PRODUCT(unit2)) {
      result = MULTIPLY(unit2, unit1);
    }
    else {
      const ProductUnit* const      product1 = &unit1->product;
      const ProductUnit* const      product2 = &unit2->product;
      short*                        indexes1 = product1->indexes;
      short*                        indexes2 = product2->indexes;
      short*                        powers1 = product1->powers;
      short*                        powers2 = product2->powers;
      int                     count1 = product1->count;
      int                     count2 = product2->count;
      int                     sumCount = count1 + count2;

      if (sumCount == 0) {
          result = unit1->common.system->one;
      }
      else {
          static short*       indexes = NULL;

          indexes = realloc(indexes, sizeof(short)*sumCount);

          if (indexes == NULL) {
            ut_set_status(UT_OS);
            ut_handle_error_message(strerror(errno));
            ut_handle_error_message("productMultiply(): "
                "Couldn't allocate %d-element index array", sumCount);
          }
          else {
            static short*     powers = NULL;
            
            powers = realloc(powers, sizeof(short)*sumCount);

            if (powers == NULL) {
                ut_set_status(UT_OS);
                ut_handle_error_message(strerror(errno));
                ut_handle_error_message("productMultiply(): "
                  "Couldn't allocate %d-element power array",
                  sumCount);
            }
            else {
                int                       count = 0;
                int                       i1 = 0;
                int                       i2 = 0;

                while (i1 < count1 || i2 < count2) {
                  if (i1 >= count1) {
                      indexes[count] = indexes2[i2];
                      powers[count++] = powers2[i2++];
                  }
                  else if (i2 >= count2) {
                      indexes[count] = indexes1[i1];
                      powers[count++] = powers1[i1++];
                  }
                  else if (indexes1[i1] > indexes2[i2]) {
                      indexes[count] = indexes2[i2];
                      powers[count++] = powers2[i2++];
                  }
                  else if (indexes1[i1] < indexes2[i2]) {
                      indexes[count] = indexes1[i1];
                      powers[count++] = powers1[i1++];
                  }
                  else {
                      if (powers1[i1] != -powers2[i2]) {
                        indexes[count] = indexes1[i1];
                        powers[count++] = powers1[i1] + powers2[i2];
                      }

                      i1++;
                      i2++;
                  }
                }

                result = (ut_unit*)productNew(unit1->common.system,
                  indexes, powers, count);
            }                 /* "powers" re-allocated */
          }                   /* "indexes" re-allocated */
      }                       /* "sumCount > 0" */
    }                         /* "unit2" is a product-unit */

    return result;
}


/*
 * Returns the result of raising a product unit to a power.
 *
 * Arguments:
 *    unit  The product unit.
 *    power The power.  Must be greater than -256 and less than 256.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given unit is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
productRaise(
    const ut_unit* const      unit,
    const int                 power)
{
    ut_unit*            result = NULL;    /* failure */
    const ProductUnit*  product;
    int                 count;
    short*        newPowers;

    assert(unit != NULL);
    assert(IS_PRODUCT(unit));
    assert(power >= -255 && power <= 255);
    assert(power != 0);
    assert(power != 1);

    product = &unit->product;
    count = product->count;

    if (count == 0) {
        result = unit->common.system->one;
    }
    else {
        newPowers = malloc(sizeof(short)*count);

        if (newPowers == NULL) {
            ut_set_status(UT_OS);
            ut_handle_error_message(strerror(errno));
            ut_handle_error_message("productRaise(): "
                "Couldn't allocate %d-element powers-buffer", count);
        }
        else {
            const short* const      oldPowers = product->powers;
            int               i;

            for (i = 0; i < count; i++)
                newPowers[i] = (short)(oldPowers[i] * power);

            result = (ut_unit*)productNew(unit->common.system,
                product->indexes, newPowers, count);

            free(newPowers);
        }                     /* "newPowers" allocated */
    }                           /* "count > 0" */

    return result;
}


/*
 * Returns the result of taking a root of a unit.
 *
 * Arguments:
 *    unit  The product unit.
 *    root  The root.  Must be greater than 1 and less than 256.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given unit is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
productRoot(
    const ut_unit* const      unit,
    const int                 root)
{
    ut_unit*            result = NULL;    /* failure */
    const ProductUnit*  product;
    int                 count;
    short*        newPowers;

    assert(unit != NULL);
    assert(IS_PRODUCT(unit));
    assert(root > 1 && root <= 255);

    product = &unit->product;
    count = product->count;

    if (count == 0) {
        result = unit->common.system->one;
    }
    else {
        newPowers = malloc(sizeof(short)*count);

        if (newPowers == NULL) {
            ut_set_status(UT_OS);
            ut_handle_error_message(strerror(errno));
            ut_handle_error_message("productRoot(): "
                "Couldn't allocate %d-element powers-buffer", count);
        }
        else {
            const short* const      oldPowers = product->powers;
            int               i;

            for (i = 0; i < count; i++) {
                if ((oldPowers[i] % root) != 0) {
                    break;
                }
                newPowers[i] = (short)(oldPowers[i] / root);
            }

            if (i < count) {
                char buf[80];

                if (ut_format(unit, buf, sizeof(buf), UT_ASCII) == -1) {
                    ut_set_status(UT_MEANINGLESS);
                    ut_handle_error_message("productRoot(): "
                        "Can't take root of unit");
                }
                else {
                    ut_set_status(UT_MEANINGLESS);
                    buf[sizeof(buf)-1] = 0;
                    ut_handle_error_message("productRoot(): "
                        "It's meaningless to take the %d%s root of \"%s\"",
                        root, root == 2 ? "nd" : root == 3 ? "rd" : "th",
                        buf);
                }
            }
            else {
                result = (ut_unit*)productNew(unit->common.system,
                    product->indexes, newPowers, count);
            }

            free(newPowers);
        }                     /* "newPowers" allocated */
    }                           /* "count > 0" */

    return result;
}


/*
 * Initializes a converter of numeric values between the given product-unit and
 * the underlying product-unit (i.e., to itself).
 *
 * Arguments:
 *    converter   Pointer to pointer to the converter to be initialized.
 * Returns:
 *     0    Success.
 */
static int
productInitConverter(
    cv_converter** const      converter)
{
    assert(converter != NULL);

    *converter = cv_get_trivial();

    return 0;
}


/*
 * Initializes the converter of numeric values from the given product-unit to
 * the underlying product-unit (i.e., to itself).
 *
 * Arguments:
 *    unit  The product unit.
 * Returns:
 *     0    Success.
 */
static int
productInitConverterToProduct(
    ut_unit* const      unit)
{
    assert(unit != NULL);
    assert(IS_PRODUCT(unit));

    return productInitConverter(&unit->common.toProduct);
}


/*
 * Initializes the converter of numeric values to the given product-unit from
 * the underlying product-unit (i.e., to itself).
 *
 * Arguments:
 *    unit  The product unit.
 * Returns:
 *     0    Success.
 */
static int
productInitConverterFromProduct(
    ut_unit* const      unit)
{
    assert(unit != NULL);
    assert(IS_PRODUCT(unit));

    return productInitConverter(&unit->common.fromProduct);
}


/*
 * Returns the relationship between two product-units.  In determining the
 * relationship, dimensionless basic-units are ignored.
 *
 * Arguments:
 *    unit1 The first product unit.
 *    unit2 The second product unit.
 * Returns:
 *    PRODUCT_EQUAL           The units are equal -- ignoring dimensionless
 *                      basic-units.
 *    PRODUCT_INVERSE         The units are reciprocals of each other.
 *    PRODUCT_UNCONVERTIBLE   The dimensionalities of the units are
 *                      unconvertible.
 */
static ProductRelationship
productRelationship(
    const ProductUnit* const  unit1,
    const ProductUnit* const  unit2)
{
    ProductRelationship       relationship = PRODUCT_UNKNOWN;

    assert(unit1 != NULL);
    assert(unit2 != NULL);

    {
      const short* const      indexes1 = unit1->indexes;
      const short* const      indexes2 = unit2->indexes;
      const short* const      powers1 = unit1->powers;
      const short* const      powers2 = unit2->powers;
      const int         count1 = unit1->count;
      const int         count2 = unit2->count;
      const ut_system* const  system = unit1->common.system;
      int               i1 = 0;
      int               i2 = 0;

      while (i1 < count1 || i2 < count2) {
          int     iBasic = -1;

          if (i1 >= count1) {
            iBasic = indexes2[i2++];
          }
          else if (i2 >= count2) {
            iBasic = indexes1[i1++];
          }
          else if (indexes1[i1] > indexes2[i2]) {
            iBasic = indexes2[i2++];
          }
          else if (indexes1[i1] < indexes2[i2]) {
            iBasic = indexes1[i1++];
          }

          if (iBasic != -1) {
            if (!system->basicUnits[iBasic]->isDimensionless) {
                relationship = PRODUCT_UNCONVERTIBLE;
                break;
            }
          }
          else {
            iBasic = indexes1[i1];

            if (!system->basicUnits[iBasic]->isDimensionless) {
                if (powers1[i1] == powers2[i2]) {
                  if (relationship == PRODUCT_INVERSE) {
                      relationship = PRODUCT_UNCONVERTIBLE;
                      break;
                  }

                  relationship = PRODUCT_EQUAL;
                }
                else if (powers1[i1] == -powers2[i2]) {
                  if (relationship == PRODUCT_EQUAL) {
                      relationship = PRODUCT_UNCONVERTIBLE;
                      break;
                  }

                  relationship = PRODUCT_INVERSE;
                }
                else {
                  relationship = PRODUCT_UNCONVERTIBLE;
                  break;
                }
            }

            i1++;
            i2++;
          }
      }
    }

    if (relationship == PRODUCT_UNKNOWN) {
      /*
       * Both units are dimensionless.
       */
      relationship = PRODUCT_EQUAL;
    }

    return relationship;
}


static ut_status
productAcceptVisitor(
    const ut_unit* const      unit,
    const ut_visitor* const   visitor,
    void* const               arg)
{
    int           count = unit->product.count;
    BasicUnit**   basicUnits = malloc(sizeof(BasicUnit)*count);

    assert(unit != NULL);
    assert(IS_PRODUCT(unit));
    assert(visitor != NULL);

    if (count != 0 && basicUnits == NULL) {
      ut_set_status(UT_OS);
      ut_handle_error_message(strerror(errno));
      ut_handle_error_message("productAcceptVisitor(): "
          "Couldn't allocate %d-element basic-unit array", count);
    }
    else {
      int*  powers = malloc(sizeof(int)*count);

      if (count != 0 && powers == NULL) {
          ut_set_status(UT_OS);
          ut_handle_error_message(strerror(errno));
          ut_handle_error_message("productAcceptVisitor(): "
            "Couldn't allocate %d-element power array", count);
      }
      else {
          const ProductUnit*  prodUnit = &unit->product;
          int                 i;

          for (i = 0; i < count; ++i) {
            basicUnits[i] =
                unit->common.system->basicUnits[prodUnit->indexes[i]];
            powers[i] = prodUnit->powers[i];
          }

          ut_set_status(visitor->visit_product(unit, count,
            (const ut_unit**)basicUnits, powers, arg));

          free(powers);
      }                       /* "powers" allocated */

      free(basicUnits);
    }                         /* "basicUnits" allocated */

    return ut_get_status();
}


static UnitOps    productOps = {
    productGetProduct,
    productClone,
    productFree,
    productCompare,
    productMultiply,
    productRaise,
    productRoot,
    productInitConverterToProduct,
    productInitConverterFromProduct,
    productAcceptVisitor
};


/*
 * Indicates if a product-unit is dimensionless or not.
 *
 * Arguments:
 *    unit  The product-unit in question.
 * Returns:
 *    0     "unit" is dimensionfull.
 *    else  "unit" is dimensionless.
 */
static int
productIsDimensionless(
    const ProductUnit* const  unit)
{
    int                 isDimensionless = 1;
    int                 count;
    const short*  indexes;
    ut_system*          system;
    int                 i;

    assert(unit != NULL);
    assert(IS_PRODUCT(unit));

    count = unit->count;
    indexes = unit->indexes;
    system = unit->common.system;

    for (i = 0; i < count; ++i) {
      if (!system->basicUnits[indexes[i]]->isDimensionless) {
          isDimensionless = 0;
          break;
      }
    }

    return isDimensionless;
}


/******************************************************************************
 * Galilean Unit:
 ******************************************************************************/


static UnitOps    galileanOps;


/*
 * Returns a new unit instance.  The returned instance is not necessarily a
 * Galilean unit.
 *
 * Arguments:
 *    scale The scale-factor for the new unit.
 *    unit  The underlying unit.  May be freed upon return.
 *    offset      The offset for the new unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_OS   Operating-system error.  See "errno".
 *    else  The newly-allocated, galilean-unit.
 */
static ut_unit*
galileanNew(
    double        scale,
    const ut_unit*      unit,
    double        offset)
{
    ut_unit*      newUnit = NULL;   /* failure */

    assert(scale != 0);
    assert(unit != NULL);

    if (IS_GALILEAN(unit)) {
      scale *= unit->galilean.scale;
      offset += (unit->galilean.scale * unit->galilean.offset) / scale;
      unit = unit->galilean.unit;
    }

    if (scale == 1 && offset == 0) {
      newUnit = CLONE(unit);
    }
    else {
      GalileanUnit*     galileanUnit = malloc(sizeof(GalileanUnit));

      if (galileanUnit == NULL) {
          ut_set_status(UT_OS);
          ut_handle_error_message("galileanNew(): "
            "Couldn't allocate %lu-byte Galilean unit",
            sizeof(GalileanUnit));
      }
      else {
          int     error = 1;

          if (commonInit(&galileanUnit->common, &galileanOps,
                unit->common.system, GALILEAN) == 0) {
            galileanUnit->scale = scale;
            galileanUnit->offset = offset;
            galileanUnit->unit = CLONE(unit);
            error = 0;
          }

          if (error) {
            free(galileanUnit);
            galileanUnit = NULL;
          }
      }                       /* "galileanUnit" allocated */

      newUnit = (ut_unit*)galileanUnit;
    }                         /* Galilean unit necessary */

    return newUnit;
}


static ProductUnit*
galileanGetProduct(
    const ut_unit* const      unit)
{
    assert(unit != NULL);
    assert(IS_GALILEAN(unit));

    return GET_PRODUCT(unit->galilean.unit);
}


static ut_unit*
galileanClone(
    const ut_unit* const      unit)
{
    const GalileanUnit* galileanUnit;

    assert(unit != NULL);
    assert(IS_GALILEAN(unit));

    galileanUnit = &unit->galilean;

    return galileanNew(galileanUnit->scale, galileanUnit->unit,
      galileanUnit->offset);
}


static int
galileanCompare(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    int     cmp;

    assert(unit1 != NULL);
    assert(IS_GALILEAN(unit1));

    if (!IS_GALILEAN(unit2)) {
      int   diff = unit1->common.type - unit2->common.type;

      cmp = diff < 0 ? -1 : diff == 0 ? 0 : 1;
    }
    else {
      const GalileanUnit* const     galilean1 = &unit1->galilean;
      const GalileanUnit* const     galilean2 = &unit2->galilean;

      cmp =
          galilean1->offset < galilean2->offset
            ? -1
            : galilean1->offset == galilean2->offset
                ? 0
                : 1;

      if (cmp == 0) {
          cmp =
            galilean1->scale < galilean2->scale
                ? -1
                : galilean1->scale == galilean2->scale
                  ? 0
                  : 1;

          if (cmp == 0)
            cmp = COMPARE(galilean1->unit, galilean2->unit);
      }
    }

    return cmp;
}


static void
galileanFree(
    ut_unit* const      unit)
{
    if (unit != NULL) {
      assert(IS_GALILEAN(unit));
      FREE(unit->galilean.unit);
      cv_free(unit->common.toProduct);
      unit->common.toProduct = NULL;
      cv_free(unit->common.fromProduct);
      unit->common.fromProduct = NULL;
      free((void*)unit);
    }
}


/*
 * Multiplies a Galilean-unit by another unit.  Any offset is ignored.
 *
 * Arguments:
 *    unit1 The Galilean-unit.
 *    unit2 The other unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
galileanMultiply(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    ut_unit*            result = NULL;    /* failure */
    const GalileanUnit* galilean1;

    assert(unit1 != NULL);
    assert(unit2 != NULL);
    assert(IS_GALILEAN(unit1));

    galilean1 = &unit1->galilean;

    if (IS_PRODUCT(unit2)) {
      ut_unit*    tmp = MULTIPLY(galilean1->unit, unit2);

      if (tmp != NULL) {
          result = galileanNew(galilean1->scale, tmp, 0);

          FREE(tmp);
      }
    }
    else if (IS_GALILEAN(unit2)) {
      const GalileanUnit* const     galilean2 = &unit2->galilean;
      ut_unit*                      tmp =
          MULTIPLY(galilean1->unit, galilean2->unit);

      if (tmp != NULL) {
          result = galileanNew(galilean1->scale * galilean2->scale, tmp, 0);

          FREE(tmp);
      }
    }
    else {
      result = MULTIPLY(unit2, unit1);
    }

    return result;
}


/*
 * Returns the result of raising a Galilean-unit to a power.  Any offset is
 * ignored.
 *
 * Arguments:
 *    unit  The Galilean-unit.
 *    power The power.  Must be greater than -256 and less than 256.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
galileanRaise(
    const ut_unit* const      unit,
    const int                 power)
{
    const GalileanUnit*  galilean;
    ut_unit*             tmp;
    ut_unit*             result = NULL;  /* failure */

    assert(unit != NULL);
    assert(IS_GALILEAN(unit));
    assert(power >= -255 && power <= 255);
    assert(power != 0);
    assert(power != 1);

    galilean = &unit->galilean;
    tmp = RAISE(galilean->unit, power);

    if (tmp != NULL) {
        result = galileanNew(pow(galilean->scale, power), tmp, 0);

        ut_free(tmp);
    }

    return result;
}


/*
 * Returns the result of taking a root of a Galilean-unit.  Any offset is
 * ignored.
 *
 * Arguments:
 *    unit  The Galilean-unit.
 *    root  The root.  Must be greater than 1 and less than 256.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
galileanRoot(
    const ut_unit* const      unit,
    const int                 root)
{
    const GalileanUnit*  galilean;
    ut_unit*             tmp;
    ut_unit*             result = NULL;  /* failure */

    assert(unit != NULL);
    assert(IS_GALILEAN(unit));
    assert(root > 1 && root <= 255);

    galilean = &unit->galilean;
    tmp = ROOT(galilean->unit, root);

    if (tmp != NULL) {
        result = galileanNew(pow(galilean->scale, 1.0/root), tmp, 0);

        ut_free(tmp);
    }

    return result;
}


/*
 * Initializes the converter of numeric values from the given Galilean unit to
 * the underlying product-unit.
 *
 * Arguments:
 *    unit  The Galilean unit.
 * Returns:
 *    -1    Failure.  "ut_get_status()" will be:
 *              UT_OS   Operating-system fault.  See "errno".
 *    0     Success.
 */
static int
galileanInitConverterToProduct(
    ut_unit* const      unit)
{
    int                 retCode = -1;     /* failure */
    cv_converter* toUnderlying;

    assert(unit != NULL);
    assert(IS_GALILEAN(unit));

    toUnderlying = cv_get_galilean(
      unit->galilean.scale, unit->galilean.offset * unit->galilean.scale);

    if (toUnderlying == NULL) {
      ut_set_status(UT_OS);
      ut_handle_error_message(strerror(errno));
      ut_handle_error_message("galileanInitConverterToProduct(): "
          "Couldn't get converter to underlying unit");
    }
    else {
      if (ENSURE_CONVERTER_TO_PRODUCT(unit->galilean.unit)) {
          assert(unit->common.toProduct == NULL);

          unit->common.toProduct = cv_combine(
            toUnderlying, unit->galilean.unit->common.toProduct);

          if (unit->common.toProduct == NULL) {
            ut_set_status(UT_OS);
            ut_handle_error_message(strerror(errno));
            ut_handle_error_message("galileanInitConverterToProduct(): "
                "Couldn't combine converters");
          }
          else {
            retCode = 0;
          }
      }

      cv_free(toUnderlying);
    }                   /* "toUnderlying" allocated */

    return retCode;
}


/*
 * Initializes the converter of numeric values to the given Galilean unit from
 * the underlying product-unit.
 *
 * Arguments:
 *    unit  The Galilean unit.
 * Returns:
 *    -1    Failure.  "ut_get_status()" will be:
 *              UT_OS   Operating-system fault.  See "errno".
 *    0     Success.
 */
static int
galileanInitConverterFromProduct(
    ut_unit* const      unit)
{
    int                 retCode = -1;           /* failure */
    cv_converter* fromUnderlying;

    assert(unit != NULL);
    assert(IS_GALILEAN(unit));

    fromUnderlying = cv_get_galilean(
      1.0/unit->galilean.scale, -unit->galilean.offset);

    if (fromUnderlying == NULL) {
      ut_set_status(UT_OS);
      ut_handle_error_message(strerror(errno));
      ut_handle_error_message("galileanInitConverterFromProduct(): "
          "Couldn't get converter from underlying unit");
    }
    else {
      if (ENSURE_CONVERTER_FROM_PRODUCT(unit->galilean.unit)) {
          assert(unit->common.fromProduct == NULL);

          unit->common.fromProduct = cv_combine(
            unit->galilean.unit->common.fromProduct, fromUnderlying);

          if (unit->common.fromProduct == NULL) {
            ut_set_status(UT_OS);
            ut_handle_error_message(strerror(errno));
            ut_handle_error_message("galileanInitConverterFromProduct(): "
                "Couldn't combine converters");
          }
          else {
            retCode = 0;
          }
      }

      cv_free(fromUnderlying);
    }                   /* "fromUnderlying" allocated */

    return retCode;
}


static ut_status
galileanAcceptVisitor(
    const ut_unit* const            unit,
    const ut_visitor* const   visitor,
    void* const               arg)
{
    assert(unit != NULL);
    assert(IS_GALILEAN(unit));
    assert(visitor != NULL);

    return visitor->visit_galilean(unit, unit->galilean.scale,
      unit->galilean.unit, unit->galilean.offset, arg);
}


static UnitOps    galileanOps = {
    galileanGetProduct,
    galileanClone,
    galileanFree,
    galileanCompare,
    galileanMultiply,
    galileanRaise,
    galileanRoot,
    galileanInitConverterToProduct,
    galileanInitConverterFromProduct,
    galileanAcceptVisitor
};


/******************************************************************************
 * Timestamp Unit:
 ******************************************************************************/


static UnitOps    timestampOps;


/*
 * Returns a new unit instance.
 *
 * Arguments:
 *    unit  The underlying unit.  May be freed upon return.
 *    origin      The timestamp origin.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_OS         Operating-system error.  See "errno".
 *              UT_MEANINGLESS      Creation of a timestamp unit based on
 *                            "unit" is not meaningful.
 *              UT_NO_SECOND  The associated unit-system doesn't
 *                            contain a second unit.
 *    else  The newly-allocated, timestamp-unit.
 */
static ut_unit*
timestampNewOrigin(
    const ut_unit*      unit,
    const double  origin)
{
    ut_unit*    newUnit = NULL;         /* failure */
    ut_unit*    secondUnit;

    assert(unit != NULL);
    assert(!IS_TIMESTAMP(unit));

    secondUnit = unit->common.system->second;

    if (secondUnit == NULL) {
      ut_set_status(UT_NO_SECOND);
      ut_handle_error_message("galileanInitConverterFromProduct(): "
          "No \"second\" unit defined");
    }
    else if (ut_are_convertible(secondUnit, unit)) {
      TimestampUnit*    timestampUnit = malloc(sizeof(TimestampUnit));

      if (timestampUnit == NULL) {
          ut_set_status(UT_OS);
          ut_handle_error_message(strerror(errno));
          ut_handle_error_message("timestampNewOrigin(): "
            "Couldn't allocate %lu-byte timestamp-unit",
            sizeof(TimestampUnit));
      }
      else {
          if (commonInit(&timestampUnit->common, &timestampOps,
                unit->common.system, TIMESTAMP) == 0) {
            timestampUnit->origin = origin;
            timestampUnit->unit = CLONE(unit);
          }
          else {
            free(timestampUnit);
            timestampUnit = NULL;
          }
      }                 /* "timestampUnit" allocated */

      newUnit = (ut_unit*)timestampUnit;
    }                   /* "secondUnit != NULL" && time unit */

    return newUnit;
}


/*
 * Returns a new unit instance.
 *
 * Arguments:
 *    unit  The underlying unit.  May be freed upon return.
 *    year  The year of the origin.
 *    month The month of the origin.
 *    day   The day of the origin.
 *    hour  The hour of the origin.
 *    minute      The minute of the origin.
 *    second      The second of the origin.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_OS         Operating-system error.  See "errno".
 *              UT_MEANINGLESS      Creation of a timestamp unit base on
 *                            "unit" is not meaningful.
 *              UT_NO_SECOND  The associated unit-system doesn't
 *                            contain a unit named "second".
 *    else  The newly-allocated, timestamp-unit.
 */
static ut_unit*
timestampNew(
    ut_unit*      unit,
    const int     year,
    const int     month,
    const int     day,
    const int     hour,
    const int     minute,
    double  second)
{
    assert(unit != NULL);

    return timestampNewOrigin(
      unit, ut_encode_time(year, month, day, hour, minute, second));
}


static ProductUnit*
timestampGetProduct(
    const ut_unit* const      unit)
{
    assert(unit != NULL);
    assert(IS_TIMESTAMP(unit));

    return GET_PRODUCT(unit->timestamp.unit);
}


static ut_unit*
timestampClone(
    const ut_unit* const      unit)
{
    assert(unit != NULL);
    assert(IS_TIMESTAMP(unit));

    return timestampNewOrigin(unit->timestamp.unit, unit->timestamp.origin);
}


static int
timestampCompare(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    int     cmp;

    assert(unit1 != NULL);
    assert(IS_TIMESTAMP(unit1));
    assert(unit2 != NULL);

    if (!IS_TIMESTAMP(unit2)) {
      int   diff = unit1->common.type - unit2->common.type;

      cmp = diff < 0 ? -1 : diff == 0 ? 0 : 1;
    }
    else {
      const TimestampUnit* const    timestamp1 = &unit1->timestamp;
      const TimestampUnit* const    timestamp2 = &unit2->timestamp;

      cmp =
          timestamp1->origin < timestamp2->origin
            ? -1
            : timestamp1->origin == timestamp2->origin
                ? 0
                : -1;

      if (cmp == 0)
          cmp = COMPARE(timestamp1->unit, timestamp2->unit);
    }

    return cmp;
}


static void
timestampFree(
    ut_unit* const      unit)
{
    if (unit != NULL) {
      assert(IS_TIMESTAMP(unit));
      FREE(unit->timestamp.unit);
      unit->timestamp.unit = NULL;
      cv_free(unit->common.toProduct);
      unit->common.toProduct = NULL;
      cv_free(unit->common.fromProduct);
      unit->common.fromProduct = NULL;
      free((void*)unit);
    }
}


/*
 * Multiplies a timestamp-unit by another unit.  The origin is ignored.
 *
 * Arguments:
 *    unit1 The timestamp-unit.
 *    unit2 The other unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
timestampMultiply(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    assert(unit1 != NULL);
    assert(IS_TIMESTAMP(unit1));
    assert(unit2 != NULL);

    return MULTIPLY(unit1->timestamp.unit, unit2);
}


/*
 * Returns the result of raising a Timestamp-unit to a power.  The origin is
 * ignored.
 *
 * Arguments:
 *    unit  The Timestamp-unit.
 *    power The power.  Must be greater than -256 and less than 256.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
timestampRaise(
    const ut_unit* const      unit,
    const int                 power)
{
    assert(unit != NULL);
    assert(IS_TIMESTAMP(unit));
    assert(power != 0);
    assert(power != 1);

    return RAISE(unit->timestamp.unit, power);
}


/*
 * Returns the result of taking a root of a Timestamp-unit.  The origin is
 * ignored.
 *
 * Arguments:
 *    unit  The Timestamp-unit.
 *    root  The root.  Must be greater than 1 and less than 256.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
timestampRoot(
    const ut_unit* const      unit,
    const int                 root)
{
    assert(unit != NULL);
    assert(IS_TIMESTAMP(unit));
    assert(root > 1 && root < 256);

    return ROOT(unit->timestamp.unit, root);
}


/*
 * Initializes the converter of numeric values from the given Timestamp unit to
 * the underlying product-unit.
 *
 * Arguments:
 *    unit  The Timestamp unit.
 * Returns:
 *    -1    Failure.
 */
static int
timestampInitConverterToProduct(
    ut_unit* const      unit)
{
    /*
     * This function is never called.
     */
    assert(0);

    return -1;
}


/*
 * Initializes the converter of numeric values to the given Timestamp unit from
 * the underlying product-unit.
 *
 * Arguments:
 *    unit  The Timestamp unit.
 * Returns:
 *    -1    Failure.
 */
static int
timestampInitConverterFromProduct(
    ut_unit* const      unit)
{
    /*
     * This function is never called.
     */
    assert(0);

    return -1;
}


static ut_status
timestampAcceptVisitor(
    const ut_unit* const            unit,
    const ut_visitor* const   visitor,
    void* const               arg)
{
    assert(unit != NULL);
    assert(IS_TIMESTAMP(unit));
    assert(visitor != NULL);

    return visitor->visit_timestamp(unit, unit->timestamp.unit,
        unit->timestamp.origin, arg);
}


static UnitOps    timestampOps = {
    timestampGetProduct,
    timestampClone,
    timestampFree,
    timestampCompare,
    timestampMultiply,
    timestampRaise,
    timestampRoot,
    timestampInitConverterToProduct,
    timestampInitConverterFromProduct,
    timestampAcceptVisitor
};


/******************************************************************************
 * Logarithmic Unit:
 ******************************************************************************/


static UnitOps    logOps;


/*
 * Returns a new instance.
 *
 * Arguments:
 *    base        The logarithmic base (e.g., 2, M_E, 10).  Must be
 *                      greater than one.
 *    reference   The reference value.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_OS   Operating-system error.  See "errno".
 *    else  The newly-allocated, logarithmic-unit.
 */
static ut_unit*
logNew(
    const double        base,
    const ut_unit* const      reference)
{
    LogUnit*      logUnit;

    assert(base > 1);
    assert(reference != NULL);

    logUnit = malloc(sizeof(LogUnit));

    if (logUnit == NULL) {
      ut_set_status(UT_OS);
      ut_handle_error_message(strerror(errno));
      ut_handle_error_message(
          "logNew(): Couldn't allocate %lu-byte logarithmic-unit",
          sizeof(LogUnit));
    }
    else {
      if (commonInit(&logUnit->common, &logOps, reference->common.system,
            LOG) != 0) {
          free(logUnit);
      }
      else {
          logUnit->reference = CLONE(reference);

          if (logUnit->reference != NULL) {
            logUnit->base = base;
          }
          else {
            free(logUnit);
            logUnit = NULL;
          }
      }
    }

    return (ut_unit*)logUnit;
}


static ProductUnit*
logGetProduct(
    const ut_unit* const      unit)
{
    assert(unit != NULL);
    assert(IS_LOG(unit));

    return GET_PRODUCT(unit->log.reference);
}


static ut_unit*
logClone(
    const ut_unit* const      unit)
{
    assert(unit != NULL);
    assert(IS_LOG(unit));

    return logNew(unit->log.base, unit->log.reference);
}


static int
logCompare(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    int     cmp;

    assert(unit1 != NULL);
    assert(IS_LOG(unit1));
    assert(unit2 != NULL);

    if (!IS_LOG(unit2)) {
      int   diff = unit1->common.type - unit2->common.type;

      cmp = diff < 0 ? -1 : diff == 0 ? 0 : 1;
    }
    else {
      const LogUnit* const    u1 = &unit1->log;
      const LogUnit* const    u2 = &unit2->log;

      cmp = ut_compare(u1->reference, u2->reference);

      if (cmp == 0)
          cmp =
            u1->base < u2->base
                ? -1
                : u1->base == u2->base
                  ? 0
                  : 1;
    }

    return cmp;
}


static void
logFree(
    ut_unit* const      unit)
{
    if (unit != NULL) {
      assert(IS_LOG(unit));
      FREE(unit->log.reference);
      unit->log.reference = NULL;
      cv_free(unit->common.toProduct);
      unit->common.toProduct = NULL;
      cv_free(unit->common.fromProduct);
      unit->common.fromProduct = NULL;
      free((void*)unit);
    }
}


/*
 * Multiplies a logarithmic-unit by another unit.
 *
 * Arguments:
 *    unit1 The logarithmic-unit.
 *    unit2 The other unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
logMultiply(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    ut_unit*      result = NULL;          /* failure */

    assert(unit1 != NULL);
    assert(IS_LOG(unit1));
    assert(unit2 != NULL);

    if (!ut_is_dimensionless(unit2)) {
      ut_set_status(UT_MEANINGLESS);
      ut_handle_error_message("logMultiply(): Second unit not dimensionless");
    }
    else if (IS_BASIC(unit2) || IS_PRODUCT(unit2)) {
      result = CLONE(unit1);
    }
    else if (IS_GALILEAN(unit2)) {
      result = galileanNew(unit2->galilean.scale, unit1, 0);
    }
    else {
      ut_set_status(UT_MEANINGLESS);
      ut_handle_error_message("logMultiply(): can't multiply second unit");
    }

    return result;
}


/*
 * Returns the result of raising a logarithmic-unit to a power.
 *
 * Arguments:
 *    unit  The logarithmic-unit.
 *    power The power.  Must be greater than -256 and less than 256.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
logRaise(
    const ut_unit* const      unit,
    const int                 power)
{
    assert(unit != NULL);
    assert(IS_LOG(unit));
    assert(power != 0);
    assert(power != 1);

    /*
     * We don't know how to raise a logarithmic unit to a non-zero power.
     */
    ut_set_status(UT_MEANINGLESS);
    ut_handle_error_message(
      "logRaise(): Can't raise logarithmic-unit to non-zero power");

    return NULL;
}


/*
 * Returns the result of taking a root of a logarithmic-unit.
 *
 * Arguments:
 *    unit  The logarithmic-unit.
 *    root  The root.  Must be greater than 1 and less than 256.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_MEANINGLESS      The operation on the given units is
 *                            meaningless.
 *    else  The resulting unit.
 */
static ut_unit*
logRoot(
    const ut_unit* const      unit,
    const int                 root)
{
    assert(unit != NULL);
    assert(IS_LOG(unit));
    assert(root > 1 && root < 256);

    /*
     * We don't know how to take a root of a logarithmic unit.
     */
    ut_set_status(UT_MEANINGLESS);
    ut_handle_error_message(
      "logRoot(): Can't take a non-unity root of a logarithmic-unit");

    return NULL;
}


/*
 * Initializes the converter of numeric values from the given logarithmic unit
 * to the underlying product-unit.
 *
 * Arguments:
 *    unit  The logarithmic unit.
 * Returns:
 *    -1    Failure.  "ut_get_status()" will be:
 *              UT_OS   Operating-system fault.  See "errno".
 *    0     Success.
 */
static int
logInitConverterToProduct(
    ut_unit* const      unit)
{
    int                 retCode = -1;           /* failure */
    cv_converter* toUnderlying;

    assert(unit != NULL);
    assert(IS_LOG(unit));

    toUnderlying = cv_get_pow(unit->log.base);

    if (toUnderlying == NULL) {
      ut_set_status(UT_OS);
      ut_handle_error_message(strerror(errno));
      ut_handle_error_message("logInitConverterToProduct(): "
          "Couldn't get converter to underlying unit");
    }
    else {
      if (ENSURE_CONVERTER_TO_PRODUCT(unit->log.reference)) {
          assert(unit->common.toProduct == NULL);

          unit->common.toProduct = cv_combine(
            toUnderlying, unit->log.reference->common.toProduct);

          if (unit->common.toProduct == NULL) {
            ut_set_status(UT_OS);
            ut_handle_error_message(strerror(errno));
            ut_handle_error_message("logInitConverterToProduct(): "
                "Couldn't combine converters");
          }
          else {
            retCode = 0;
          }
      }

      cv_free(toUnderlying);
    }                   /* "toUnderlying" allocated */

    return retCode;
}


/*
 * Initializes the converter of numeric values to the given logarithmic unit
 * from the underlying product-unit.
 *
 * Arguments:
 *    unit  The logarithmic unit.
 * Returns:
 *    -1    Failure.  "ut_get_status()" will be:
 *              UT_OS         Operating-system fault.  See "errno".
 *    0     Success.
 */
static int
logInitConverterFromProduct(
    ut_unit* const      unit)
{
    int                 retCode = -1;           /* failure */
    cv_converter* fromUnderlying;

    assert(unit != NULL);
    assert(IS_LOG(unit));

    fromUnderlying = cv_get_log(unit->log.base);

    if (fromUnderlying == NULL) {
      ut_set_status(UT_OS);
      ut_handle_error_message(strerror(errno));
      ut_handle_error_message("logInitConverterFromProduct(): "
          "Couldn't get converter from underlying unit");
    }
    else {
      if (ENSURE_CONVERTER_FROM_PRODUCT(unit->log.reference)) {
          assert(unit->common.fromProduct == NULL);

          unit->common.fromProduct = cv_combine(
            unit->log.reference->common.fromProduct, fromUnderlying);

          if (unit->common.fromProduct == NULL) {
            ut_set_status(UT_OS);
            ut_handle_error_message(strerror(errno));
            ut_handle_error_message("logInitConverterFromProduct(): "
                "Couldn't combine converters");
          }
          else {
            retCode = 0;
          }
      }

      cv_free(fromUnderlying);
    }                   /* "fromUnderlying" allocated */

    return retCode;
}


static ut_status
logAcceptVisitor(
    const ut_unit* const            unit,
    const ut_visitor* const   visitor,
    void* const               arg)
{
    assert(unit != NULL);
    assert(IS_LOG(unit));
    assert(visitor != NULL);

    return visitor->visit_logarithmic(unit, unit->log.base, unit->log.reference,
      arg);
}


static UnitOps    logOps = {
    logGetProduct,
    logClone,
    logFree,
    logCompare,
    logMultiply,
    logRaise,
    logRoot,
    logInitConverterToProduct,
    logInitConverterFromProduct,
    logAcceptVisitor
};


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


/*
 * Returns a new unit-system.  On success, the unit-system will only contain
 * the dimensionless unit one.  See "ut_get_dimensionless_unit_one()".
 *
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_OS   Operating-system error.  See "errno".
 *    else  Pointer to a new unit system.
 */
ut_system*
ut_new_system(void)
{
    ut_system*    system = malloc(sizeof(ut_system));

    ut_set_status(UT_SUCCESS);

    if (system == NULL) {
      ut_set_status(UT_OS);
      ut_handle_error_message(strerror(errno));
      ut_handle_error_message(
          "ut_new_system(): Couldn't allocate %lu-byte unit-system",
          sizeof(ut_system));
    }
    else {
      system->second = NULL;
      system->basicUnits = NULL;
      system->basicCount = 0;

      system->one = (ut_unit*)productNew(system, NULL, NULL, 0);

      if (ut_get_status() != UT_SUCCESS) {
          ut_handle_error_message(
            "ut_new_system(): Couldn't create dimensionless unit one");
          free(system);
      }
    }

    return system;
}


/*
 * Frees resources associated with a unit-system by this module.
 *
 * Arguments:
 *    system            Pointer to the unit-system.
 */
void
coreFreeSystem(
    ut_system*    system)
{
    if (system != NULL) {
      int   i;

      for (i = 0; i < system->basicCount; ++i)
          basicFree((ut_unit*)system->basicUnits[i]);

      free(system->basicUnits);

      if (system->second != NULL)
          FREE(system->second);

      if (system->one != NULL)
          productReallyFree(system->one);

      free(system);
    }
}


/*
 * Returns the dimensionless-unit one of a unit-system.
 *
 * Arguments:
 *    system      Pointer to the unit-system for which the dimensionless-unit one
 *          will be returned.
 * Returns:
 *    NULL  Failure.  "utgetStatus()" will be:
 *              UT_BAD_ARG    "system" is NULL.
 *    else  Pointer to the dimensionless-unit one associated with "system".
 *          While not necessary, the pointer may be passed to ut_free()
 *          when the unit is no longer needed by the client.
 */
ut_unit*
ut_get_dimensionless_unit_one(
    const ut_system* const    system)
{
    ut_unit*      one;

    ut_set_status(UT_SUCCESS);

    if (system == NULL) {
      one = NULL;
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message(
          "ut_get_dimensionless_unit_one(): NULL unit-system argument");
    }
    else {
      one = system->one;
    }

    return one;
}


/*
 * Returns the unit-system to which a unit belongs.
 *
 * Arguments:
 *    unit  Pointer to the unit in question.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be
 *              UT_BAD_ARG    "unit" is NULL.
 *    else  Pointer to the unit-system to which "unit" belongs.
 */
ut_system*
ut_get_system(
    const ut_unit* const      unit)
{
    ut_system*    system;

    ut_set_status(UT_SUCCESS);

    if (unit == NULL) {
      system = NULL;
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_get_system(): NULL unit argument");
    }
    else {
      system = unit->common.system;
    }

    return system;
}


/*
 * Indicates if two units belong to the same unit-system.
 *
 * Arguments:
 *    unit1       Pointer to a unit.
 *    unit2       Pointer to another unit.
 * Returns:
 *    0           Failure or the units belong to different unit-systems.
 *                "ut_get_status()" will be
 *                    UT_BAD_ARG          "unit1" or "unit2" is NULL.
 *                    UT_SUCCESS          The units belong to different
 *                                  unit-systems.
 *    else        The units belong to the same unit-system.
 */
int
ut_same_system(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    int     sameSystem = 0;

    if (unit1 == NULL || unit2 == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_same_system(): NULL argument");
    }
    else {
      ut_set_status(UT_SUCCESS);
      sameSystem = unit1->common.system == unit2->common.system;
    }

    return sameSystem;
}


/*
 * Returns a new basic-unit that has been added to a unit-system.
 *
 * Arguments:
 *    system            The unit-system to which to add the new basic-unit.
 *    isDimensionless   Whether or not the basic-unit is dimensionless (e.g.,
 *                a radian).
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be
 *              UT_BAD_ARG          "system" is NULL.
 *              UT_OS         Operating-system error.  See "errno".
 *    else  Pointer to the new base-unit.
 */
static BasicUnit*
newBasicUnit(
    ut_system* const    system,
    const int           isDimensionless)
{
    BasicUnit*    basicUnit = NULL; /* failure */

    if (system == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("newBasicUnit(): NULL unit-system argument");
    }
    else {
      basicUnit = basicNew(system, isDimensionless, system->basicCount);

      if (basicUnit != NULL) {
          int           error = 1;
          BasicUnit*    save = (BasicUnit*)basicClone((ut_unit*)basicUnit);

          if (save == NULL) {
            ut_set_status(UT_OS);
            ut_handle_error_message(strerror(errno));
            ut_handle_error_message(
                "newBasicUnit(): Couldn't clone basic-unit");
          }
          else {
            BasicUnit** basicUnits = realloc(system->basicUnits,
                (system->basicCount+1)*sizeof(BasicUnit*));

            if (basicUnits == NULL) {
                ut_set_status(UT_OS);
                ut_handle_error_message(strerror(errno));
                ut_handle_error_message("newBasicUnit(): "
                  "Couldn't allocate %d-element basic-unit array",
                  system->basicCount+1);
            }
            else {
                basicUnits[system->basicCount++] = save;
                system->basicUnits = basicUnits;
                error = 0;
            }                 /* "system->basicUnits" re-allocated */

            if (error)
                basicFree((ut_unit*)save);
          }                   /* "save" allocated */

          if (error) {
            basicFree((ut_unit*)basicUnit);
            basicUnit = NULL;
          }
      }                       /* "basicUnit" allocated */
    }                         /* valid arguments */

    return basicUnit;
}


/*
 * Adds a base-unit to a unit-system.  Clients that use ut_read_xml() should not
 * normally need to call this function.
 *
 * Arguments:
 *    system      Pointer to the unit-system to which to add the new base-unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be
 *              UT_BAD_ARG          "system" or "name" is NULL.
 *              UT_OS         Operating-system error.  See "errno".
 *    else  Pointer to the new base-unit.  The pointer should be passed to
 *          ut_free() when the unit is no longer needed by the client (the
 *          unit will remain in the unit-system).
 */
ut_unit*
ut_new_base_unit(
    ut_system* const    system)
{
    BasicUnit*    basicUnit = newBasicUnit(system, 0);

    return (ut_unit*)basicUnit;
}


/*
 * Adds a dimensionless-unit to a unit-system.  In the SI system of units, the
 * derived-unit radian is a dimensionless-unit.  Clients that use ut_read_xml()
 * should not normally need to call this function.
 *
 * Arguments:
 *    system      Pointer to the unit-system to which to add the new
 *          dimensionless-unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be
 *              UT_BAD_ARG          "system" is NULL.
 *              UT_OS         Operating-system error.  See "errno".
 *    else  Pointer to the new dimensionless-unit.  The pointer should be
 *          passed to ut_free() when the unit is no longer needed by the
 *          client (the unit will remain in the unit-system).
 */
ut_unit*
ut_new_dimensionless_unit(
    ut_system* const    system)
{
    return (ut_unit*)newBasicUnit(system, 1);
}


/*
 * Sets the "second" unit of a unit-system.  This function must be called before
 * the first call to "ut_offset_by_time()". ut_read_xml() calls this function if the
 * resulting unit-system contains a unit named "second".
 *
 * Arguments:
 *    second            Pointer to the "second" unit.
 * Returns:
 *    UT_BAD_ARG  "second" is NULL.
 *    UT_EXISTS   The second unit of the unit-system to which "second"
 *                belongs is set to a different unit.
 *    UT_SUCCESS  Success.
 */
ut_status
ut_set_second(
    const ut_unit* const      second)
{
    ut_set_status(UT_SUCCESS);

    if (second == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message(
            "ut_set_second(): NULL \"second\" unit argument");
    }
    else {
      ut_system*  system = second->common.system;

      if (system->second == NULL) {
          system->second = CLONE(second);
      }
      else {
          if (ut_compare(system->second, second) != 0) {
            ut_set_status(UT_EXISTS);
            ut_handle_error_message(
                "ut_set_second(): Different \"second\" unit already "
                    "defined");
          }
      }
    }

    return ut_get_status();
}


/*
 * Compares two units.  Returns a value less than, equal to, or greater than
 * zero as the first unit is considered less than, equal to, or greater than
 * the second unit, respectively.  Units from different unit-systems never
 * compare equal.
 *
 * Arguments:
 *    unit1       Pointer to a unit or NULL.
 *    unit2       Pointer to another unit or NULL.
 * Returns:
 *    <0    The first unit is less than the second unit.
 *     0    The first and second units are equal or both units are NULL.
 *    >0    The first unit is greater than the second unit.
 */
int
ut_compare(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    int     cmp = 0;

    ut_set_status(UT_SUCCESS);

    if (unit1 == NULL) {
      cmp = unit2 != NULL ? -1 : 0;
    }
    else if (unit2 == NULL) {
      cmp = 1;
    }
    else if (unit1->common.system < unit2->common.system) {
      cmp = -1;
    }
    else if (unit1->common.system > unit2->common.system) {
      cmp = 1;
    }
    else {
      /*
       * NB: The comparison function is called if and only if the units
       * belong to the same unit-system.
       */
      cmp = COMPARE(unit1, unit2);
    }

    return cmp;
}


/*
 * Returns a unit equivalent to another unit scaled by a numeric factor,
 * e.g.,
 *    const ut_unit*    meter = ...
 *    const ut_unit*    kilometer = ut_scale(1000, meter);
 *
 * Arguments:
 *    factor            The numeric scale factor.
 *    unit        Pointer to the unit to be scaled.
 * Returns:
 *    NULL        Failure.  "ut_get_status()" will be
 *                    UT_BAD_ARG    "factor" is 0 or "unit" is NULL.
 *                    UT_OS         Operating-system error.  See
 *                                  "errno".
 *    else        Pointer to the resulting unit.  The pointer should be
 *                passed to ut_free() when the unit is no longer needed by
 *                the client.
 */
ut_unit*
ut_scale(
    const double        factor,
    const ut_unit* const      unit)
{
    ut_unit*            result = NULL;    /* failure */

    ut_set_status(UT_SUCCESS);

    if (unit == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_scale(): NULL unit argument");
    }
    else {
      if (factor == 0) {
          ut_set_status(UT_BAD_ARG);
          ut_handle_error_message("ut_scale(): NULL factor argument");
      }
      else {
          result = factor == 1
            ? CLONE(unit)
            : galileanNew(factor, unit, 0.0);
      }
    }

    return result;
}


/*
 * Returns a unit equivalent to another unit offset by a numeric amount,
 * e.g.,
 *    const ut_unit*    kelvin = ...
 *    const ut_unit*    celsius = ut_offset(kelvin, 273.15);
 *
 * Arguments:
 *    unit        Pointer to the unit to be offset.
 *    offset            The numeric offset.
 * Returns:
 *    NULL        Failure.  "ut_get_status()" will be
 *                    UT_BAD_ARG          "unit" is NULL.
 *                    UT_OS         Operating-system error.  See
 *                                  "errno".
 *    else        Pointer to the resulting unit.  The pointer should be
 *                passed to ut_free() when the unit is no longer needed by
 *                the client.
 */
ut_unit*
ut_offset(
    const ut_unit* const      unit,
    const double        offset)
{
    ut_unit*            result = NULL;    /* failure */

    ut_set_status(UT_SUCCESS);

    if (unit == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_offset(): NULL unit argument");
    }
    else {
      result = offset == 0
          ? CLONE(unit)
          : galileanNew(1.0, unit, offset);
    }

    return result;
}


/*
 * Returns a unit equivalent to another unit relative to a particular time.
 * e.g.,
 *    const ut_unit*    second = ...
 *    const ut_unit*    secondsSinceTheEpoch =
 *              ut_offset_by_time(second, ut_encode_time(1970, 1, 1, 0, 0, 0.0));
 *
 * Arguments:
 *    unit  Pointer to the time-unit to be made relative to a time-origin.
 *    origin      The origin as returned by ut_encode_time().
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be
 *              UT_BAD_ARG          "unit" is NULL.
 *              UT_OS         Operating-system error.  See "errno".
 *              UT_MEANINGLESS      Creation of a timestamp unit based on
 *                            "unit" is not meaningful.
 *              UT_NO_SECOND  The associated unit-system doesn't
 *                            contain a "second" unit.  See
 *                            ut_set_second().
 *    else  Pointer to the resulting unit.  The pointer should be passed
 *          to ut_free() when the unit is no longer needed by the client.
 */
ut_unit*
ut_offset_by_time(
    const ut_unit* const      unit,
    const double        origin)
{
    ut_unit*            result = NULL;    /* failure */

    ut_set_status(UT_SUCCESS);

    if (unit == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_offset_by_time(): NULL unit argument");
    }
    else {
      result = timestampNewOrigin(unit, origin);
    }

    return result;
}


/*
 * Returns the result of multiplying one unit by another unit.
 *
 * Arguments:
 *    unit1 Pointer to a unit.
 *    unit2 Pointer to another unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_BAD_ARG          "unit1" or "unit2" is NULL.
 *              UT_NOT_SAME_SYSTEM  "unit1" and "unit2" belong to
 *                            different unit-systems.
 *              UT_OS         Operating-system error. See "errno".
 *    else  Pointer to the resulting unit.  The pointer should be passed
 *          to ut_free() when the unit is no longer needed by the client.
 */
ut_unit*
ut_multiply(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    ut_unit*      result = NULL;    /* failure */

    ut_set_status(UT_SUCCESS);

    if (unit1 == NULL || unit2 == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_multiply(): NULL argument");
    }
    else if (unit1->common.system != unit2->common.system) {
      ut_set_status(UT_NOT_SAME_SYSTEM);
      ut_handle_error_message(
            "ut_multiply(): Units in different unit-systems");
    }
    else {
      result = MULTIPLY(unit1, unit2);
    }

    return result;
}


/*
 * Returns the inverse (i.e., reciprocal) of a unit.  This convenience function
 * is equal to "ut_raise(unit, -1)".
 *
 * Arguments:
 *    unit  Pointer to the unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_BAD_ARG          "unit" is NULL.
 *              UT_OS         Operating-system error. See "errno".
 *    else  Pointer to the resulting unit.  The pointer should be passed to
 *          ut_free() when the unit is no longer needed by the client.
 */
ut_unit*
ut_invert(
    const ut_unit* const      unit)
{
    return ut_raise(unit, -1);
}


/*
 * Returns the result of dividing one unit by another unit.  This convenience
 * function is equivalent to the following sequence:
 *     {
 *         ut_unit* inverse = ut_invert(denom);
 *         ut_multiply(numer, inverse);
 *         ut_free(inverse);
 *     }
 *
 * Arguments:
 *    numer Pointer to the numerator (top, dividend) unit.
 *    denom Pointer to the denominator (bottom, divisor) unit.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_BAD_ARG          "numer" or "denom" is NULL.
 *              UT_NOT_SAME_SYSTEM  "unit1" and "unit2" belong to
 *                            different unit-systems.
 *              UT_OS         Operating-system error. See "errno".
 *    else  Pointer to the resulting unit.  The pointer should be passed to
 *          ut_free() when the unit is no longer needed by the client.
 */
ut_unit*
ut_divide(
    const ut_unit* const      numer,
    const ut_unit* const      denom)
{
    ut_unit*      result = NULL;          /* failure */

    ut_set_status(UT_SUCCESS);

    if (numer == NULL || denom == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_divide(): NULL argument");
    }
    else if (numer->common.system != denom->common.system) {
      ut_set_status(UT_NOT_SAME_SYSTEM);
      ut_handle_error_message("ut_divide(): Units in different unit-systems");
    }
    else {
      ut_unit*    inverse = RAISE(denom, -1);

      if (inverse != NULL) {
          result = MULTIPLY(numer, inverse);

          ut_free(inverse);
      }
    }

    return result;
}


/*
 * Returns the result of raising a unit to a power.
 *
 * Arguments:
 *    unit  Pointer to the unit.
 *    power The power by which to raise "unit".  Must be greater than or 
 *          equal to -255 and less than or equal to 255.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_BAD_ARG          "unit" is NULL, or "power" is invalid.
 *              UT_OS         Operating-system error. See "errno".
 *    else  Pointer to the resulting unit.  The pointer should be passed to
 *          ut_free() when the unit is no longer needed by the client.
 */
ut_unit*
ut_raise(
    const ut_unit* const      unit,
    const int                 power)
{
    ut_unit*            result = NULL;    /* failure */

    ut_set_status(UT_SUCCESS);

    if (unit == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_raise(): NULL unit argument");
    }
    else if (power < -255 || power > 255) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_raise(): Invalid power argument");
    }
    else {
      result = 
          power == 0
            ? unit->common.system->one
            : power == 1
                ? CLONE(unit)
                : RAISE(unit, power);
    }

    return result;
}


/*
 * Returns the result of taking the root of a unit.
 *
 * Arguments:
 *    unit  Pointer to the unit.
 *    root  The root to take of "unit".  Must be greater than or 
 *          equal to 1 and less than or equal to 255.
 * Returns:
 *    NULL  Failure.  "ut_get_status()" will be:
 *              UT_BAD_ARG          "unit" is NULL, or "root" is invalid.
 *                                  In particular, all powers of base units
 *                                  in "unit" must be integral multiples of
 *                                  "root".
 *              UT_OS         Operating-system error. See "errno".
 *    else  Pointer to the resulting unit.  The pointer should be passed to
 *          ut_free() when the unit is no longer needed by the client.
 */
ut_unit*
ut_root(
    const ut_unit* const      unit,
    const int                 root)
{
    ut_unit*            result = NULL;    /* failure */

    ut_set_status(UT_SUCCESS);

    if (unit == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_root(): NULL unit argument");
    }
    else if (root < 1 || root > 255) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_root(): Invalid root argument");
    }
    else {
      result = 
          root == 1
            ? CLONE(unit)
                : ROOT(unit, root);
    }

    return result;
}


/*
 * Returns the logarithmic unit corresponding to a logarithmic base and a
 * reference level.  For example, the following creates a decibel unit with a
 * one milliwatt reference level:
 *
 *     const ut_unit* watt = ...;
 *     const ut_unit* milliWatt = ut_scale(0.001, watt);
 *
 *     if (milliWatt != NULL) {
 *         const ut_unit* bel_1_mW = ut_log(10.0, milliWatt);
 *
 *         if (bel_1_mW != NULL) {
 *             const ut_unit* decibel_1_mW = ut_scale(0.1, bel_1_mW);
 *
 *             if (decibel_1_mW != NULL) {
 *                 ...
 *                 ut_free(decibel_1_mW);
 *             }              // "decibel_1_mW" allocated
 *
 *             ut_free(bel_1_mW);
 *         }                        // "bel_1_mW" allocated
 *
 *         ut_free(milliWatt);
 *     }                      // "milliWatt" allocated
 *
 * Arguments:
 *    base        The logarithmic base (e.g., 2, M_E, 10).  Must be
 *                      greater than one.  "M_E" is defined in <math.h>.
 *    reference   Pointer to the reference value as a unit.
 * Returns:
 *    NULL        Failure.  "ut_get_status()" will be:
 *                    UT_BAD_ARG    "base" is invalid or "reference"
 *                                              is NULL.
 *                    UT_OS         Operating-system error. See
 *                                  "errno".
 *    else        Pointer to the resulting unit.  The pointer should be
 *                passed to ut_free() when the unit is no longer needed by
 *                the client.
 */
ut_unit*
ut_log(
    const double        base,
    const ut_unit* const      reference)
{
    ut_unit*            result = NULL;    /* failure */

    ut_set_status(UT_SUCCESS);

    if (base <= 1) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_log(): Invalid logarithmic base, %g", base);
    }
    else if (reference == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_log(): NULL reference argument");
    }
    else {
      result = logNew(base, reference);
    }

    return result;
}


/*
 * Indicates if numeric values in one unit are convertible to numeric values in
 * another unit via "ut_get_converter()".  In making this determination, 
 * dimensionless units are ignored.
 *
 * Arguments:
 *    unit1       Pointer to a unit.
 *    unit2       Pointer to another unit.
 * Returns:
 *    0           Failure.  "ut_get_status()" will be
 *                    UT_BAD_ARG          "unit1" or "unit2" is NULL.
 *                    UT_NOT_SAME_SYSTEM  "unit1" and "unit2" belong to
 *                                  different unit-sytems.
 *                    UT_SUCCESS          Conversion between the units is
 *                                  not possible (e.g., "unit1" is
 *                                  "meter" and "unit2" is
 *                                  "kilogram").
 *    else  Numeric values can be converted between the units.
 */
int
ut_are_convertible(
    const ut_unit* const      unit1,
    const ut_unit* const      unit2)
{
    int                 areConvertible = 0;

    if (unit1 == NULL || unit2 == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_are_convertible(): NULL unit argument");
    }
    else if (unit1->common.system != unit2->common.system) {
      ut_set_status(UT_NOT_SAME_SYSTEM);
      ut_handle_error_message(
          "ut_are_convertible(): Units in different unit-systems");
    }
    else {
      ut_set_status(UT_SUCCESS);

      if (IS_TIMESTAMP(unit1) || IS_TIMESTAMP(unit2)) {
          areConvertible = IS_TIMESTAMP(unit1) && IS_TIMESTAMP(unit2);
      }
      else {
          ProductRelationship relationship =
            productRelationship(GET_PRODUCT(unit1), GET_PRODUCT(unit2));

          areConvertible = relationship == PRODUCT_EQUAL ||
            relationship == PRODUCT_INVERSE;
      }
    }

    return areConvertible;
}


/*
 * Returns a converter of numeric values in one unit to numeric values in
 * another unit.  The returned converter should be passed to cv_free() when it
 * is no longer needed by the client.
 *
 * NOTE:  Leap seconds are not taken into account when converting between
 * timestamp units.
 *
 * Arguments:
 *    from        Pointer to the unit from which to convert values.
 *    to          Pointer to the unit to which to convert values.
 * Returns:
 *    NULL        Failure.  "ut_get_status()" will be:
 *                    UT_BAD_ARG          "from" or "to" is NULL.
 *                    UT_NOT_SAME_SYSTEM  "from" and "to" belong to
 *                                  different unit-systems.
 *                    UT_MEANINGLESS      Conversion between the units is
 *                                  not possible.  See
 *                                  "ut_are_convertible()".
 *    else        Pointer to the appropriate converter.  The pointer
 *                should be passed to cv_free() when no longer needed by
 *                the client.
 */
cv_converter*
ut_get_converter(
    ut_unit* const      from,
    ut_unit* const      to)
{
    cv_converter* converter = NULL; /* failure */

    if (from == NULL || to == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_get_converter(): NULL unit argument");
    }
    else if (from->common.system != to->common.system) {
      ut_set_status(UT_NOT_SAME_SYSTEM);
      ut_handle_error_message(
          "ut_get_converter(): Units in different unit-systems");
    }
    else {
      ut_set_status(UT_SUCCESS);

      if (!IS_TIMESTAMP(from) && !IS_TIMESTAMP(to)) {
          ProductRelationship relationship =
            productRelationship(GET_PRODUCT(from), GET_PRODUCT(to));

          if (relationship == PRODUCT_UNCONVERTIBLE) {
            ut_set_status(UT_MEANINGLESS);
            ut_handle_error_message(
                    "ut_get_converter(): Units not convertible");
          }
          else if (ENSURE_CONVERTER_TO_PRODUCT(from) &&
                  ENSURE_CONVERTER_FROM_PRODUCT(to)) {
            if (relationship == PRODUCT_EQUAL) {
                converter = cv_combine(
                  from->common.toProduct, to->common.fromProduct);
            }
            else {
                /*
                 * The underlying product-units are reciprocals of each
                 * other.
                 */
                cv_converter* invert = cv_get_inverse();

                if (invert != NULL) {
                  cv_converter*     phase1 =
                      cv_combine(from->common.toProduct, invert);

                  if (phase1 != NULL) {
                      converter =
                        cv_combine(phase1, to->common.fromProduct);

                      cv_free(phase1);
                  }           /* "phase1" allocated */

                  cv_free(invert);
                }             /* "invert" allocated */
            }                 /* reciprocal product-units */

            if (converter == NULL) {
                ut_set_status(UT_OS);
                ut_handle_error_message(strerror(errno));
                ut_handle_error_message(
                  "ut_get_converter(): Couldn't get converter");
            }
          }                   /* got necessary product converters */
      }                       /* neither unit is a timestamp */
      else {
          cv_converter* toSeconds =
                ut_get_converter(from->timestamp.unit,
                    from->common.system->second);

          if (toSeconds == NULL) {
            ut_set_status(UT_OS);
            ut_handle_error_message(strerror(errno));
            ut_handle_error_message(
                "ut_get_converter(): Couldn't get converter to seconds");
          }
          else {
            cv_converter*     shiftOrigin =
                cv_get_offset(
                  from->timestamp.origin - to->timestamp.origin);

            if (shiftOrigin == NULL) {
                ut_set_status(UT_OS);
                ut_handle_error_message(strerror(errno));
                ut_handle_error_message(
                  "ut_get_converter(): Couldn't get offset-converter");
            }
            else {
                cv_converter* toToUnit =
                  cv_combine(toSeconds, shiftOrigin);

                if (toToUnit == NULL) {
                  ut_set_status(UT_OS);
                  ut_handle_error_message(strerror(errno));
                  ut_handle_error_message(
                      "ut_get_converter(): Couldn't combine converters");
                }
                else {
                  cv_converter*     fromSeconds = ut_get_converter(
                      to->common.system->second, to->timestamp.unit); 

                  if (fromSeconds == NULL) {
                      ut_set_status(UT_OS);
                      ut_handle_error_message(strerror(errno));
                      ut_handle_error_message(
                        "ut_get_converter(): Couldn't get converter "
                        "from seconds");
                  }
                  else {
                      converter = cv_combine(toToUnit, fromSeconds);

                      if (converter == NULL) {
                        ut_set_status(UT_OS);
                        ut_handle_error_message(strerror(errno));
                        ut_handle_error_message("ut_get_converter(): "
                            "Couldn't combine converters");
                      }

                      cv_free(fromSeconds);
                  }           /* "fromSeconds" allocated */

                  cv_free(toToUnit);
                }             /* "toToUnit" allocated */

                cv_free(shiftOrigin);
            }                 /* "shiftOrigin" allocated */

            cv_free(toSeconds);
          }                   /* "toSeconds" allocated */
      }                       /* units are timestamps */
    }                         /* valid arguments */

    return converter;
}


/*
 * Indicates if a given unit is dimensionless or not.  Note that logarithmic
 * units are dimensionless by definition.
 *
 * Arguments:
 *    unit  Pointer to the unit in question.
 * Returns:
 *    0     "unit" is dimensionfull or an error occurred.  "ut_get_status()"
 *           will be
 *              UT_BAD_ARG          "unit" is NULL.
 *              UT_SUCCESS          "unit" is dimensionfull.
 *    else  "unit" is dimensionless.
 */
int
ut_is_dimensionless(
    const ut_unit* const      unit)
{
    int           isDimensionless = 0;

    ut_set_status(UT_SUCCESS);

    if (unit == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_is_dimensionless(): NULL unit argument");
    }
    else {
      /*
       * Special case logarithmic units because logGetProduct() can be
       * dimensionfull.
       */
      isDimensionless =
          IS_LOG(unit)
            ? 1
            : productIsDimensionless(GET_PRODUCT(unit));
    }

    return isDimensionless;
}


/*
 * Returns a clone of a unit.
 *
 * Arguments:
 *    unit  Pointer to the unit to be cloned.
 * Returns:
 *    NULL  Failure.  ut_get_status() will be
 *              UT_OS   Operating-system failure.  See "errno".
 *              UT_BAD_ARG    "unit" is NULL.
 *    else  Pointer to the clone of "unit".  The pointer should be
 *          passed to ut_free() when the unit is no longer needed by the
 *          client.
 */
ut_unit*
ut_clone(
    const ut_unit* const      unit)
{
    ut_unit*      clone = NULL;           /* failure */

    ut_set_status(UT_SUCCESS);

    if (unit == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_clone(): NULL unit argument");
    }
    else {
      clone =
          unit == unit->common.system->one
            ? (ut_unit*)unit
            : CLONE(unit);
    }

    return clone;
}


/*
 * Frees resources associated with a unit.  This function should be invoked on
 * all units that are no longer needed by the client.  Use of the unit upon
 * return from this function will result in undefined behavior.
 *
 * Arguments:
 *    unit  Pointer to the unit to have its resources freed or NULL.
 */
void
ut_free(
    ut_unit* const      unit)
{
    ut_set_status(UT_SUCCESS);

    if (unit != NULL) {
      if (unit != unit->common.system->one)
          FREE(unit);
    }
}


/*
 * Accepts a visitor to a unit.
 *
 * Arguments:
 *    unit        Pointer to the unit to accept the visitor.
 *    visitor           Pointer to the visitor of "unit".
 *    arg         An arbitrary pointer that will be passed to "visitor".
 * Returns:
 *    UT_BAD_ARG  "unit" or "visitor" is NULL.
 *    UT_VISIT_ERROR    A error occurred in "visitor" while visiting "unit".
 *    UT_SUCCESS  Success.
 */
ut_status
ut_accept_visitor(
    const ut_unit* const      unit,
    const ut_visitor* const   visitor,
    void* const               arg)
{
    ut_set_status(UT_SUCCESS);

    if (unit == NULL || visitor == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("ut_accept_visitor(): NULL argument");
    }
    else {
      ut_set_status(ACCEPT_VISITOR(unit, visitor, arg));
    }

    return ut_get_status();
}

Generated by  Doxygen 1.6.0   Back to index