netcdf-c/udunits/lib/unitcore.c
2010-06-03 13:24:43 +00:00

3809 lines
88 KiB
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;
struct ut_system {
ut_unit* second;
ut_unit* one; /* the dimensionless-unit one */
BasicUnit** basicUnits;
int basicCount;
};
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))
typedef struct {
ut_system* system;
const UnitOps* ops;
UnitType type;
cv_converter* toProduct;
cv_converter* fromProduct;
} Common;
struct BasicUnit {
Common common;
ProductUnit* product; /* equivalent product-unit */
int index; /* system->basicUnits index */
int isDimensionless;
};
struct ProductUnit {
Common common;
short* indexes;
short* powers;
int count;
};
typedef struct {
Common common;
ut_unit* unit;
double scale;
double offset;
} GalileanUnit;
typedef struct {
Common common;
ut_unit* unit;
double origin;
} TimestampUnit;
typedef struct {
Common common;
ut_unit* reference;
double base;
} LogUnit;
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);
static long juldayOrigin = 0;
/*
* 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;
}
/*
* 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)
{
if (juldayOrigin == 0)
juldayOrigin = gregorianDateToJulianDay(2001, 1, 1);
return 86400.0 *
(gregorianDateToJulianDay(year, month, day) - juldayOrigin);
}
/*
* 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 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(juldayOrigin + 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);
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);
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);
}
if (count > 0)
free(basicUnits);
}
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);
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);
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);
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();
}