diff options
Diffstat (limited to 'ast/unit.c')
-rw-r--r-- | ast/unit.c | 6218 |
1 files changed, 6218 insertions, 0 deletions
diff --git a/ast/unit.c b/ast/unit.c new file mode 100644 index 0000000..bec4b0a --- /dev/null +++ b/ast/unit.c @@ -0,0 +1,6218 @@ +/* +* Name: +* unit.c + +* Purpose: +* Implement unit conversion functions. + +* Description: +* This file implements the Unit module which is used for identifying +* units and transforming between them. It follows the recommendations +* for unit handling contained within FITS WCS paper I (Greisen & +* Calabretta). All methods have protected access. + +* Methods: +* astUnitMapper: Create a Mapping between two systems of units. +* astUnitLabel: Returns a label for a given unit symbol. + +* Copyright: +* Copyright (C) 1997-2006 Council for the Central Laboratory of the +* Research Councils + +* Licence: +* This program is free software: you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation, either +* version 3 of the License, or (at your option) any later +* version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General +* License along with this program. If not, see +* <http://www.gnu.org/licenses/>. + +* Authors: +* DSB: D.S. Berry (Starlink) + +* History: +* 10-DEC-2002 (DSB): +* Original version. +* 10-FEB-2004 (DSB): +* Added debug conditional code to keep track of memory leaks. +* 15-JUL-2004 (DSB): +* In astUnitMapper: if no Mapping can be found from input to +* output units (e.g. because fo the less than perfect simplication +* algorithm in SimplifyTree), try finding a Mapping from output to +* input units and inverting the result. +* 14-DEC-2004 (DSB): +* In CreateTree, move the invocation of FixConstants from after +* InvertConstants to before InvertConstants. This is because +* InvertConstants ignores nodes which contain all constant +* arguments. This results in constants not being inverted in +* expressions such as "1/60 deg" (because all arguments are +* constant in the the "1/60" node). +* 18-JAN-2005 (DSB): +* Fix memory leaks. +* 2-FEB-2005 (DSB): +* - Avoid using astStore to allocate more storage than is supplied +* in the "data" pointer. This can cause access violations since +* astStore will then read beyond the end of the "data" area. +* 15-FEB-2005 (DSB): +* - Modified CleanExp to fix up some common units mistakes. +* 21-FEB-2005 (DSB): +* - Modified CleanExp to accept <word><digit> as equivalent to +* <word>^<digit>. +* - Modified MakeTree to do case insensitive checking if case +* sensitive checking failsto produce a match to a multiplier/unit +* symbol combination. If this still produces no match, do a case +* insensitive match for multiplier/unit label. +* 1-MAR-2006 (DSB): +* Replace astSetPermMap within DEBUG blocks by astBeginPM/astEndPM. +* 6-APR-2006 (DSB): +* Modify CleanExp to convert "MJY/STER" to standard form ("MJy/sr"). +* 7-JUL-2006 (DSB): +* Correct initialisation of "word" flag in CleanExp. +* 17-MAY-2007 (DSB): +* Simplify the units string returned by astUnitNormaliser. +* - Fix indexing bug in CombineFactors. +* 26-MAY-2007 (DSB): +* - Correct error reporting in astUNitNormaliser. +* - Fix bug in CleanExp that caused (for example) "-2" to be +* converted to "^-2". +* 2-DEC-2008 (DSB): +* Correct memory allocation bug in CleanExp. +* 6-MAY-2011 (DSB): +* Include "adu" as basic unit. +* 9-MAY-2011 (DSB): +* Change "A" to be Ampere (as defined by FITS-WCS paper 1) rather +* than "Angstrom". +*/ + +/* Module Macros. */ +/* ============== */ +/* Define the astCLASS macro (even although this is not a class + implementation) to obtain access to the protected error and memory + handling functions. */ +#define astCLASS +#define PI 3.141592653589793 +#define E 2.718281828459045 + +/* Macro which returns the nearest integer to a given floating point + value. */ +#define NINT(x) (int)((x)+(((x)>0.0)?0.5:-0.5)) + +/* Macro identifying a character as lower or upper case letter, digit or ++ or -. */ +#define ISWORD(c) (isalnum(c)||((c)=='+')||((c)=='-')) + +/* The number of basic dimension quantities used for dimensional analysis. + In addition to the usual M, L and T, this includes pseudo-dimensions + describing strictly dimensionless quantities such as plane angle, + magnitude, etc. */ +#define NQUANT 10 + +/* Include files. */ +/* ============== */ +/* Interface definitions. */ +/* ---------------------- */ +#include "error.h" +#include "memory.h" +#include "pointset.h" +#include "mapping.h" +#include "unitmap.h" +#include "zoommap.h" +#include "mathmap.h" +#include "unit.h" + +/* Error code definitions. */ +/* ----------------------- */ +#include "ast_err.h" /* AST error codes */ + +/* C header files. */ +/* --------------- */ +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <limits.h> +#include <math.h> + +#ifdef THREAD_SAFE +#include <pthread.h> +#endif + +/* Module Type Definitions. */ +/* ======================== */ + +/* This declaration enumerates the codes for the operations which may + legally be included in a units expression. */ +typedef enum { + OP_LDCON, /* Load constant */ + OP_LDVAR, /* Load variable */ + OP_LOG, /* Common logarithm */ + OP_LN, /* Natural logarithm */ + OP_EXP, /* Natural exponential */ + OP_SQRT, /* Square root */ + OP_POW, /* Exponentiate */ + OP_DIV, /* Division */ + OP_MULT, /* Multiplication */ + OP_LDPI, /* Load constant PI */ + OP_LDE, /* Load constant E */ + OP_NULL /* Null operation */ +} Oper; + +/* A structure describing a standard multiplier prefix. */ +typedef struct Multiplier { + const char *label; /* Multipler label string (null terminated) */ + const char *sym; /* Multipler symbol string (null terminated) */ + int symlen; /* Length of symbol (without trailing null ) */ + int lablen; /* Length of label (without trailing null ) */ + double scale; /* The scale factor associated with the prefix */ + struct Multiplier *next; /* Next Multiplier in linked list */ +} Multiplier; + +/* A structure describing a single node in a tree representation of a + single units expression. */ +typedef struct UnitNode { + Oper opcode; /* Code for operation performed by this node */ + int narg; /* No. of arguments used by the operation */ + struct UnitNode **arg; /* Array of pointers to argument nodes */ + double con; /* Constant to be loaded by OP_LDCON operations */ + struct KnownUnit *unit; /* Known unit referred to by OP_LDVAR nodes */ + Multiplier *mult; /* Multiplier used by OP_LDVAR nodes */ + const char *name; /* User-defined unit referred to by OP_LDVAR + nodes (no multiplier prefix included) */ +} UnitNode; + +/* A structure describing a known unit. */ +typedef struct KnownUnit { + const char *sym; /* Unit symbol string (null terminated) */ + const char *label; /* Unit label string (null terminated) */ + int symlen; /* Length of symbol (without trailing null ) */ + int lablen; /* Length of label (without trailing null ) */ + struct UnitNode *head; /* Head of definition tree (NULL for basic units) */ + struct KnownUnit *next; /* Next KnownUnit in linked list */ + struct KnownUnit *use; /* KnownUnit to be used in place of this one */ +} KnownUnit; + +/* Module Variables. */ +/* ================= */ + +/* A pointer to the KnownUnit structure at the head of a linked list of + such structures containing definitions of all known units. */ +static KnownUnit *known_units = NULL; + +/* An array of pointers to KnownUnits which list the basic quantities +used in dimensional analysis. */ +static KnownUnit *quant_units[ NQUANT ]; + +/* A pointer to the Multiplier structure at the head of a linked list of + such structures containing definitions of all known multipliers. */ +static Multiplier *multipliers = NULL; + +/* Set up mutexes */ +#ifdef THREAD_SAFE + +static pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX1 pthread_mutex_lock( &mutex1 ); +#define UNLOCK_MUTEX1 pthread_mutex_unlock( &mutex1 ); + +static pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; +#define LOCK_MUTEX2 pthread_mutex_lock( &mutex2 ); +#define UNLOCK_MUTEX2 pthread_mutex_unlock( &mutex2 ); + +#else + +#define LOCK_MUTEX1 +#define UNLOCK_MUTEX1 + +#define LOCK_MUTEX2 +#define UNLOCK_MUTEX2 + +#endif + +/* Prototypes for Private Functions. */ +/* ================================= */ +static AstMapping *MakeMapping( UnitNode *, int * ); +static KnownUnit *GetKnownUnits( int, int * ); +static Multiplier *GetMultipliers( int * ); +static UnitNode *ConcatTree( UnitNode *, UnitNode *, int * ); +static UnitNode *CopyTree( UnitNode *, int * ); +static UnitNode *CreateTree( const char *, int, int, int * ); +static UnitNode *FixUnits( UnitNode *, UnitNode *, int * ); +static UnitNode *FreeTree( UnitNode *, int * ); +static UnitNode *MakeTree( const char *, int, int, int * ); +static UnitNode *MakeLabelTree( const char *, int, int * ); +static UnitNode *NewNode( UnitNode *, Oper, int * ); +static UnitNode *CombineFactors( UnitNode **, double *, int, double, int * ); +static const char *CleanExp( const char *, int * ); +static int EndsWith( const char *, int, const char *, int * ); +static int CmpTree( UnitNode *, UnitNode *, int, int * ); +static void FixConstants( UnitNode **, int, int * ); +static void InvertConstants( UnitNode **, int * ); +static UnitNode *InvertTree( UnitNode *, UnitNode *, int * ); +static void LocateUnits( UnitNode *, UnitNode ***, int *, int * ); +static void MakeKnownUnit( const char *, const char *, const char *, int * ); +static void MakeUnitAlias( const char *, const char *, int * ); +static void RemakeTree( UnitNode **, int * ); +static int SimplifyTree( UnitNode **, int, int * ); +static int ComplicateTree( UnitNode **, int * ); +static int ReplaceNode( UnitNode *, UnitNode *, UnitNode *, int * ); +static void FindFactors( UnitNode *, UnitNode ***, double **, int *, double *, int * ); +static const char *MakeExp( UnitNode *, int, int, int * ); +static int DimAnal( UnitNode *, double[NQUANT], double *, int * ); +static int Ustrcmp( const char *, const char *, int * ); +static int Ustrncmp( const char *, const char *, size_t, int * ); +static int SplitUnit( const char *, int, const char *, int, Multiplier **, int *, int * ); +static UnitNode *ModifyPrefix( UnitNode *, int * ); +static int ConStart( const char *, double *, int *, int * ); + +/* Debug functions... +static const char *DisplayTree( UnitNode *, int ); +static void OpSym( UnitNode *, char * ); +static const char *OpName( Oper ); +static const char *TreeExp( UnitNode * ); +*/ + +/* Function implementations. */ +/* ========================= */ +static const char *CleanExp( const char *exp, int *status ) { +/* +* Name: +* CleanExp + +* Purpose: +* Produce a clean copy of a units expression. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* const char *CleanExp( const char *exp ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a pointer to dynamic memory containing a +* cleaned copy of the supplied units expression. Cleaning consists of +* the following operations: +* - removal of leading and trailing white space. +* - replacement of multiple adjacent spaces by a single space +* - removal of spaces adjacent to a parenthesis +* - removal of spaces adjacent to a binary operator +* - translates various common non-standard units into equivalent +* standard units. +* +* Such carefull handling of spaces is necessary since a space is +* recognised by the MakeTree function as a multiplication operator. + +* Parameters: +* exp +* A pointer to the expression to be cleaned. + +* Returned Value: +* A pointer to the cleaned expression, which should be freed using +* astFree when no longer needed. + +* Notes: +* - This function returns NULL if it is invoked with the global error +* status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + char **tok; + char *p; + char *r; + char *result; + char *s; + char *t; + char *w; + const char *start; + int i; + int l; + int len; + char *tt; + int ntok; + int po; + int ps; + int word; + +/* Initialise */ + result = NULL; + +/* Check inherited status */ + if( !astOK ) return result; + +/* Split the supplied string up into tokens. Each block of contiguous + alphanumeric characters is a token. Each contiguous block of + non-alphanumerical characters is also a token. The + and - signs are + counted as alphanumeric. */ + start = exp; + p = (char *) exp - 1; + word = ISWORD( *( p + 1 ) ); + ntok = 0; + tok = NULL; + while( *(++p) ){ + if( word ) { + if( !ISWORD( *p ) ) { + l = p - start; + t = astStore( NULL, start, l + 1 ); + if( t ) t[ l ] = 0; + tok = astGrow( tok, ntok + 1, sizeof( char * ) ); + if( tok ) tok[ ntok++ ] = t; + start = p; + word = 0; + } + } else { + if( ISWORD( *p ) ) { + l = p - start; + t = astStore( NULL, start, l + 1 ); + if( t ) t[ l ] = 0; + tok = astGrow( tok, ntok + 1, sizeof( char * ) ); + if( tok ) tok[ ntok++ ] = t; + start = p; + word = 1; + } + } + } + + l = p - start; + t = astStore( NULL, start, l + 1 ); + if( t ) t[ l ] = 0; + tok = astGrow( tok, ntok + 1, sizeof( char * ) ); + if( tok ) tok[ ntok++ ] = t; + +/* Check the tokens for known non-standard unit syntax, and replace with the + equivalent standard syntax. Starlink SPLAT has a class called UnitUtilities + which has more of these common units mistakes. AST has to be a bit + more conservative than SPLAT though because of its wider remit. */ + len = 0; + tt = NULL; + for( i = 0; i < ntok; i++ ) { + t = tok[ i ]; + l = strlen( t ); + tt = astStore( tt, t, l + 1 ); + +/* Any alphabetical word followed by a digit is taken as <word>^<digit>. + Any alphabetical word followed by a sign and a digit is taken as + <word>^<sign><digit>. */ + if( l > 1 && *t != '-' && *t != '+' && + strcspn( t, "0123456789" ) == l - 1 ) { + tok[ i ] = astMalloc( l + 2 ); + if( tok[ i ] ) { + strcpy( tok[ i ], t ); + w = t + l - 2; + if( *w != '+' && *w != '-' ) { + tok[ i ][ l - 1 ] = '^'; + strcpy( tok[ i ] + l, t + l - 1 ); + } else { + tok[ i ][ l - 2 ] = '^'; + strcpy( tok[ i ] + l - 1, t + l - 2 ); + } + t = astFree( t ); + } + l++; + +/* If the word ends with "micron" change to "(<start>m*1.0E-6)". Should be OK + for things like "Kmicron". */ + } else if( ( s = strstr( t, "micron" ) ) ) { + tok[ i ] = astMalloc( s - t + 11 ); + if( tok[ i ] ) { + w = tok[ i ]; + *(w++) = '('; + if( s > t ) { + strncpy( w, t, s - t ); + w += s - t; + } + strcpy( w, "m*1.0E-6)" ); + l = s - t + 11; + t = astFree( t ); + } + +/* Convert "STER" to "sr". */ + } else if( !Ustrcmp( t, "STER", status ) ) { + tok[ i ] = astStore( NULL, "sr", 3 ); + l = 2; + t = astFree( t ); + +/* If the word ends with "JY" and is preceded by a single character, change + to "<start>Jy". Should be OK for things like "MJY". */ + } else if( l == 3 && !strcmp( t + 1, "JY" ) ) { + tok[ i ][ 2 ] = 'y'; + +/* If the word begins with "nano" (case-insensitive) change "nano" to + "n". Such changes are usually handled by SplitUnit, but we need to + handle this as a special case here since scanf seems to read "nan" as + a string representation of NaN. */ + } else if( !Ustrncmp( t, "nano", 4, status ) ) { + tok[ i ] = astStore( NULL, t + 3, l - 2 ); + if( tok[ i ] ) { + *(tok[ i ]) = 'n'; + t = astFree( t ); + } + l -= 3; + } + +/* Update the total length of the string. */ + len += l; + } + tt = astFree( tt ); + +/* Concatentate the tokens into a single string, freeing the individual + strings. */ + result = astMalloc( len + 1 ); + if( result ) { + p = result; + for( i = 0; i < ntok; i++ ) { + len = strlen( tok[ i ] ); + memcpy( p, tok[ i ], len ); + p += len; + tok[ i ] = astFree( tok[ i ] ); + } + *p = 0; + tok = astFree( tok ); + +/* Now do other cleaning. + ---------------------- */ + +/* Initialise a pointer to the previous character read from the string. */ + r = result - 1; + +/* Initialise a pointer to the next character to be written to the string. */ + w = result; + +/* Pretend the previous character written to the string was a space. */ + ps = 1; + +/* Read all the supplied string, copying it to earlier parts of the + string discarding leading spaces and multiple adjacent embedded spaces in + the process. */ + while( *(++r) ) { + +/* If the character read is a space, only write it to the string if the + previous character written was not a space (in which case set a flag + to indicate that the previous character written to the string is now a + space). */ + if( isspace( *r ) ) { + if( !ps ) { + *(w++) = *r; + ps = 1; + } + +/* Write all non-space characters to the string, and clear the flag which + indicates if the previous character written to the string was a space. */ + } else { + *(w++) = *r; + ps = 0; + } + } + +/* If the last character written to the string was a space, reduce the + length of the string by one since we do not want any trailing spaces. */ + if( ps ) w--; + +/* Terminate the string. */ + *w = 0; + +/* We now need to pass through the string again, this time removing any + spaces which are adjacent to a binary operator or a parenthesis. */ + r = result - 1; + w = result; + ps = 0; + po = 0; + while( *(++r) ) { + +/* If the current character is a space, only write it if the previous + written character was not an operator or parenthesis. */ + if( isspace( *r ) ) { + if( !po ) { + *(w++) = *r; + po = 1; + ps = 1; + } + +/* If the current character is an operator or parenthesis, back up one + character before writing it out if the previous written character was + a space. */ + } else if( *r == '*' || *r == '/' || *r == '^' || *r == '.' || + *r == ')' || *r == '(' ) { + if( ps ) w--; + *(w++) = *r; + po = 1; + ps = 0; + +/* If the current character is not a space and not an operator symbol, + just write it out. */ + } else { + *(w++) = *r; + po = 0; + ps = 0; + } + } + +/* Terminate the string. */ + if( ps ) w--; + *w = 0; + + } + +/* Return the result. */ + return (const char *) result; +} + +static int CmpTree( UnitNode *tree1, UnitNode *tree2, int exact, int *status ) { +/* +* Name: +* CmpTree + +* Purpose: +* Compares two trees of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int CmpTree( UnitNode *tree1, UnitNode *tree2, int exact, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a zero value if the two trees are +* equivalent. This requires the trees to have identical structure +* except that, if "exact" is zero, arguments for OP_MULT nodes can +* be swapped. +* +* If the trees are not equivalent then a value of +1 or -1 is returned +* depending on whether tree1 should be placed before or after tree2 +* in a sorted list of trees. + +* Parameters: +* tree1 +* A pointer to the UnitNode at the head of the first tree. +* tree2 +* A pointer to the UnitNode at the head of the second tree. +* exact +* If non-zero, then OP_MULT nodes must have their arguments the +* same way round in order for the OP_MULT nodes to match. Otherwise, +* OP_MULT nodes with equivalent arguments match even if the +* arguments are swapped. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the two trees are equal. +1 if tree1 should be placed before +* tree2 in a sorted list of trees. -1 if tree1 should be placed after +* tree2 in a sorted list of trees. + +* Notes: +* - Zero is returned if an error has already occurred, or +* if this function fails for any reason. + +*/ + +/* Local Variables: */ + int result; + int i; + Oper op; + +/* Initialise. */ + result = 0; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* If the op codes differ, compare them as integers. */ + op = tree1->opcode; + if( op != tree2->opcode ) { + result = ( op > tree2->opcode ) ? 1: -1; + +/* If both supplied nodes are OP_LDVAR nodes, compare the associated names. */ + } else if( op == OP_LDVAR ){ + result = strcmp( tree1->name, tree2->name ); + +/* If both supplied nodes are constant nodes, compare the constant values. */ + } else if( tree1->con != AST__BAD ){ + result = astEQUAL( tree1->con, tree2->con ) ? 0 : ( + ( tree1->con > tree2->con ) ? 1 : -1 ); + +/* Otherwise, compare the arguments for the node. */ + } else { + for( i = 0; i < tree1->narg; i++ ) { + result = CmpTree( tree1->arg[ i ], tree2->arg[ i ], exact, status ); + if( result ) break; + } + +/* If the head nodes of the two trees are OP_MULT nodes, and the above + check determined they are different, this may be just because they + have their operands swapped. If "exact" si zero, this is considered an + insignificant difference between the two trees which we should ignore. + To check for this try comparing the arguments again, this time swapping + the arguments of tree2. */ + if( result && op == OP_MULT && !exact ) { + for( i = 0; i < tree1->narg; i++ ) { + result = CmpTree( tree1->arg[ i ], tree2->arg[ 1 - i ], 0, status ); + if( result ) break; + } + } + } + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the answer. */ + return result; +} + +static UnitNode *CombineFactors( UnitNode **factors, double *powers, + int nfactor, double coeff, int *status ) { +/* +* Name: +* CombineFactors + +* Purpose: +* Create a tree which represents the product of the supplied factors. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *CombineFactors( UnitNode **factors, double *powers, +* int nfactor, double coeff, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function createa a tree of UnitNodes which represents the +* product of the supplied factors, and the supplied coefficient. +* The factors are sorted before being combined, using the sort order +* implemented by the CmpTree function. + +* Parameters: +* factors +* A pointer to an array with "nfactor" elements, each element being +* a pointer to a UnitNode which is a factor of the required tree. +* On exit, the array is sorted. +* powers +* A pointer to an array with "nfactor" elements, each element being a +* double holding the power of the associated factor in "factors". +* On exit, the array reflects the sorting applied to "factors". +* nfactor +* The number of elements in the "factors" and "powers" arrays. +* coeff +* The overall coefficient to be applied to the product of the factors. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which is at the head of the new tree. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or +* if this function fails for any reason. + +*/ + +/* Local Variables: */ + UnitNode *result; + int i; + int j; + int jp; + int done; + UnitNode *ftmp; + UnitNode *node1; + UnitNode *node2; + UnitNode *pnode; + double ptmp; + +/* Initialise. */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Sort the supplied list of factors, modifying the powers array + correspondingly. A simple bubblesort algorithm is used since there + will only be a handfull of factors. */ + for( i = nfactor - 1; i > 0; i-- ) { + done = 1; + for( j = 0, jp = 1; j < i; j++, jp++ ) { + if( CmpTree( factors[ j ], factors[ jp ], 0, status ) > 0 ) { + ftmp = factors[ j ]; + factors[ j ] = factors[ jp ]; + factors[ jp ] = ftmp; + + ptmp = powers[ j ]; + powers[ j ] = powers[ jp ]; + powers[ jp ] = ptmp; + + done = 0; + } + } + if( done ) break; + } + +/* The first root term of the returned tree is the coefficient, unless the + coefficient is 1.0, in which case it will be the first factor. */ + if( coeff != 1.0 ) { + node1 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) node1->con = coeff; + } else { + node1 = NULL; + } + +/* Loop through the factors. */ + for( i = 0; i < nfactor; i++ ) { + +/* If the power of this factor is zero, we ignore the factor. */ + if( powers[ i ] != 0.0 ) { + +/* If the power of this factor is one, we use the factor directly. */ + if( astEQUAL( powers[ i ], 1.0 ) ) { + node2 = CopyTree( factors[ i ], status ); + +/* Otherwise, for non-zero, non-unity powers, we create a POW node for + the factor. */ + } else { + node2 = NewNode( NULL, OP_POW, status ); + pnode = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + pnode->con = powers[ i ]; + node2->arg[ 0 ] = CopyTree( factors[ i ], status ); + node2->arg[ 1 ] = pnode; + } + } + +/* We now combine node1 and node2 using an OP_MULT node, which becomes + the "node1" for the next pass. On the first pass we may have no node1 (if + the supplied coefficient was 1.0), in which case we reserve the current + node2 as the node1 for the next pass. */ + if( node1 ) { + result = NewNode( NULL, OP_MULT, status ); + if( astOK ) { + result->arg[ 0 ] = node1; + result->arg[ 1 ] = node2; + node1 = result; + } + } else { + node1 = node2; + } + } + } + +/* Ensure we have a node to return. */ + if( astOK ) { + if( !result ) result = node1; + if( !result ) { + result = NewNode( NULL, OP_LDCON, status ); + if( astOK ) result->con = 1.0; + } + } + +/* If an error has occurred, free any new tree. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the answer. */ + return result; +} + +static int ComplicateTree( UnitNode **node, int *status ) { +/* +* Name: +* ComplicateTree + +* Purpose: +* Removes standardisations introduced by SimplifyTree. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int ComplicateTree( UnitNode **node ) + +* Class Membership: +* Unit member function. + +* Description: +* This function modifies a tree of UnitNodes by removing standardisations +* introduced by SimplifyTree. The standardisations removed are ones +* which would make the corresponding algebraic expression (as produced +* by MakeExp) unnatural to a human reader. + +* Parameters: +* node +* The address of a pointer to the UnitNode at the head of the tree +* which is to be complicated. On exit the supplied tree is freed and +* a pointer to a new tree is placed at the given address. + +* Returned Value: +* Non-zero if any change was introduced into the tree. + +*/ + +/* Local Variables: */ + int i; + UnitNode *newnode; + UnitNode *node1; + UnitNode *node2; + UnitNode *node3; + Oper op; + double con; + double fk; + int k; + int result; + double kcon; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Initiallially, we have no replacement node. */ + newnode = NULL; + node1 = NULL; + node3 = NULL; + +/* Complicate the sub-trees corresponding to the arguments of the node at + the head of the supplied tree. */ + for( i = 0; i < (*node)->narg; i++ ) { + if( ComplicateTree( &( (*node)->arg[ i ] ), status ) ) result = 1; + } + +/* Now undo specific simplifications appropriate to the nature of the node at + the head of the tree. */ + op = (*node)->opcode; + +/* If the head is an OP_MULT node with a constant first argument and + a "LN" second argument, rearrange the nodes to represent ln(x**k) instead + of k*ln(x). If k is an integer multiple of "0.1/ln(10)" convert the "ln" + function into a "log" (base 10) function. Check for "k==1" in which + case we do not need a POW node. */ + if( (*node)->opcode == OP_MULT ) { + + con = (*node)->arg[ 0 ]->con; + if( con != AST__BAD && (*node)->arg[ 1 ]->opcode == OP_LN ) { + fk = 10.0*con*log( 10.0 ); + k = NINT(fk); + if( astEQUAL(fk,((double)k)) ) { + newnode = NewNode( NULL, OP_LOG, status ); + con = k/10.0; + } else { + newnode = NewNode( NULL, OP_LN, status ); + } + + node2 = CopyTree( (*node)->arg[ 1 ]->arg[ 0 ], status ); + if( !astEQUAL( con, 1.0 ) ){ + node1 = CopyTree( (*node)->arg[ 0 ], status ); + node3 = NewNode( NULL, OP_POW, status ); + } + + if( astOK ) { + if( !astEQUAL( con, 1.0 ) ){ + node1->con = con; + node3->arg[ 0 ] = node2; + node3->arg[ 1 ] = node1; + newnode->arg[ 0 ] = node3; + } else { + newnode->arg[ 0 ] = node2; + } + } + +/* Replace "(A**-1)*B" with "B/A" */ + } else if( (*node)->arg[ 0 ]->opcode == OP_POW && + astEQUAL( (*node)->arg[ 0 ]->arg[ 1 ]->con, -1.0 )) { + newnode = NewNode( NULL, OP_DIV, status ); + if( astOK ) { + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 1 ], status ); + newnode->arg[ 1 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + } + +/* Replace "B*(A**-1)" with "B/A" */ + } else if( (*node)->arg[ 1 ]->opcode == OP_POW && + astEQUAL( (*node)->arg[ 1 ]->arg[ 1 ]->con, -1.0 )) { + newnode = NewNode( NULL, OP_DIV, status ); + if( astOK ) { + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ], status ); + newnode->arg[ 1 ] = CopyTree( (*node)->arg[ 1 ]->arg[ 0 ], status ); + } + +/* Convert (x**k)*(y**k) to (x*y)**k. */ + } else if( (*node)->arg[ 0 ]->opcode == OP_POW && + (*node)->arg[ 1 ]->opcode == OP_POW && + astEQUAL( (*node)->arg[ 0 ]->arg[ 1 ]->con, + (*node)->arg[ 1 ]->arg[ 1 ]->con )) { + newnode = NewNode( NULL, OP_POW, status ); + node1 = NewNode( NULL, OP_MULT, status ); + if( astOK ) { + node1->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + node1->arg[ 1 ] = CopyTree( (*node)->arg[ 1 ]->arg[ 0 ], status ); + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 1 ], status ); + } + +/* Convert c*sqrt(x) to sqrt((c**2)*x) (if c > 0). */ + } else if( (kcon=(*node)->arg[ 0 ]->con) != AST__BAD && + kcon > 0.0 && (*node)->arg[ 1 ]->opcode == OP_SQRT ) { + newnode = NewNode( NULL, OP_SQRT, status ); + node1 = NewNode( NULL, OP_MULT, status ); + node2 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node2->con = kcon*kcon; + node1->arg[ 0 ] = node2; + node1->arg[ 1 ] = CopyTree( (*node)->arg[ 1 ]->arg[ 0 ], status ); + newnode->arg[ 0 ] = node1; + } + } + +/* If the head node is a POW node, replace "x**0.5" by sqrt(x) */ + } else if( (*node)->opcode == OP_POW ) { + if( astEQUAL( (*node)->arg[ 1 ]->con, 0.5 ) ) { + newnode = NewNode( NULL, OP_SQRT, status ); + if( astOK ) { + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ], status ); + } + } + } + +/* If we have produced a new node which is identical to the old node, + free it. Otherwise, indicate we have made some changes. */ + if( newnode ) { + if( !CmpTree( newnode, *node, 1, status ) ) { + newnode = FreeTree( newnode, status ); + } else { + result = 1; + } + } + +/* If an error has occurred, free any new node. */ + if( !astOK ) { + newnode = FreeTree( newnode, status ); + result = 0; + } + +/* If we have a replacement node, free the supplied tree and return a + pointer to the new tree. */ + if( newnode ) { + FreeTree( *node, status ); + *node = newnode; + } + +/* If the above produced some change, try simplifying (without + re-introducing the standardisation we have just got rid of!) and + then re-complicating the tree. */ + if( result ) { + SimplifyTree( node, 0, status ); + ComplicateTree( node, status ); + } + +/* Return the result. */ + return result; +} + +static UnitNode *ConcatTree( UnitNode *tree1, UnitNode *tree2, int *status ) { +/* +* Name: +* ConcatTree + +* Purpose: +* Concatenate two trees together. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *ConcatTree( UnitNode *tree1, UnitNode *tree2, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function a pointer to the head of a new tree of UnitNodes which +* is formed by feeding the output of "tree1" (i.e. the quantity +* represented by the node at the head of tree1) into the (single) +* input of "tree2" (i.e. the single OP_LDVAR Node containined within +* tree2). + +* Parameters: +* tree1 +* A pointer to the first tree. +* tree2 +* A pointer to the second tree. This should have no more than one +* OP_LDVAR node. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which is at the head of the new tree. + +* Notes: +* - If "tree2" contains zero units, a NULL pointer is returned but no +* error is reported. +* - If "tree2" contains more than one unit, an error is reported +* error is reported. +* - A NULL pointer is returned if an error has already occurred, or +* if this function fails for any reason. + +*/ + +/* Local Variables: */ + UnitNode *result; + UnitNode **units; + int nunits; + +/* Initialise. */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Produce a copy of tree2. */ + result = CopyTree( tree2, status ); + +/* Locate the OP_LDVAR node in the copy of tree2. */ + units = NULL; + nunits = 0; + LocateUnits( result, &units, &nunits, status ); + +/* If no OP_LDVAR nodes were found in tree2, we cannot concatenate the + trees. */ + if( nunits > 0 ) { + +/* Report an error if the number of pointers returned is larger than 1. */ + if( nunits > 1 && astOK ) { + astError( AST__INTER, "ConcatTree(unit): tree2 uses %d units - " + "should be 1 (internal AST programming error).", status, nunits ); + } + +/* Replace the OP_LDVAR node in the copy of tree2 with a copy of tree1. */ + if( astOK ) { + +/* If the node at the head of the supplied tree2 is the node to be + replaced, just free the tree created earlier and return a copy of + tree1. */ + if( units[ 0 ] == result ) { + FreeTree( result, status ); + result = CopyTree( tree1, status ); + +/* Otherwise, search for the node to be replaced and do the substitution + within the tree created earlier. */ + } else { + ReplaceNode( result, units[ 0 ], CopyTree( tree1, status ), status ); + } + } + } + +/* Free resources. */ + units = astFree( units ); + +/* If an error has occurred, free any new tree. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the answer. */ + return result; +} + +static int ConStart( const char *text, double *val, int *nc, int *status ) { +/* +* Name: +* ConStart + +* Purpose: +* See if the supplied string starts with a literal numeric constant. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int ConStart( const char *text, double *val, int *nc, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function checks if the supplied string starts with a literal +* numeric constant and returns it if it does. It is a wrap-up for scanf +* since scanf has non-standard behaviour on some platforms (e.g. Cygwin +* scanf interprets the character "n" as a floating point number!). + +* Parameters: +* text +* The text to check. +* val +* Address of a double to receive any numerical constant read +* from the start of the string. Unity is returned if the string +* does not start with a numerical constant. +* nc +* Address of an int to receive the number of characters used to +* create the value returned in "val". Zero is returned if the +* string does not start with a numerical constant. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the text started with a numerical constant. + +*/ + +/* Local Variables: */ + int result; + const char *c; + +/* Initialise */ + *nc = 0; + *val = 1.0; + +/* Return zero if no text was supplied */ + if( !text ) return 0; + +/* Use sscanf to see if the string begin with a numerical constant */ + result = astSscanf( text, "%lf%n", val, nc ); + +/* If so, check that the first non-blank character in the string + is not "N" (interpreted by Cygwin as numerical zero!). */ + if( result ) { + c = text; + while( isspace( *c ) ) c++; + if( *c == 'n' || *c == 'N' ) { + result = 0; + *nc = 0; + *val = 1.0; + } + } + +/* Return the result. */ + return result; +} + +static UnitNode *CopyTree( UnitNode *tree, int *status ) { +/* +* Name: +* CopyTree + +* Purpose: +* Create a new tree of UnitNodes containing a copy of a given tree. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *CopyTree( UnitNode *tree, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates a copy of the supplied tree of UnitNodes. + +* Parameters: +* tree +* The UnitNode at the head of the tree to be copied. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the UnitNode at the head of the new tree. + +* Notes: +* - A value of NULL will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + UnitNode **args; + UnitNode *result; + int i; + int narg; + +/* Initialise. */ + result = NULL; + +/* Check the inherited status. */ + if( !astOK || !tree ) return result; + +/* Create a new node to represent the head of the supplied tree. */ + result = astMalloc( sizeof( UnitNode ) ); + +/* Check pointers can be used safely. */ + if( astOK ) { + +/* Copy the fields of the supplied node. */ + narg = tree->narg; + + result->arg = NULL; + result->unit = tree->unit; + result->mult = tree->mult; + result->opcode = tree->opcode; + result->narg = narg; + result->con = tree->con; + result->name = tree->name ? astStore( NULL, tree->name, + strlen( tree->name ) + 1 ) : NULL; + +/* Create an array of UnitNode pointers for the arguments. */ + args = astMalloc( narg*sizeof( UnitNode * ) ); + if( astOK ) { + result->arg = args; + +/* Copy the sub-trees headed by the argument nodes. */ + for( i = 0; i < narg; i++ ) { + args[ i ] = CopyTree( tree->arg[ i ], status ); + } + } + } + +/* Free any result if an error occurred. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the answer. */ + return result; +} + +static UnitNode *CreateTree( const char *exp, int basic, int lock, int *status ){ +/* +* Name: +* CreateTree + +* Purpose: +* Convert an algebraic units expression into a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *CreateTree( const char *exp, int basic, int lock, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function converts the supplied algebraic units expression into +* a tree of UnitNodes. The result tree can optionally be expanded to +* create a tree in which the "roots" (LDVAR nodes) all refer to +* basic units. + +* Parameters: +* exp +* The units expression. This should not include any leading or +* trailing spaces. +* basic +* Should the tree created from parsing "exp" be expanded so that +* the leaf nodes of the tree are all basic units? +* lock +* Use a mutex to guard access to the KnownUnits list? +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which forms the head of a tree of UnitNodes +* representing the supplied unit expression. + +* Notes: +* - A NULL value is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + UnitNode *result; + const char *cleanex; + +/* Initialise */ + result = NULL; + +/* Check the global error status, and that we have a string. */ + if ( !astOK ) return result; + +/* Produce a clean copy of the supplied string. This has no leading + or trailing white space, and any spaces adjacent to operators within + the string are removed (this is needed because spaces are treated as + multiplication symbols). */ + cleanex = CleanExp( exp, status ); + +/* If the string is blank, return the NULL pointer. Otherwise, create a + tree of UnitNodes describing the units. The returned tree has LDVAR + nodes which refer to the unit symbols contained in the supplied string. */ + if( cleanex && (*cleanex) ) { + result = MakeTree( cleanex, strlen( cleanex ), lock, status ); + +/* Replace each subtree which simply combines constants (i.e. which has no + OP_LDVAR nodes) with a single OP_LDCON node. */ + FixConstants( &result, 0, status ); + +/* Invert literal constant unit multipliers. */ + InvertConstants( &result, status ); + +/* Now replace each LDVAR node which refers to a known derived unit with + a sub-tree which defines the derived unit in terms of known basic units. + The LDVAR nodes in the resulting tree all refer to basic units. */ + if( basic ) RemakeTree( &result, status ); + } + +/* Free resources. */ + cleanex = astFree( (void *) cleanex ); + +/* Free any returned tree if an error has occurred. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the result. */ + return result; +} + +static int DimAnal( UnitNode *node, double powers[NQUANT], double *scale, int *status ) { +/* +* Name: +* DimAnal + +* Purpose: +* Perform a dimensional analysis of a unit tree. + +* Type: +* Protected function. + +* Synopsis: +* #include "unit.h" +* int DimAnal( UnitNode *node, double powers[NQUANT], double *scale, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a set of powers and a scaling factor which +* represent the units tree. + +* Parameters: +* node +* Pointer to the UnitNode at the head of the unit tree. +* powers +* An array in which are returned the powers for each of the following +* basic units (in the order shown): kilogramme, metre, second, radian, +* Kelvin, count, adu, photon, magnitude, pixel. If the supplied unit +* does not depend on a given basic unit a value of 0.0 will be returned +* in the array. The returns values represent a system of units which is +* a scaled form of the supplied units, expressed in the basic units of +* m, kg, s, rad, K, count, adu, photon, mag and pixel. For instance, a +* returned array of [1,0,-2,0,0,0,0,0,0,0] would represent "m/s**2". +* scale +* Pointer to a location at which to return a scaling factor for the +* supplied units. The is the value, in the units represented by the +* returned powers, which corresponds to a value of 1.0 in the supplied +* units. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the tree was analysed succesfully. Zero otherwise. + +* Notes: +* - Zero is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables; */ + Oper oper; + int result; + int i; + double p0[ NQUANT ]; + double p1[ NQUANT ]; + double s0; + double s1; + +/* Check inherited status */ + if( !astOK ) return 0; + +/* Initialise the powers of all dimensions to zero, and set the scaling + factor to unity. */ + result = 1; + *scale = 1.0; + for( i = 0; i < NQUANT; i++ ) powers[ i ] = 0.0; + +/* Load constant: constant is dimensionaless so leave powers unchanged, + and set the scaling factor. */ + oper = node->opcode; + if( oper == OP_LDCON ) { + *scale = 1.0/node->con; + +/* Load variable: check it is one of the basic known dimensional + quantities. If so, set the power of the quantity to unity and store + the scale factor. If the unit is "g" modify the scale factor so that + the analysis quantity is "kg". */ + } else if( oper == OP_LDVAR ) { + result = 0; + for( i = 0; i < NQUANT; i++ ) { + if( node->unit == quant_units[ i ] ) { + powers[ i ] = 1.0; + *scale = node->mult ? 1.0/node->mult->scale : 1.0; + if( !strcmp( node->unit->sym, "g" ) ) *scale *= 0.001; + result = 1; + break; + } + } + +/* How does dimensional analysis handle log or exp units?*/ + } else if( oper == OP_LOG ) { + result= 0; + + } else if( oper == OP_LN ) { + result= 0; + + } else if( oper == OP_EXP ) { + result= 0; + +/* Get the powers for the child unit and then multiply each by 0.5 and + take the square root of the scale factor. */ + } else if( oper == OP_SQRT ) { + result = DimAnal( node->arg[0], powers, scale, status ); + if( result ) { + for( i = 0; i < NQUANT; i++ ) powers[ i ]*= 0.5; + *scale = sqrt( *scale ); + } + +/* Similarly for pow nodes. */ + } else if( oper == OP_POW ) { + result = DimAnal( node->arg[0], powers, scale, status ); + if( result ) { + double power = node->arg[1]->con; + for( i = 0; i < NQUANT; i++ ) powers[ i ]*= power; + *scale = pow( *scale, power ); + } + +/* Binary operators. Analyses the operands dimensions and combine. */ + } else if( oper == OP_DIV ) { + if( DimAnal( node->arg[0], p0, &s0, status ) && + DimAnal( node->arg[1], p1, &s1, status ) ) { + for( i = 0; i < NQUANT; i++ ) powers[ i ] = p0[ i ] - p1[ i ]; + *scale = s0/s1; + } else { + result = 0; + } + + } else if( oper == OP_MULT ) { + if( DimAnal( node->arg[0], p0, &s0, status ) && + DimAnal( node->arg[1], p1, &s1, status ) ) { + for( i = 0; i < NQUANT; i++ ) powers[ i ] = p0[ i ] + p1[ i ]; + *scale = s0*s1; + } else { + result = 0; + } + +/* Named constants are dimensionless */ + } else if( oper == OP_LDPI ) { + *scale = 1.0/PI; + + } else if( oper == OP_LDE ) { + *scale = 1.0/E; + + } + + return result; + +} + +static int EndsWith( const char *c, int nc, const char *test, int *status ){ +/* +* Name: +* EndsWith + +* Purpose: +* See if a string ends with another string + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int EndsWith( const char *c, int nc, const char *test, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function sees if the string given by "c" ends with the string +* given by "test". The comparison is case-insensitive. + +* Parameters: +* c +* A pointer to the last character in the string to be tested. +* nc +* The number of characters in the string to be tested. +* test +* A pointer to the string to be tested for. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if the string "c" ends with the string "test". + +*/ + +/* Local Variables: */ + const char *start; + int i; + int result; + int tlen; + +/* initialise. */ + result = 0; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Check the string being tested for is not longer than the string being + tested. */ + tlen = strlen( test ); + if( tlen <= nc ){ + +/* Get a pointer to where the matching string would start if the string "c" + ends with the required string "test". */ + start = c - tlen + 1; + +/* Do the comparison. */ + result = 1; + for( i = 0; i < tlen; i++ ) { + if( tolower( start[ i ] ) != tolower( test[ i ] ) ) { + result = 0; + break; + } + } + } + +/* Return the result. */ + return result; + +} + +static void FindFactors( UnitNode *node, UnitNode ***factors, double **powers, + int *nfactor, double *coeff, int *status ){ +/* +* Name: +* FindFactors + +* Purpose: +* Find the factors within an expression given by a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void FindFactors( UnitNode *node, UnitNode ***factors, double **powers, +* int *nfactor, double *coeff, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function analyses the supplied tree of UnitNoes and returns +* an array of pointers to nodes within the supplied tree which form +* factors of the tree. The power associated with each factor is also +* returned, together with an overall coefficient for the tree. The +* expression represented by the tree is thus the product of the +* coefficient with each of the factors, each raised to the associated +* power. + +* Parameters: +* node +* A pointer to the UnitNode at the head of the tree which is to be +* analysed. +* factors +* The address at which to return a pointer to an array with "*nfactor" +* elements, each element being a pointer to a UnitNode within the +* supplied tree which is a factor of the supplied tree. +* powers +* The address at which to return a pointer to an array with "*nfactor" +* elements, each element being a double holding the power of the +* associated factor in "*factors". +* nfactor +* The address of an int containing the number of elements in the +* returned "*factors" and "*powers" arrays. +* coeff +* The address of a double containing the overall coefficient to be +* applied to the product of the factors. +* status +* Pointer to the inherited status variable. + +* Notes: +* - If the supplied node is a constant node, then "*coeff" is +* returned holding the value of the constant and "*nfactor" is returned +* equal to zero ("*factors" and "*powers" are returned holding NULL). +* - If an error has already occurred, or if this function fails, then +* "*factors" and "*powers" are returned holding NULL, "*nfactor" is +* returned holding zero and "*coeff" is returned holding 1.0. + +*/ + +/* Local Variables: */ + int i; + int j; + int found; + UnitNode **fact1; + double *pow1; + double coeff1; + int nfac1; + double con; + +/* Initialise */ + *factors = NULL; + *powers = NULL; + *nfactor = 0; + *coeff = 1.0; + +/* Check inherited status. */ + if( !astOK ) return; + +/* If the node at the head of the supplied tree is an OP_MULT node... */ + if( node->opcode == OP_MULT ) { + +/* Find the factors of the two arguments of the OP_MULT node. */ + FindFactors( node->arg[ 0 ], factors, powers, nfactor, coeff, status ); + FindFactors( node->arg[ 1 ], &fact1, &pow1, &nfac1, &coeff1, status ); + +/* Combine the two lists. Loop round the factors of the seocnd argument. */ + for( i = 0; i < nfac1; i++ ) { + +/* See if there is already an equivalent factor in the returned list of + factors. */ + found = 0; + for( j = 0; j < *nfactor; j++ ) { + if( !CmpTree( (*factors)[ j ], fact1[ i ], 0, status ) ){ + found = 1; + break; + } + } + +/* If so, increment the power of the factor. */ + if( found ) { + (*powers)[ j ] += pow1[ i ]; + +/* Otherwise, add the factor to the end of the returned list. */ + } else { + *factors = astGrow( *factors, *nfactor + 1, sizeof( UnitNode *) ); + *powers = astGrow( *powers, *nfactor + 1, sizeof( double ) ); + if( astOK ) { + (*factors)[ *nfactor ] = fact1[ i ]; + (*powers)[ (*nfactor)++ ] = pow1[ i ]; + } + } + } + +/* Modify the overall coefficient. */ + *coeff *= coeff1; + +/* Free resources */ + fact1 = astFree( fact1 ); + pow1 = astFree( pow1 ); + +/* If the node at the head of the supplied tree is an OP_POW node, */ + } else if( node->opcode == OP_POW ) { + +/* Find the factors of the first argument. */ + FindFactors( node->arg[ 0 ], factors, powers, nfactor, coeff, status ); + +/* Multiply all the factor powers by the constant exponent of the POW + node. */ + con = node->arg[ 1 ]->con; + for( j = 0; j < *nfactor; j++ ) { + (*powers)[ j ] *= con; + } + +/* Exponentiate the coefficient. */ + if( *coeff >= 0.0 || (int) con == con ) { + *coeff = pow( *coeff, con ); + } else { + astError( AST__BADUN, "Simplifying a units expression requires a " + "negative value to be raised to a non-intergal power." , status); + } + +/* If the node at the head of the supplied tree is an OP_DIV node, */ + } else if( node->opcode == OP_DIV ) { + +/* Find the factors of the two arguments of the OP_DIV node. */ + FindFactors( node->arg[ 0 ], factors, powers, nfactor, coeff, status ); + FindFactors( node->arg[ 1 ], &fact1, &pow1, &nfac1, &coeff1, status ); + +/* Combine the two lists. Loop round the factors of the second argument + (the denominator). */ + for( i = 0; i < nfac1; i++ ) { + +/* See if there is already an equivalent factor in the returned list of + factors. */ + found = 0; + for( j = 0; j < *nfactor; j++ ) { + if( !CmpTree( (*factors)[ j ], fact1[ i ], 0, status ) ){ + found = 1; + break; + } + } + +/* If so, decrement the power of the factor. */ + if( found ) { + (*powers)[ j ] -= pow1[ i ]; + +/* Otherwise, add the factor to the end of the returned list, with a + negated power. */ + } else { + *factors = astGrow( *factors, *nfactor + 1, sizeof( UnitNode *) ); + *powers = astGrow( *powers, *nfactor + 1, sizeof( double ) ); + if( astOK ) { + (*factors)[ *nfactor ] = fact1[ i ]; + (*powers)[ (*nfactor)++ ] = -pow1[ i ]; + } + } + } + +/* Modify the overall coefficient. */ + if( coeff1 != 0.0 ) { + *coeff /= coeff1; + } else { + astError( AST__BADUN, "Simplifying a units expression" + "requires a division by zero." , status); + } + +/* Free resources */ + fact1 = astFree( fact1 ); + pow1 = astFree( pow1 ); + +/* If the node at the head of the supplied tree is an OP_SQRT node, */ + } else if( node->opcode == OP_SQRT ) { + +/* Find the factors of the argument. */ + FindFactors( node->arg[ 0 ], factors, powers, nfactor, coeff, status ); + +/* Multiply all the factor powers by 0.5. */ + for( j = 0; j < *nfactor; j++ ) { + (*powers)[ j ] *= 0.5; + } + +/* Square root the coefficient. */ + if( *coeff >= 0.0 ) { + *coeff = sqrt( *coeff ); + } else { + astError( AST__BADUN, "Simplifying a units expression requires " + "the square root of a negative value to be taken." , status); + } + +/* If the node at the head of the supplied tree is constant we have no + factors but we have a coeffcient. */ + } else if( node->con != AST__BAD ) { + *coeff = node->con; + +/* Other nodes have no factors other than themselves, so just return a + pointer to the supplied node. */ + } else { + *factors = astMalloc( sizeof( UnitNode *) ); + *powers = astMalloc( sizeof( double ) ); + if( astOK ) { + *nfactor = 1; + (*factors)[ 0 ] = node; + (*powers)[ 0 ] = 1.0; + *coeff = 1.0; + } + } + +/* If an error has occurred, free any returned resources. */ + if( !astOK ) { + *factors = astFree( *factors ); + *powers = astFree( *powers ); + *nfactor = 0; + *coeff = 1.0; + } +} + +static void FixConstants( UnitNode **node, int unity, int *status ) { +/* +* Name: +* FixConstants + +* Purpose: +* Take the reciprocal of all constants in a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void FixConstants( UnitNode **node, int unity, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function replaces sub-trees which have a constant value by +* a single OP_LDCON node which loads the appropriate constant. + +* Parameters: +* node +* The address of a pointer to the UnitNode at the head of the tree +* which is to be fixed. On exit the supplied tree is freed and a +* pointer to a new tree is palced at he given address. +* unity +* If non-zero, then all multiplicative constants are set to 1.0, and +* their original values are forgotten, but only if the other +* argument of the OP_MULT node is an OP_LDVAR, OP_POW or OP_SQRT Node. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int i; + UnitNode *newnode; + int allcon; + Oper op; + double newcon; + +/* Check inherited status and pointer. */ + if( !astOK || !node || !(*node) ) return; + +/* Initiallially, we have no replacement node */ + newnode = NULL; + newcon = AST__BAD; + +/* There is nothing to fix if the node has no arguments. */ + if( (*node)->narg > 0 ) { + +/* Note the op code for the node. */ + op = (*node)->opcode; + +/* Fix up the argument nodes. Also note if all the arguments are + constants. */ + allcon = 1; + for( i = 0; i < (*node)->narg; i++ ) { + FixConstants( &( (*node)->arg[ i ] ), unity, status ); + if( (*node)->arg[ i ]->con == AST__BAD ) allcon = 0; + } + +/* If an OP_MULT nodes within a simplified tree has a constant argument, + it will always be argument zero. If this is an OP_MULT node and arg[0] + is constant and "unity" is non-zero and arg[1] is an OP_LDVAR, OP_POW + or OP_SQRT node, replace the constant value by 1.0. */ + if( unity && op == OP_MULT && + (*node)->arg[ 0 ]->con != AST__BAD && + ( (*node)->arg[ 1 ]->opcode == OP_LDVAR || + (*node)->arg[ 1 ]->opcode == OP_SQRT || + (*node)->arg[ 1 ]->opcode == OP_POW ) ) { + (*node)->arg[ 0 ]->con = 1.0; + } + +/* If the arguments of this node are all constants, replace the node by + an OP_LDCON node which loads the resulting constant value. */ + if( allcon ) { + if( (*node)->narg > 0 ) { + newnode = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + if( op == OP_LOG ) { + if( (*node)->arg[ 0 ]->con > 0.0 ) { + newcon = log10( (*node)->arg[ 0 ]->con ); + } else { + astError( AST__BADUN, "Illegal negative or zero constant " + "value '%g' encountered.", status, + (*node)->arg[ 0 ]->con ); + } + } else if( op == OP_LN ){ + if( (*node)->arg[ 0 ]->con > 0.0 ) { + newcon = log( (*node)->arg[ 0 ]->con ); + } else { + astError( AST__BADUN, "Illegal negative or zero constant value " + "'%g' encountered.", status, (*node)->arg[ 0 ]->con ); + } + } else if( op == OP_EXP ){ + newcon = exp( (*node)->arg[ 0 ]->con ); + + } else if( op == OP_SQRT ){ + if( (*node)->arg[ 0 ]->con >= 0.0 ) { + newcon = sqrt( (*node)->arg[ 0 ]->con ); + } else { + astError( AST__BADUN, "Illegal negative constant value " + "'%g' encountered.", status, (*node)->arg[ 0 ]->con ); + } + + } else if( op == OP_POW ){ + if( (*node)->arg[ 0 ]->con >= 0.0 || + (int) (*node)->arg[ 1 ]->con == (*node)->arg[ 1 ]->con ) { + newcon = pow( (*node)->arg[ 0 ]->con, + (*node)->arg[ 1 ]->con ); + } else { + astError( AST__BADUN, "Illegal negative constant value " + "'%g' encountered.", status, (*node)->arg[ 0 ]->con ); + } + + } else if( op == OP_DIV ){ + if( (*node)->arg[ 1 ]->con != 0.0 ) { + newcon = (*node)->arg[ 0 ]->con / (*node)->arg[ 1 ]->con; + } else { + astError( AST__BADUN, "Illegal zero constant value encountered." , status); + } + + } else if( op == OP_MULT ){ + newcon = (*node)->arg[ 0 ]->con * (*node)->arg[ 1 ]->con; + + } + + + if( astOK ) newnode->con = newcon; + } + } + } + } + +/* If an error has occurred, free any new node. */ + if( !astOK ) newnode = FreeTree( newnode, status ); + +/* If we have a replacement node, free the supplied tree and return a + pointer to the new tree. */ + if( newnode ) { + FreeTree( *node, status ); + *node = newnode; + } + +} + +static UnitNode *FixUnits( UnitNode *node, UnitNode *test, int *status ) { +/* +* Name: +* FixUnits + +* Purpose: +* Assign a constant value to all units except for one. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *FixUnits( UnitNode *node, UnitNode *test, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a copy of the supplied tree of UnitNodes. All +* OP_LDVAR nodes within the copy which refer to units which differ +* from those referred to by the supplied test node are replaced by +* OP_LDCON nodes which load the constant value 1.0. + +* Parameters: +* node +* A pointer to the UnitNode at the head of the tree to be used. +* test +* A pointer to an OP_LDVAR node which defines the units which are +* *not* to be replaced by a constant value of 1.0. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which is at the head of a tree of UnitNodes +* which forms the required copy of th einput tree. + +* Notes: +* - A NULL pointer is returned if an error has already occurred, or +* if this function fails for any reason. + +*/ + +/* Local Variables: */ + int i; + UnitNode *result; + +/* Initialise. */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Create a complete copy of the supplied tree. */ + result = CopyTree( node, status ); + +/* Is the node at the head of the supplied tree an OP_LDVAR node? */ + if( node->opcode == OP_LDVAR ) { + +/* Does it refer to a unit which differs from that of the test node? If so + annul the copy created above and return a new OP_LDCON node which loads + the constant value 1.0. */ + if( strcmp( test->name, node->name ) ) { + FreeTree( result, status ); + result = NewNode( NULL, OP_LDCON, status ); + if( astOK ) result->con = 1.0; + } + +/* If the supplied node is not an OP_LDVAR node, check each argument of + the head node. */ + } else { + for( i = 0; i < node->narg; i++ ) { + +/* Free the resources used to hold this argument in the tree copy created + above. */ + FreeTree( result->arg[ i ], status ); + +/* Create a new argument tree by calling this function recursively to + fix units in the argument sub-trees. */ + result->arg[ i ] = FixUnits( node->arg[ i ], test, status ); + } + } + +/* If an error has occurred, free any new tree. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the answer. */ + return result; +} + +static UnitNode *FreeTree( UnitNode *node, int *status ) { +/* +* Name: +* FreeTree + +* Purpose: +* Free resources used by a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *FreeTree( UnitNode *node, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function frees the memory used to store a tree of UnitNodes. + +* Parameters: +* node +* A pointer to the UnitNode at the head of the tree which is to be +* freed. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A NULL pointer is returned. + +* Notes: +* - This function attempts to execute even if it is invoked with +* the global error status set. +*/ + +/* Local Variables: */ + int i; + +/* Check a node was supplied. */ + if( node ) { + +/* Recursively free any argument nodes. */ + if( node->arg ) { + for( i = 0; i < node->narg; i++ ) { + (node->arg)[ i ] = FreeTree( (node->arg)[ i ], status ); + } + node->arg = astFree( node->arg ); + } + +/* Nullify other pointers for safety. */ + node->unit = NULL; + node->mult = NULL; + +/* Free the copy of the symbol string (if any). */ + node->name = astFree( (char *) node->name ); + +/* Free the memory holding the node. */ + node = astFree( node ); + } + +/* Return a null pointer. */ + return NULL; +} + +static KnownUnit *GetKnownUnits( int lock, int *status ) { +/* +* Name: +* GetKnownUnits + +* Purpose: +* Get a pointer to the head of a linked list of known unit definitions. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* KnownUnit *GetKnownUnits( int lock, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a pointer to the head of a linked list of known +* unit definitions. The unit definitions are created as static module +* variables if they have not previously been created. + +* Parameters: +* lock +* If non-zero, then lock a mutex prior to accessing the list of +* known units. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the first known unit definition. + +* Notes: +* - A NULL pointer is returned if it is invoked with the global error +* status set, or if an error occurs. +*/ + +/* Local Variables: */ + int iq; + KnownUnit *result; + +/* Initialise. */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Ensure the known units list is only initialised once. */ + if( lock ) { + LOCK_MUTEX1 + } + +/* If the linked list of KnownUnit structures describing the known units + has not yet been created, create it now. A pointer to the head of the + linked list is put into the static variable "known_units". */ + if( !known_units ) { + +/* At the same time we store pointers to the units describing the basic + quantities used in dimensional analysis. Initialise th index of the + next such unit. */ + iq = 0; + +/* Create definitions for the known units. First do all IAU basic units. + We include "g" instead of "kg" because otherwise we would have to + refer to a gramme as a milli-kilogramme. */ + MakeKnownUnit( "g", "gram", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "m", "metre", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "s", "second", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "rad", "radian", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "K", "Kelvin", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "A", "Ampere", NULL, status ); + MakeKnownUnit( "mol", "mole", NULL, status ); + MakeKnownUnit( "cd", "candela", NULL, status ); + +/* Now do all IAU derived units. Unit definitions may only refer to units + which have already been defined. */ + MakeKnownUnit( "sr", "steradian", "rad rad", status ); + MakeKnownUnit( "Hz", "Hertz", "1/s", status ); + MakeKnownUnit( "N", "Newton", "kg m/s**2", status ); + MakeKnownUnit( "J", "Joule", "N m", status ); + MakeKnownUnit( "W", "Watt", "J/s", status ); + MakeKnownUnit( "C", "Coulomb", "A s", status ); + MakeKnownUnit( "V", "Volt", "J/C", status ); + MakeKnownUnit( "Pa", "Pascal", "N/m**2", status ); + MakeKnownUnit( "Ohm", "Ohm", "V/A", status ); + MakeKnownUnit( "S", "Siemens", "A/V", status ); + MakeKnownUnit( "F", "Farad", "C/V", status ); + MakeKnownUnit( "Wb", "Weber", "V s", status ); + MakeKnownUnit( "T", "Tesla", "Wb/m**2", status ); + MakeKnownUnit( "H", "Henry", "Wb/A", status ); + MakeKnownUnit( "lm", "lumen", "cd sr", status ); + MakeKnownUnit( "lx", "lux", "lm/m**2", status ); + +/* Now do additional derived and basic units listed in the FITS-WCS paper. */ + MakeKnownUnit( "deg", "degree", "pi/180 rad", status ); + MakeKnownUnit( "arcmin", "arc-minute", "1/60 deg", status ); + MakeKnownUnit( "arcsec", "arc-second", "1/3600 deg", status ); + MakeKnownUnit( "mas", "milli-arcsecond", "1/3600000 deg", status ); + MakeKnownUnit( "min", "minute", "60 s", status ); + MakeKnownUnit( "h", "hour", "3600 s", status ); + MakeKnownUnit( "d", "day", "86400 s", status ); + MakeKnownUnit( "yr", "year", "31557600 s", status ); + MakeKnownUnit( "a", "year", "31557600 s", status ); + MakeKnownUnit( "eV", "electron-Volt", "1.60217733E-19 J", status ); + MakeKnownUnit( "erg", "erg", "1.0E-7 J", status ); + MakeKnownUnit( "Ry", "Rydberg", "13.605692 eV", status ); + MakeKnownUnit( "solMass", "solar mass", "1.9891E30 kg", status ); + MakeKnownUnit( "u", "unified atomic mass unit", "1.6605387E-27 kg", status ); + MakeKnownUnit( "solLum", "solar luminosity", "3.8268E26 W", status ); + MakeKnownUnit( "Angstrom", "Angstrom", "1.0E-10 m", status ); + MakeKnownUnit( "micron", "micron", "1.0E-6 m", status ); + MakeKnownUnit( "solRad", "solar radius", "6.9599E8 m", status ); + MakeKnownUnit( "AU", "astronomical unit", "1.49598E11 m", status ); + MakeKnownUnit( "lyr", "light year", "9.460730E15 m", status ); + MakeKnownUnit( "pc", "parsec", "3.0867E16 m", status ); + MakeKnownUnit( "count", "count", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "adu", "analogue-to-digital unit", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "photon", "photon", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "Jy", "Jansky", "1.0E-26 W /m**2 /Hz", status ); + MakeKnownUnit( "mag", "magnitude", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "G", "Gauss", "1.0E-4 T", status ); + MakeKnownUnit( "pixel", "pixel", NULL, status ); + quant_units[ iq++ ] = known_units; + MakeKnownUnit( "barn", "barn", "1.0E-28 m**2", status ); + MakeKnownUnit( "D", "Debye", "(1.0E-29/3) C.m", status ); + + if( iq != NQUANT && astOK ) { + astError( AST__INTER, "unit(GetKnownUnits): %d basic quantities " + "noted but this should be %d (internal AST programming " + "error).", status, iq, NQUANT ); + } + +/* Unit aliases... */ + MakeUnitAlias( "Angstrom", "Ang", status ); + MakeUnitAlias( "count", "ct", status ); + MakeUnitAlias( "photon", "ph", status ); + MakeUnitAlias( "Jy", "Jan", status ); + MakeUnitAlias( "pixel", "pix", status ); + MakeUnitAlias( "s", "sec", status ); + MakeUnitAlias( "m", "meter", status ); + } + +/* If succesful, return the pointer to the head of the list. */ + if( astOK ) result = known_units; + +/* Allow the next thread to proceed. */ + if( lock ) { + UNLOCK_MUTEX1 + } + +/* Return the result. */ + return result; +} + +static Multiplier *GetMultipliers( int *status ) { +/* +* Name: +* GetMultiplier + +* Purpose: +* Get a pointer to the head of a linked list of multiplier definitions. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* Multiplier *Multipliers( void ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a pointer to the head of a linked list of known +* multiplier definitions. The multiplier definitions are created as +* static module variables if they have not previously been created. + +* Returned Value: +* A pointer to the first known multiplier definition. + +* Notes: +* - A NULL pointer is returned if it is invoked with the global error +* status set, or if an error occurs. +*/ + +/* Local Variables: */ + Multiplier *result; + Multiplier *mult; + +/* Initialise. */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Ensure the list is only initialised by one thread. */ + LOCK_MUTEX2 + +/* If the linked list of Multiplier structures describing the known + multipliers has not yet been created, create it now. A pointer to the + head of the linked list is put into the static variable "multipliers". */ + if( !multipliers ) { + +/* Define a macro to create a multiplier struncture and add it to the + linked list of multiplier structures. */ +#define MAKEMULT(s,sl,sc,lab,ll) \ + mult = astMalloc( sizeof( Multiplier ) ); \ + if( astOK ) { \ + mult->sym = s; \ + mult->symlen = sl; \ + mult->lablen = ll; \ + mult->scale = sc; \ + mult->label = lab; \ + mult->next = multipliers; \ + multipliers = mult; \ + } + +/* Use the above macro to create all the standard multipliers listed in the + FITS WCS paper I. */ + MAKEMULT("d",1,1.0E-1,"deci",4) + MAKEMULT("c",1,1.0E-2,"centi",5) + MAKEMULT("m",1,1.0E-3,"milli",5) + MAKEMULT("u",1,1.0E-6,"micro",5) + MAKEMULT("n",1,1.0E-9,"nano",4) + MAKEMULT("p",1,1.0E-12,"pico",4) + MAKEMULT("f",1,1.0E-15,"femto",5) + MAKEMULT("a",1,1.0E-18,"atto",4) + MAKEMULT("z",1,1.0E-21,"zepto",5) + MAKEMULT("y",1,1.0E-24,"yocto",5) + MAKEMULT("da",2,1.0E1,"deca",4) + MAKEMULT("h",1,1.0E2,"hecto",5) + MAKEMULT("k",1,1.0E3,"kilo",4) + MAKEMULT("M",1,1.0E6,"mega",4) + MAKEMULT("G",1,1.0E9,"giga",4) + MAKEMULT("T",1,1.0E12,"tera",4) + MAKEMULT("P",1,1.0E15,"peta",4) + MAKEMULT("E",1,1.0E18,"exa",3) + MAKEMULT("Z",1,1.0E21,"zetta",5) + MAKEMULT("Y",1,1.0E24,"yotta",5) + +/* Undefine the macro. */ +#undef MAKEMULT + + } + +/* If succesful, return the pointer to the head of the list. */ + if( astOK ) result = multipliers; + +/* Allow the next thread to proceed. */ + UNLOCK_MUTEX2 + +/* Return the result. */ + return result; +} + +static void InvertConstants( UnitNode **node, int *status ) { +/* +* Name: +* InvertConstants + +* Purpose: +* Take the reciprocal of all constants in a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void InvertConstants( UnitNode **node, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function replaces constant unit coefficients by their reciprocal. +* This is because a string such as "0.01 m" will be interpreted as +* meaning "multiply a value in metres by 0.01 to get the value in the +* required units", whereas what is actually meant is "use units of +* 0.01 of a metre" which requires us to divide the value in metres by +* 0.01, not multiply it. + +* Parameters: +* node +* The address of a pointer to the UnitNode at the head of the tree. +* On exit the supplied tree is freed and a pointer to a new tree is +* placed at the given address. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int i; + UnitNode *newnode; + int allcon; + Oper op; + +/* Check inherited status and pointer. */ + if( !astOK || !node || !(*node) ) return; + +/* Initiallially, we have no replacement node */ + newnode = NULL; + +/* There is nothing to fix if the node has no arguments. */ + if( (*node)->narg > 0 ) { + +/* Note the op code for the node. */ + op = (*node)->opcode; + +/* Fix up the argument nodes. Also note if all the arguments are + constants. */ + allcon = 1; + for( i = 0; i < (*node)->narg; i++ ) { + InvertConstants( &( (*node)->arg[ i ] ), status ); + if( (*node)->arg[ i ]->con == AST__BAD ) allcon = 0; + } + +/* If all nodes are constant, there are no co-efficients to invert. */ + if( !allcon ) { + +/* Iif this is a multiplication node, see if either of its arguments + is a constant. If so, invert the constant. This is because a string like + "0.01 m" means "each unit is 0.01 of a metre". Therefore, to transform + a value in metres into required units means multiplying the metres + value by 100.0 (i.e the reciprocal of 0.01), not 0.01. */ + if( op == OP_MULT ) { + for( i = 0; i < 2; i++ ) { + if( (*node)->arg[ i ]->con != AST__BAD ) { + if( (*node)->arg[ i ]->con != 0.0 ) { + + (*node)->arg[ i ]->con = 1.0/(*node)->arg[ i ]->con; + } else { + astError( AST__BADUN, "Illegal zero constant encountered." , status); + } + } + } + +/* Likewise, check for division nodes in which the denominator is + constant. */ + } else if( op == OP_DIV ) { + if( (*node)->arg[ 1 ]->con != AST__BAD ) { + if( (*node)->arg[ 1 ]->con != 0.0 ) { + (*node)->arg[ 1 ]->con = 1.0/(*node)->arg[ 1 ]->con; + } else { + astError( AST__BADUN, "Illegal zero constant encountered." , status); + } + } + +/* If this is a "pow" node check that the second argument is constant + (required by FITS WCS paper I). */ + } else if( op == OP_POW ) { + if( (*node)->arg[ 1 ]->con == AST__BAD ) { + astError( AST__BADUN, "Illegal variable exponent." , status); + } + } + } + } + +/* If an error has occurred, free any new node. */ + if( !astOK ) newnode = FreeTree( newnode, status ); + +/* If we have a replacement node, free the supplied tree and return a + pointer to the new tree. */ + if( newnode ) { + FreeTree( *node, status ); + *node = newnode; + } +} + +static UnitNode *InvertTree( UnitNode *fwdnode, UnitNode *src, int *status ) { +/* +* Name: +* InvertTree + +* Purpose: +* Invert a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *InvertTree( UnitNode *fwdnode, UnitNode *src ) + +* Class Membership: +* Unit member function. + +* Description: +* This function inverts a tree of UnitNodes. The supplied tree should +* have exactly one OP_LDVAR node. This will be the quantity represented +* by the node at the head of the returned tree. + +* Parameters: +* fwdnode +* A pointer to the UnitNode at the head of the tree which is to be +* inverted. +* src +* A pointer to a UnitNode which is to be used as the root of the +* inverted tree. That is, the output from this node should form +* the (one and only) varying input to the inverted tree. If the +* supplied tree is succesfulyl inverted, the tree of which "src" +* is the head will be contained within the returned inverted tree. +* Therefore "src" only needs to be freed explicitly if this +* function fails to invert the supplied tree for any reason. If +* this function succeeds, then "src" will be freed as part of +* freeing the returned inverted tree. + +* Returned Value: +* A pointer to a UnitNode which forms the head of the inverted tree. + +* Algorithm: +* The algorithm works through the supplied forward tree, from the head +* to the roots. First, the supplied node at the head of the forward +* tree is inverted. To be invertable, the supplied head node must have +* exactly one varying argument (any other arguments must be fixed, +* i.e. not vary). This varying argument becomes the output of the +* inverted node. The other (fixed) arguments to the forward node are +* also used as arguments to the inverted node. The supplied "src" node +* is used as the single varying input to the inverted node. Having +* inverted the supplied forward head node, this function is called +* recursively to invert the lower parts of the forward tree (i.e. the +* part of the forward tree which provided the varying input to node +* which has just been inverted). + +* Notes: +* - It is assumed that he supplied forward tree has been simplified +* using SimplifyTree. This means that the tree contains no nodes with +* the following op codes: OP_LOG, OP_SQRT. OP_DIV (SimplifyTree +* converts these nodes into OP_LN, OP_POW and OP_MULT nodes). +* - A value of NULL will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. + +*/ + +/* Local Variables: */ + UnitNode *newnode; + UnitNode *nextnode; + UnitNode *result; + UnitNode *node1; + Oper fop; + int varg; + +/* Initialise */ + result = NULL; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Initiallially, we have no replacement node */ + newnode = NULL; + nextnode = NULL; + +/* Save the op code at the head of the forward tree. */ + fop = fwdnode->opcode; + +/* If the head of the forward tree is a OP_EXP node. Inverse of + "exp(x)" is "ln(x)". */ + if( fop == OP_EXP ) { + newnode = NewNode( NULL, OP_LN, status ); + if( astOK ) { + newnode->arg[ 0 ] = src; + nextnode = fwdnode->arg[ 0 ]; + } + +/* If the head of the forward tree is a OP_LN node. Inverse of + "ln(x)" is "exp(x)". */ + } else if( fop == OP_LN ) { + newnode = NewNode( NULL, OP_EXP, status ); + if( astOK ) { + newnode->arg[ 0 ] = src; + nextnode = fwdnode->arg[ 0 ]; + } + +/* If the head of the forward tree is a OP_POW node. Inverse of + "x**k" is "x**(1/k)" */ + } else if( fop == OP_POW ) { + newnode = NewNode( NULL, OP_POW, status ); + node1 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node1->con = 1.0/fwdnode->arg[ 1 ]->con; + newnode->arg[ 0 ] = src; + newnode->arg[ 1 ] = node1; + nextnode = fwdnode->arg[ 0 ]; + } + +/* If the head of the forward tree is a OP_MULT node... */ + } else if( fop == OP_MULT ) { + +/* The node is only invertable if it has one constant node and one + non-constant node. Get the index of the varying argument. */ + if( fwdnode->arg[ 0 ]->con != AST__BAD && + fwdnode->arg[ 1 ]->con == AST__BAD ) { + varg = 1; + } else if( fwdnode->arg[ 0 ]->con == AST__BAD && + fwdnode->arg[ 1 ]->con != AST__BAD ) { + varg = 0; + } else { + varg = -1; + } + if( varg != -1 ) { + +/* The inverse of "k*x" is "(1/k)*x" (we use MULT nodes instead of DIV + nodes to maintain the standardisation implemented by SimplifyTree). */ + newnode = NewNode( NULL, OP_MULT, status ); + node1 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node1->con = 1.0/fwdnode->arg[ 1 - varg ]->con; + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = src; + nextnode = fwdnode->arg[ varg ]; + } + } + +/* If the head of the forward tree is a OP_LDVAR node, there is nothing + left to invert. SO return a pointer to the suppleid source node. */ + } else if( fop == OP_LDVAR ) { + result = src; + nextnode = NULL; + +/* If the head of the forward tree is any other node (e.g. a OP_LDCON node), + the tree cannot be inverted. */ + } else { + nextnode = NULL; + } + +/* If we managed to invert the node at the head of the supplied tree, + continue to invert its varying argument node (if any). */ + if( nextnode && newnode ) result = InvertTree( nextnode, newnode, status ); + +/* If the tree could not be inverted, free the newnode. */ + if( !result ) newnode = FreeTree( newnode, status ); + +/* If an error has occurred, free any new node. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the result. */ + return result; + +} + +static void LocateUnits( UnitNode *node, UnitNode ***units, int *nunits, int *status ){ +/* +* Name: +* LocateUnits + +* Purpose: +* Locate the units used by a supplied tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void LocateUnits( UnitNode *node, UnitNode ***units, int *nunits, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function locates the units used by a supplied tree of +* UnitNodes. + +* Parameters: +* node +* A pointer to the UnitNode at the head of the tree to be searched. +* units +* The address at which is stored a pointer to an array of "*nunits" +* elements. Each element of the array holds a pointer to a UnitNode. +* The array is extended on exit to hold pointers to the OP_LDVAR nodes +* within the supplied tree (i.e. nodes which represent named units, +* either known or unknown). A node is only included in the returned +* array if no other node for the same unit is already included in the +* array. A NULL pointer should be supplied on the first invocation of +* this function. +* nunits +* The address of an integer which holds the number of elements in +* the array given by "*units". Updated on exit to included any +* elements added to the array. Zero should be supplied on the first +* invocation of this function. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + int i; + int found; + +/* Check the global error status. */ + if( !astOK ) return; + +/* Is the node at the head of the supplied tree an OP_LDVAR node? */ + if( node->opcode == OP_LDVAR ) { + +/* If an array was supplied, see if it already contains a pointer to a node + which refers to the same units. */ + found = 0; + if( *units ) { + for( i = 0; i < *nunits; i++ ) { + if( !strcmp( (*units)[ i ]->name, node->name ) ) { + found = 1; + break; + } + } + } + +/* If not, ensure the array is big enough and add a pointer to the + supplied node to the array. */ + if( !found ) { + *units = astGrow( *units, *nunits + 1, sizeof( UnitNode * ) ); + if( astOK ) (*units)[ (*nunits)++ ] = node; + } + +/* If the supplied node is not an OP_LDVAR node, call this function + recursively to search the argument sub-trees. */ + } else { + for( i = 0; i < node->narg; i++ ) { + LocateUnits( node->arg[ i ], units, nunits, status ); + } + } +} + +static const char *MakeExp( UnitNode *tree, int mathmap, int top, int *status ) { +/* +* Name: +* MakeExp + +* Purpose: +* Make an algebraic expression from a supplied tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* const char *MakeExp( UnitNode *tree, int mathmap, int top, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function produces a string holding an algebraic expression +* corresponding to a supplied tree of UnitNodes. + +* Parameters: +* tree +* A pointer to the UnitNode at the head of the tree to be converted +* into an algebraic expression. +* mathmap +* If zero, format as an axis label expression. If 1, format as a +* MathMap expression. If 2, format as a FITS unit string. +* top +* Should be non-zero for a top-level entry to this function, and +* zero for a recursive entry. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the cleaned expression, which should be freed using +* astFree when no longer needed. + +* Notes: +* - This function returns NULL if it is invoked with the global error +* status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + UnitNode *newtree; + UnitNode *sunit; + char *a; + char *result; + char buff[200]; + const char *arg0; + const char *arg1; + const char *mtxt; + int larg0; + int larg1; + int lbuff; + int mlen; + int par; + int tlen; + +/* Check inherited status. */ + result = NULL; + if( !astOK ) return result; + +/* Modify the tree to make the resulting transformation functions more + natural to human readers. */ + newtree = CopyTree( tree, status ); + ComplicateTree( &newtree, status ); + +/* If we are producing an axis label... */ + if( !mathmap ) { + +/* Fix all multiplicative constants to 1.0 if they multiply an OP_LDVAR + OP_SQRT or OP_POW node. This is on the assumption that the returned label + should not include any simple unit scaling (e.g. if the output label would + be "2.345*wavelength", we prefer simply to use "wavelength" since a scaled + wavelength is still a wavelength - i.e. simple scaling does not change + the dimensions of a quantity). */ + FixConstants( &newtree, 1, status ); + +/* Simplify the tree again to get rid of the 1.0 terms which may have + been introduced by the previous line (but do not re-introduce any + standardisations - removing them was the reason for calling ComplicateTree). + If this simplication introduces any changes, try fixing multiplicative + constants again, and so on, until no more changes occur. */ + while( SimplifyTree( &newtree, 0, status ) ) { + FixConstants( &newtree, 1, status ); + } + + } + +/* Produce a string describing the action performed by the UnitNode at + the head of the supplied tree, and then invoke this function recursively + to format any arguments of the head node. */ + +/* Constant valued nodes... just format the constant in a local buffer and + then copy the buffer. */ + if( newtree->con != AST__BAD ) { + lbuff = sprintf( buff, "%.*g", DBL_DIG, newtree->con ); + result = astStore( NULL, buff, lbuff + 1 ); + +/* "Load Variable Value" nodes - return the variable name. If this is a + recursive call to this function, and we are producing a label, append a + single space before and after the name. */ + } else if( newtree->opcode == OP_LDVAR ) { + tlen = strlen( newtree->name ); + + if( !mathmap && !top ){ + result = astMalloc( tlen + 3 ); + if( result ) { + result[ 0 ] = ' '; + memcpy( result + 1, newtree->name, tlen ); + memcpy( result + tlen + 1, " ", 2 ); + } + + } else if( mathmap == 2 ) { + + if( newtree->mult ) { + mlen = newtree->mult->symlen; + mtxt = newtree->mult->sym; + } else { + mlen = 0; + mtxt = NULL; + } + + result = astMalloc( tlen + 1 + mlen ); + if( result ) { + if( mtxt ) memcpy( result, mtxt, mlen ); + memcpy( result + mlen, newtree->name, tlen + 1 ); + } + + } else { + result = astStore( NULL, newtree->name, tlen + 1 ); + } + +/* Single argument functions... place the argument in parentheses after + the function name. */ + } else if( newtree->opcode == OP_LOG ) { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + if( mathmap == 1 ) { + result = astMalloc( larg0 + 8 ); + if( result ) memcpy( result, "log10(", 7 ); + a = result + 6; + } else { + result = astMalloc( larg0 + 6 ); + if( result ) memcpy( result, "log(", 5 ); + a = result + 4; + } + if( result ){ + memcpy( a, arg0, larg0 + 1 ); + memcpy( a + larg0, ")", 2 ); + } + arg0 = astFree( (void *) arg0 ); + + } else if( newtree->opcode == OP_LN ) { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + if( mathmap == 1 ) { + result = astMalloc( larg0 + 6 ); + if( result ) memcpy( result, "log(", 5 ); + a = result + 4; + } else { + result = astMalloc( larg0 + 5 ); + if( result ) memcpy( result, "ln(", 4 ); + a = result + 3; + } + if( astOK ){ + memcpy( a, arg0, larg0 ); + memcpy( a + larg0, ")", 2 ); + } + arg0 = astFree( (void *) arg0 ); + + } else if( newtree->opcode == OP_EXP ) { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + result = astMalloc( larg0 + 6 ); + if( result ){ + memcpy( result, "exp(", 5 ); + memcpy( result + 4, arg0, larg0 ); + memcpy( result + 4 + larg0, ")", 2 ); + } + arg0 = astFree( (void *) arg0 ); + + } else if( newtree->opcode == OP_SQRT ) { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + result = astMalloc( larg0 + 7 ); + if( result ){ + memcpy( result, "sqrt(", 6 ); + memcpy( result + 5, arg0, larg0 ); + memcpy( result + 5 + larg0, ")", 2 ); + } + arg0 = astFree( (void *) arg0 ); + +/* POW... the exponent (arg[1]) is always a constant and so does not need + to be placed in parentheses. The first argument only needs to be + placed in parentheses if it is a two arg node (except we also put it + in parentheses if it is an OP_LDVAR node and "mathmap" is zero - this is + because such OP_LDVAR nodes will correspond to axis labels which will + have spaces before and after them which would look odd if not encloses + in parentheses). */ + } else if( newtree->opcode == OP_POW ) { + + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + + arg1 = MakeExp( newtree->arg[ 1 ], mathmap, 0, status ); + larg1 = strlen( arg1 ); + + if( newtree->arg[ 0 ]->narg == 2 || + (newtree->arg[ 0 ]->opcode == OP_LDVAR && !mathmap) ) { + par = 1; + result = astMalloc( larg0 + larg1 + 7 ); + if( result ) memcpy( result, "(", 2 ); + a = result + 1; + } else { + par = 0; + result = astMalloc( larg0 + larg1 + 5 ); + a = result; + } + + if( result ) { + memcpy( a, arg0, larg0 ); + a += larg0; + if( par ) *(a++) = ')'; + memcpy( a, "**", 3 ); + a += 2; + memcpy( a, arg1, larg1 ); + a += larg1; + *a = 0; + } + + arg0 = astFree( (void *) arg0 ); + arg1 = astFree( (void *) arg1 ); + +/* DIV... the first argument (numerator) never needs to be in parentheses. + The second argument (denominator) only needs to be placed in parentheses + if it is a MULT node. */ + } else if( newtree->opcode == OP_DIV ) { + + if( mathmap == 2 && ( sunit = ModifyPrefix( newtree, status ) ) ) { + result = (char *) MakeExp( sunit, mathmap, 0, status ); + sunit = FreeTree( sunit, status ); + + } else { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + + arg1 = MakeExp( newtree->arg[ 1 ], mathmap, 0, status ); + larg1 = strlen( arg1 ); + + if( newtree->arg[ 1 ]->opcode == OP_MULT && + strchr( arg1, '*' ) ) { + par = 1; + result = astMalloc( larg0 + larg1 + 4 ); + } else { + par = 0; + result = astMalloc( larg0 + larg1 + 2 ); + } + + if( result ) { + memcpy( result, arg0, larg0 ); + a = result + larg0; + *(a++) = '/'; + if( par ) *(a++) = '('; + memcpy( a, arg1, larg1 ); + a += larg1; + if( par ) *(a++) = ')'; + *a = 0; + } + + arg0 = astFree( (void *) arg0 ); + arg1 = astFree( (void *) arg1 ); + } + +/* MULT... the second argument never needs to be in parentheses. The first + argument only needs to be placed in parentheses if it is a DIV or POW + node. */ + } else if( newtree->opcode == OP_MULT ) { + if( mathmap == 2 && ( sunit = ModifyPrefix( newtree, status ) ) ) { + result = (char *) MakeExp( sunit, mathmap, 0, status ); + sunit = FreeTree( sunit, status ); + + } else { + arg0 = MakeExp( newtree->arg[ 0 ], mathmap, 0, status ); + larg0 = strlen( arg0 ); + + arg1 = MakeExp( newtree->arg[ 1 ], mathmap, 0, status ); + larg1 = strlen( arg1 ); + +/* If this is a top-level entry and we are producing an axis label, do + not include any constant multiplicative terms. */ + if( top && !mathmap ) { + if( newtree->arg[ 0 ]->con != AST__BAD ) arg0 = astFree( (void *) arg0 ); + if( newtree->arg[ 1 ]->con != AST__BAD ) arg1 = astFree( (void *) arg1 ); + } + +/* If we have two arguments, concatentate them, placing the operands in + parentheses if necessary. */ + if( arg0 && arg1 ) { + + if( ( newtree->arg[ 0 ]->opcode == OP_DIV && + strchr( arg0, '/' ) ) || + ( newtree->arg[ 0 ]->opcode == OP_POW && + strstr( arg0, "**" ) ) ) { + par = 1; + result = astMalloc( larg0 + larg1 + 4 ); + if( result ) result[ 0 ] = '('; + a = result + 1; + } else { + par = 0; + result = astMalloc( larg0 + larg1 + 2 ); + a = result; + } + + if( result ) { + memcpy( a, arg0, larg0 ); + a += larg0; + if( par ) *(a++) = ')'; + *(a++) = '*'; + memcpy( a, arg1, larg1 ); + a += larg1; + *a = 0; + } + + arg0 = astFree( (void *) arg0 ); + arg1 = astFree( (void *) arg1 ); + +/* If we do not have two arguments, just return the one we do have. */ + } else if( arg0 ){ + result = (char *) arg0; + + } else { + result = (char *) arg1; + } + } + } + +/* Free the complicated tree. */ + newtree = FreeTree( newtree, status ); + +/* Free the returned string if an error has occurred. */ + if( !astOK ) result = astFree( result ); + +/* Return the result. */ + return (const char *) result; +} + +static void MakeKnownUnit( const char *sym, const char *label, const char *exp, int *status ){ +/* +* Name: +* MakeKnownUnit + +* Purpose: +* Create a KnownUnit structure describing a known unit. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void MakeKnownUnit( const char *sym, const char *label, const char *exp, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates a KnownUnit structure decribing a known unit, +* and adds it to the head of the linked list of known units stored in +* a module variable. + +* Parameters: +* sym +* A pointer to a string which can be used as a symbol to represent +* the new named unit. Once defined, this symbol can be included within +* the definition of other derived units. The string should contain +* only alphabetical characters (no digits, spaces, punctuation, +* etc). Symbols are case sensitive (e.g. "s" is second, but "S" is +* Siemens). The string should not include any multiplier prefix. +* label +* Pointer to a null terminated string containing the label for +* the required units. No restriction on content. +* exp +* This should be a pointer to a null terminated string containing +* a definition of the required unit. See the description of the +* "in" and "out" parameters for the astUnitMapper function. +* +* A NULL pointer or a blank string may supplied for "exp", which +* is interpreted as a request for a new basic unit to be created with +* the symbol and label given by the other parameters. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The supplied symbol and label strings are not copied. The +* supplied pointers are simply stored in the returned structure. +* Therefore the strings to which the pointers point should not be +* modified after this function returned (in fact this function is +* always called with literal strings for these arguments). +*/ + +/* Local Variables: */ + KnownUnit *result; + +/* Check the global error status. */ + if( !astOK ) return; + +/* Indicate that subsequent memory allocations may never be freed (other + than by any AST exit handler). */ + astBeginPM; + +/* Allocate memory for the structure, and check the returned pointer can + be used safely. */ + result = astMalloc( sizeof( KnownUnit ) ); + if( astOK ) { + +/* In case of errors, first nullify the pointer to the next KnownUnit. */ + result->next = NULL; + +/* Store the supplied label and symbol pointers. */ + result->sym = sym; + result->label = label; + +/* Store the length of the symbol (without the trailing null character). */ + result->symlen = strlen( sym ); + +/* Store the length of the label (without the trailing null character). */ + result->lablen = strlen( label ); + +/* Create a tree of UnitNodes describing the unit if an expression was + supplied. */ + result->head = exp ? CreateTree( exp, 1, 0, status ) : NULL; + +/* Unit aliases are replaced in use by the KnownUnit pointed to by the + "use" component of the structure. Indicate this KnownUnitis not an + alias by setting its "use" component NULL. */ + result->use = NULL; + } + +/* Mark the end of the section in which memory allocations may never be + freed (other than by any AST exit handler). */ + astEndPM; + +/* If an error has occurred, free any returned structure. */ + if( !astOK ) { + result->head = FreeTree( result->head, status ); + result = astFree( result ) ; + +/* Otherwise, add the new KnownUnit to the head of the linked list of + known units. */ + } else { + result->next = known_units; + known_units = result; + } + +} + +static AstMapping *MakeMapping( UnitNode *tree, int *status ) { +/* +* Name: +* MakeMapping + +* Purpose: +* Create a new Mapping from a given tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* AstMapping *MakeMapping( UnitNode *tree ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates a Mapping with a forward transformation equal +* to the transformation described by the tree of UnitNodes. The head +* node of the tree corresponds to the output of the Mapping. + +* Parameters: +* tree +* The UnitNode at the head of the tree to be used. It should have +* exactly one OP_LDVAR node, and should have been simplified using +* the SimplifyTree function. + +* Returned Value: +* A pointer to the Mapping. Its Nin and Nout attributes will both be 1. + +* Notes: +* - A value of NULL will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + AstMapping *result; + UnitNode *inv; + UnitNode *src; + const char *fwdexp; + char *fwdfun; + const char *invexp; + char *invfun; + int lfwd; + int linv; + +/* Initialise. */ + result = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* First see if a UnitMap can be used to represent the Mapping from + input units to output units. This will be the case if the supplied tree + consists of a aingle OP_LDVAR node (corresponding to the input units). */ + if( tree->opcode == OP_LDVAR ) { + result = (AstMapping *) astUnitMap( 1, "", status ); + +/* Now see if a UnitMap or ZoomMap can be used to represent the Mapping from + input units to output units. This will be the case if the supplied tree + consists of a OP_MULT node with one constant argument and on OP_LDVAR + argument (corresponding to the input units). The standardisation done by + SimplifyTree will have ensured that the constant will be argument 0 + (and will also have converted "x/k" trees into "(1/k)*x" trees). */ + } else if( tree->opcode == OP_MULT && + tree->arg[ 0 ]->con != AST__BAD && + tree->arg[ 1 ]->opcode == OP_LDVAR ) { + + if( tree->arg[ 0 ]->con == 1.0 ) { + result = (AstMapping *) astUnitMap( 1, "", status ); + } else { + result = (AstMapping *) astZoomMap( 1, tree->arg[ 0 ]->con, "", status ); + } + +/* For other trees we need to create a MathMap. */ + } else { + +/* Format the supplied tree as an algebraic expression, and get its length. */ + fwdexp = MakeExp( tree, 1, 1, status ); + lfwd = strlen( fwdexp ); + +/* The MathMap constructor requires the forward and inverse + transformation functions to be specified as equations (i.e. including an + equals sign). We use the output variable name "output_units" (the + astUnitMapper function creates the supplied tree usign the variable + name "input_units" ). */ + lfwd += 13; + +/* Invert the supplied tree and create an algebraic expression from it. */ + src = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) src->name = astStore( NULL, "output_units", 13 ); + inv = InvertTree( tree, src, status ); + if( !inv ) { + src = FreeTree( src, status ); + astError( AST__BADUN, "MakeMapping(Unit): Failed to invert " + "supplied tree '%s' (internal AST programming error).", status, + fwdexp ); + +/* If inverted succesfully (which it should be since astUnitMapper should + have checked this)... */ + } else { + +/* Format the inverted tree as an algebraic expression, and get its + length, adding on extra characters for the variable name ("input_units") + and equals sign. */ + invexp = MakeExp( inv, 1, 1, status ); + linv = strlen( invexp ); + linv += 12; + +/* Allocate memory for the transformation functions, plus an extra + character for the trailing null. */ + fwdfun = astMalloc( lfwd + 1 ); + invfun = astMalloc( linv + 1 ); + if( invfun ) { + memcpy( fwdfun, "output_units=", 14 ); + memcpy( invfun, "input_units=", 13 ); + +/* Append the expressions following the equals signs. */ + strcpy( fwdfun + 13, fwdexp ); + strcpy( invfun + 12, invexp ); + +/* Create the MathMap. */ + result = (AstMapping *) astMathMap( 1, 1, 1, + (const char **) &fwdfun, 1, + (const char **) &invfun, + "SimpFI=1,SimpIF=1", status ); + } + +/* Free resources. */ + inv = FreeTree( inv, status ); + fwdfun = astFree( fwdfun ); + invfun = astFree( invfun ); + invexp = astFree( (void *) invexp ); + } + fwdexp = astFree( (void *) fwdexp ); + } + +/* Free any result if an error occurred. */ + if( !astOK ) result = astAnnul( result ); + +/* Return the answer. */ + return result; +} + +static UnitNode *MakeLabelTree( const char *lab, int nc, int *status ){ +/* +* Name: +* MakeLabelTree + +* Purpose: +* Convert an axis label into a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *MakeLabelTree( const char *lab, int nc, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function converts an axis label into a tree of UnitNodes. +* It is assumed the supplied label represents some "basic" label +* modified by the application of one or more single function arguments +* and/or exponentiation operators. The (single) OP_LDVAR node in the +* returned tree refers to the basic label (it is stored as the "name" +* component of UnitNode structure). + +* Parameters: +* lab +* The label expression. +* nc +* The number of characters from "lab" to use. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which forms the head of a tree of UnitNodes +* representing the supplied label expression. + +* Notes: +* - A NULL value is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + Oper op; + UnitNode *result; + char buff[ 10 ]; + const char *c; + const char *exp; + int depth; + int i; + int oplen; + int n; + double con; + +/* Initialise */ + result = NULL; + oplen = 0; + +/* Check the global error status, and that we have a string. */ + if ( !astOK || !lab || !nc ) return result; + +/* Get a pointer to the first non-blank character, and store the number of + characters to examine (this excludes any trailing white space). */ + exp = lab; + while( isspace( *exp ) ) exp++; + c = lab + nc - 1; + while( c >= exp && isspace( *c ) ) c--; + nc = c - exp + 1; + +/* Scan through the supplied string looking for the first pow operator at + zero depth of nesting within parentheses. */ + depth = 0; + c = exp; + i = 0; + op = OP_NULL; + while( i < nc && *c ){ + +/* If this character is an opening parenthesis, increment the depth of + nesting. */ + if( *c == '(' ) { + depth++; + +/* If this character is an closing parenthesis, decrement the depth of + nesting. Report an error if it ever goes negative. */ + } else if( *c == ')' ) { + depth--; + if( depth < 0 && astOK ) { + astError( AST__BADUN, "Missing opening parenthesis." , status); + break; + } + +/* Ignore all other characters unless they are at zero depth of nesting. + Also ignore spaces. */ + } else if( depth == 0 && !isspace( *c ) ) { + +/* Compare the next part of the string with each of the "pow" operators. */ + if( !strncmp( c, "**", 2 ) ) { + op = OP_POW; + oplen = 2; + } else if( *c == '^' ) { + op = OP_POW; + oplen = 1; + } + +/* If an operator was found, break out of the loop. */ + if( op != OP_NULL ) break; + } + +/* Pass on to check the next character. */ + i++; + c++; + } + +/* If a "pow" operator was found, the strings on either side of it should be + valid unit expressions, in which case we use this routine recursively to + create corresponding trees of UnitNodes. */ + if( op != OP_NULL ) { + +/* Create a UnitNode for the operator. */ + result = NewNode( NULL, op, status ); + if( astOK ) { + +/* Create a tree of unit nodes from the string which precedes the binary + operator. Report an error if it cannot be done. */ + result->arg[ 0 ] = MakeLabelTree( exp, i, status ); + if( !result->arg[ 0 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand before '%s'.", status, buff ); + } + +/* Create a tree of unit nodes from the string which follows the binary + operator. Report an error if it cannot be done. */ + result->arg[ 1 ] = MakeLabelTree( c + oplen, nc - i - oplen, status ); + if( !result->arg[ 1 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand after '%s'.", status, buff ); + } + } + +/* If no binary operator was found at depth zero, see if the supplied string + starts with a function name (the only legal place for a function name + given that the string has no binary operators at depth zero). */ + } else { + if( !strncmp( exp, "sqrt(", 5 ) || !strncmp( exp, "SQRT(", 5 ) ) { + op = OP_SQRT; + oplen = 4; + } else if( !strncmp( exp, "exp(", 4 ) || !strncmp( exp, "EXP(", 4 ) ) { + op = OP_EXP; + oplen = 3; + } else if( !strncmp( exp, "ln(", 3 ) || !strncmp( exp, "LN(", 3 ) ) { + op = OP_LN; + oplen = 2; + } else if( !strncmp( exp, "log(", 4 ) || !strncmp( exp, "LOG(", 4 ) ) { + op = OP_LOG; + oplen = 3; + } + +/* If a function was found, the string following the function name + (including the opening parenthesis) should form a legal units + expresssion (all the supported functions take a single argument and + so we do not need to worry about comma-separated lists of function + arguments). Use this routine recursively to create a tree of UnitNodes + from the string which forms the function argument. */ + if( op != OP_NULL ) { + +/* Create a UnitNode for the function. */ + result = NewNode( NULL, op, status ); + if( astOK ) { + +/* Create a tree of unit nodes from the string which follows the function + name. Report an error if it cannot be done. */ + result->arg[ 0 ] = MakeLabelTree( exp + oplen, nc - oplen, status ); + if( !result->arg[ 0 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing argument for '%s'.", status, buff ); + } + } + +/* Arrive here if the supplied string does not contain a POW operator + or function at depth zero. Check to see if the whole string is contained + within parentheses, In which we interpret the contents of the + parentheses as a units expression. It is safe simply to check the + first and last characters (a string like "(fred)(Harry)" is not a + legal possibility since there should be an operator in the middle).*/ + } else if( nc > 0 && ( exp[ 0 ] == '(' && exp[ nc - 1 ] == ')' ) ) { + result = MakeLabelTree( exp + 1, nc - 2, status ); + +/* Does the string begin with a numerical constant? */ + } else if( ConStart( exp, &con, &n, status ) == 1 ) { + +/* If the entire string was a numerical constant, represent it by a LDCON + node. */ + if( n == nc ) { + result = NewNode( NULL, OP_LDCON, status ); + if( astOK ) result->con = con; + +/* If there was anything following the numerical constant, report an + error. */ + } else if( astOK ){ + astError( AST__BADUN, "Missing operator after " + "numerical string '%.*s'.", status, n, exp ); + } + +/* The only legal possibility left is that the string represents the basic + label. Create an OP_LDVAR node for it and store the basic label as + the node name, omitting any enclosing white space. */ + } else { + result = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) { + result->name = astStore( NULL, exp, nc + 1 ); + if( astOK ) ( (char *) result->name)[ nc ] = 0; + } + } + } + +/* Free any returned tree if an error has occurred. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the result. */ + return result; +} + +static UnitNode *MakeTree( const char *exp, int nc, int lock, int *status ){ +/* +* Name: +* MakeTree + +* Purpose: +* Convert an algebraic units expression into a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *MakeTree( const char *exp, int nc, int lock, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function converts an algebraic units expression into a tree of +* UnitNodes. It is a service routine for CreateTree. The roots of the +* returned tree (i.e. the LDVAR nodes) refer to the unit symbols +* contained within the supplied expression (i.e. definitions of these +* units are not grafted onto the tree in place of the original nodes, +* as is done by CreateTree). + +* Parameters: +* exp +* The units expression. This should not include any leading or +* trailing spaces. +* nc +* The number of characters from "exp" to use. +* lock +* Use a mutex to guard access to the KnownUnits list? +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to a UnitNode which forms the head of a tree of UnitNodes +* representing the supplied unit expression. + +* Notes: +* - A NULL value is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*/ + +/* Local Variables: */ + KnownUnit *munit; + KnownUnit *unit; + Multiplier *mmult; + Multiplier *mult; + Oper op; + UnitNode *result; + char buff[ 10 ]; + char d; + const char *c; + double con; + int depth; + int i; + int l; + int maxlen; + int n; + int oplen; + int plural; + +/* Initialise */ + result = NULL; + +/* Check the global error status, and that we have a string. */ + if ( !astOK || !exp || nc <= 0 ) return result; + +/* Scan through the supplied string from the end to the start looking for + the last multiplication or division operator at zero depth of nesting + within parentheses. We go backwards through the string in order to + give the correct priority to multiple division operators (i.e. "a/b/c" + needs to be interpreted as "(a/b)/c", not "a/(b/c)"). */ + op = OP_NULL; + oplen = 1; + depth = 0; + c = exp + nc - 1; + i = nc - 1; + while( i >= 0 ){ + +/* If this character is an opening parenthesis, decrement the depth of + nesting. Report an error if it ever goes negative. */ + if( *c == '(' ) { + depth--; + if( depth < 0 && astOK ) { + astError( AST__BADUN, "Missing closing parenthesis." , status); + break; + } + +/* An opening parenthesis at level zero must always be either the first + character in the string, or be preceded by the name of a function, or + be preceded by an operator. If none of these are true, assume there is + an implicit multiplication operator before the parenthesis. */ + if( depth == 0 && i > 0 ) { + d = *( c - 1 ); + if( d != '*' && d != '/' && d != '^' && d != '.' && d != ' ' && + !EndsWith( c, i + 1, "sqrt(", status ) && !EndsWith( c, i + 1, "exp(", status ) && + !EndsWith( c, i + 1, "ln(", status ) && !EndsWith( c, i + 1, "log(", status ) ) { + op = OP_MULT; + oplen = 0; + break; + } + } + +/* If this character is an closing parenthesis, increment the depth of + nesting. */ + } else if( *c == ')' ) { + depth++; + +/* A closing parenthesis at level zero must always be either the last + character in the string, or be followed by an operator. If neither of + these are true, assume there is an implicit multiplication operator. */ + if( depth == 1 && i < nc - 1 ) { + d = *(c+1); + if( d != '*' && d != '/' && d != '^' && d != '.' && d != ' ') { + op = OP_MULT; + oplen = 0; + +/* Correct "i" so that it gives the length of the left hand operand of + the implicit MULT operator, correct "c" so that it points to the first + character in the right hand operand, and leave the loop. */ + i++; + c++; + break; + } + } + +/* Ignore all other characters unless they are at zero depth of nesting. */ + } else if( depth == 0 ) { + +/* Compare the next part of the string with each of the multiplication + and division operators. */ + if( *c == '/' ) { + op = OP_DIV; + + } else if( *c == ' ' ) { + op = OP_MULT; + +/* An asterisk is only treated as a multiplication symbol if it does not occur + before or after another asterisk. */ + } else if( *c == '*' ) { + if( c == exp ) { + if( *(c+1) != '*' ) op = OP_MULT; + } else if( i == nc - 1 ) { + if( *(c-1) != '*' ) op = OP_MULT; + } else { + if( *(c+1) != '*' && *(c-1) != '*' ) op = OP_MULT; + } + +/* A dot is only treated as a multiplication symbol if it does not occur + between two digits. */ + } else if( *c == '.' ) { + if( ( c == exp || !isdigit( *(c-1) ) ) && + ( i == nc - 1 || !isdigit( *(c+1) ) ) ) { + op = OP_MULT; + } + } + } + +/* If an operator was found, break out of the loop. */ + if( op != OP_NULL ) break; + +/* Pass on to check the next character. */ + i--; + c--; + } + +/* If a multiplication or division operator was found, the strings on either + side of it should be valid unit expressions, in which case we use this + routine recursively to create corresponding trees of UnitNodes. */ + if( op != OP_NULL ) { + +/* Create a UnitNode for the binary operator. */ + result = NewNode( NULL, op, status ); + if( astOK ) { + +/* Create a tree of unit nodes from the string which precedes the binary + operator. Report an error if it cannot be done. */ + result->arg[ 0 ] = MakeTree( exp, i, lock, status ); + if( !result->arg[ 0 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand before '%s'.", status, buff ); + } + +/* Create a tree of unit nodes from the string which follows the binary + operator. Report an error if it cannot be done. */ + result->arg[ 1 ] = MakeTree( c + oplen, nc - i - oplen, lock, status ); + if( !result->arg[ 1 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand after '%s'.", status, buff ); + } + } + +/* If no multiplication or division operator was found at depth zero, check + that the final depth of nesting was zero. Report an error if not. */ + } else if( depth > 0 && astOK ) { + astError( AST__BADUN, "Missing opening parenthesis." , status); + +/* Otherwise check for a "Pow" operator at depth zero. */ + } else { + +/* Scan through the supplied string looking for the first pow operator at + zero depth of nesting within parentheses. */ + depth = 0; + c = exp; + i = 0; + while( i < nc && *c ){ + +/* If this character is an opening parenthesis, increment the depth of + nesting. */ + if( *c == '(' ) { + depth++; + +/* If this character is an closing parenthesis, decrement the depth of + nesting. Report an error if it ever goes negative. */ + } else if( *c == ')' ) { + depth--; + if( depth < 0 && astOK ) { + astError( AST__BADUN, "Missing opening parenthesis." , status); + break; + } + +/* Ignore all other characters unless they are at zero depth of nesting. */ + } else if( depth == 0 ) { + +/* Compare the next part of the string with each of the "pow" operators. */ + if( !strncmp( c, "**", 2 ) ) { + op = OP_POW; + oplen = 2; + } else if( *c == '^' ) { + op = OP_POW; + oplen = 1; + } + +/* If an operator was found, break out of the loop. */ + if( op != OP_NULL ) break; + } + +/* Pass on to check the next character. */ + i++; + c++; + } + +/* If a "pow" operator was found, the strings on either side of it should be + valid unit expressions, in which case we use this routine recursively to + create corresponding trees of UnitNodes. */ + if( op != OP_NULL ) { + +/* Create a UnitNode for the operator. */ + result = NewNode( NULL, op, status ); + if( astOK ) { + +/* Create a tree of unit nodes from the string which precedes the binary + operator. Report an error if it cannot be done. */ + result->arg[ 0 ] = MakeTree( exp, i, lock, status ); + if( !result->arg[ 0 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand before '%s'.", status, buff ); + } + +/* Create a tree of unit nodes from the string which follows the binary + operator. Report an error if it cannot be done. */ + result->arg[ 1 ] = MakeTree( c + oplen, nc - i - oplen, lock, status ); + if( !result->arg[ 1 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing operand after '%s'.", status, buff ); + } + } + +/* If no binary operator was found at depth zero, see if the supplied string + starts with a function name (the only legal place for a function name + given that the string has no binary operators at depth zero). */ + } else { + if( !strncmp( exp, "sqrt(", 5 ) || !strncmp( exp, "SQRT(", 5 ) ) { + op = OP_SQRT; + oplen = 4; + } else if( !strncmp( exp, "exp(", 4 ) || !strncmp( exp, "EXP(", 4 ) ) { + op = OP_EXP; + oplen = 3; + } else if( !strncmp( exp, "ln(", 3 ) || !strncmp( exp, "LN(", 3 ) ) { + op = OP_LN; + oplen = 2; + } else if( !strncmp( exp, "log(", 4 ) || !strncmp( exp, "LOG(", 4 ) ) { + op = OP_LOG; + oplen = 3; + } + +/* If a function was found, the string following the function name + (including the opening parenthesis) should form a legal units + expresssion (all the supported functions take a single argument and + so we do not need to worry about comma-separated lists of function + arguments). Use this routine recursively to create a tree of UnitNodes + from the string which forms the function argument. */ + if( op != OP_NULL ) { + +/* Create a UnitNode for the function. */ + result = NewNode( NULL, op, status ); + if( astOK ) { + +/* Create a tree of unit nodes from the string which follows the function + name. Report an error if it cannot be done. */ + result->arg[ 0 ] = MakeTree( exp + oplen, nc - oplen, lock, status ); + if( !result->arg[ 0 ] && astOK ) { + for( i = 0; i < oplen; i++ ) buff[ i ] = c[ i ]; + buff[ oplen ] = 0; + astError( AST__BADUN, "Missing argument for '%s'.", status, buff ); + } + } + +/* Arrive here if the supplied string does not contain a binary operator + or function at depth zero. Check to see if the whole string is contained + within parentheses, In which we interpret the contents of the + parentheses as a units expression. It is safe simply to check the + first and last characters (a string like "(fred)(Harry)" is not a + legal possibility since there should be an operator in the middle).*/ + } else if( exp[ 0 ] == '(' && exp[ nc - 1 ] == ')' ) { + result = MakeTree( exp + 1, nc - 2, lock, status ); + +/* Does the string begin with a numerical constant? */ + } else if( ConStart( exp, &con, &n, status ) == 1 ) { + +/* If the entire string was a numerical constant, represent it by a LDCON + node. */ + if( n == nc ) { + result = NewNode( NULL, OP_LDCON, status ); + if( astOK ) result->con = con; + +/* If there was anything following the numerical constant, report an + error. */ + } else if( astOK ){ + astError( AST__BADUN, "Missing operator after " + "numerical string '%.*s'.", status, n, exp ); + } + +/* Does the string represent one of the named constants? If so represent it + by a an appropriate operator. */ + } else if( nc == 2 && ( !strncmp( exp, "pi", 2 ) || + !strncmp( exp, "PI", 2 ) ) ) { + result = NewNode( NULL, OP_LDPI, status ); + + } else if( nc == 1 && ( !strncmp( exp, "e", 1 ) || + !strncmp( exp, "E", 1 ) ) ) { + result = NewNode( NULL, OP_LDE, status ); + +/* The only legal possibility left is that the string represents the name + of a basic unit, possibly prefixed by a multiplier character. */ + } else { + +/* See if the string ends with the symbol for any of the known basic + units. If it matches more than one basic unit, choose the longest. + First ensure descriptions of the known units are available. */ + mmult = NULL; + plural = 0; + while( 1 ) { + unit = GetKnownUnits( lock, status ); + + maxlen = -1; + munit = NULL; + while( unit ) { + if( SplitUnit( exp, nc, unit->sym, 1, &mult, &l, status ) ) { + if( l > maxlen ) { + maxlen = l; + munit = unit; + mmult = mult; + } + } + unit = unit->next; + } + +/* If the above did not produce a match, try matching the unit symbol + case insensitive. */ + if( !munit ) { + unit = GetKnownUnits( lock, status ); + while( unit ) { + if( SplitUnit( exp, nc, unit->sym, 0, &mult, &l, status ) ) { + if( l > maxlen ) { + maxlen = l; + munit = unit; + mmult = mult; + } + } + unit = unit->next; + } + } + +/* If the above did not produce a match, try matching the unit label + case insensitive. */ + if( !munit ) { + unit = GetKnownUnits( lock, status ); + while( unit ) { + if( SplitUnit( exp, nc, unit->label, 0, &mult, &l, status ) ) { + if( l > maxlen ) { + maxlen = l; + munit = unit; + mmult = mult; + } + } + unit = unit->next; + } + } + +/* If we still do not have a match, and if the string ends with "s", try + removing the "s" (which could be a plural as in "Angstroms") and + trying again. */ + if( !munit && nc > 1 && !plural && + ( exp[ nc - 1 ] == 's' || exp[ nc - 1 ] == 'S' ) ) { + plural = 1; + nc--; + } else { + break; + } + } + if( plural ) nc++; + +/* If a known unit and multiplier combination was found, create an + OP_LDVAR node from it. */ + unit = munit; + mult = mmult; + if( unit ) { + +/* If the unit is an alias for another unit, it will have a non-NULL + value for its "use" component.In this case, use the unit for which the + identified unit is an alias. */ + result = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) { + result->unit = unit->use ? unit->use : unit; + result->mult = mult; + result->name = astStore( NULL, result->unit->sym, result->unit->symlen + 1 ); + } + +/* If no known unit and multiplier combination was found, we assume the + string represents a new user-defined basic unit, possibly preceded by a + standard multiplier prefix. */ + } else { + +/* Check the string to see if starts with a known multiplier prefix (but + do not allow the multiplier to account for the entire string). */ + mult = GetMultipliers( status ); + c = exp; + while( mult ) { + n = nc - mult->symlen; + if( n > 0 && !strncmp( exp, mult->sym, mult->symlen ) ) { + c += mult->symlen; + break; + } + mult = mult->next; + } + if( !mult ) n = nc; + +/* Check there are no illegal characters in the following string. */ + for( i = 0; i < n && astOK; i++ ) { + if( !isalpha( c[ i ] ) ) { + astError( AST__BADUN, "Illegal character '%c' found.", status, c[ i ] ); + break; + } + } + +/* If succesfull, create an OP_LDVAR node for th user-defined basic unit. */ + if( astOK ) { + result = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) { + result->mult = mult; + result->name = astStore( NULL, c, n + 1 ); + if( astOK ) ( (char *) result->name)[ n ] = 0; + } + } + } + } + } + } + +/* Free any returned tree if an error has occurred. */ + if( !astOK ) result = FreeTree( result, status ); + +/* Return the result. */ + return result; +} + +static void MakeUnitAlias( const char *sym, const char *alias, int *status ){ +/* +* Name: +* MakeUnitAlias + +* Purpose: +* Create a KnownUnit structure describing an alias for a known unit. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void MakeUnitAlias( const char *sym, const char *alias, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates a KnownUnit structure decribing an alias for a +* known unit, and adds it to the head of the linked list of known units +* stored in a module variable. An alias is a KnownUnit which is +* identical to an existing known but which has a different symbol. + +* Parameters: +* sym +* A pointer to the symbol string of an existing KnwonUnit. The string +* should not include any multiplier prefix. +* alias +* A pointer to the symbol string to use as the alasi for the existing +* KnownUnit. The string should not include any multiplier prefix. +* status +* Pointer to the inherited status variable. + +* Notes: +* - The supplied symbol and label strings are not copied. The +* supplied pointers are simply stored in the returned structure. +* Therefore the strings to which the pointers point should not be +* modified after this function returned (in fact this function is +* always called with literal strings for these arguments). +*/ + +/* Local Variables: */ + KnownUnit *unit; + +/* Check the global error status. */ + if( !astOK ) return; + +/* Search the existing list of KnownUnits for the specified symbol. */ + unit = known_units; + while( unit ) { + if( !strcmp( sym, unit->sym ) ) { + +/* Create a new KnownUnit for the alias. It will becomes the head of the + known units chain. */ + MakeKnownUnit( alias, unit->label, NULL, status ); + +/* Store a pointer to the KnownUnit which is to be used in place of the + alias. */ + known_units->use = unit; + +/* Leave the loop. */ + break; + } + +/* Move on to check the next existing KnownUnit. */ + unit = unit->next; + } + +/* Report an error if the supplied unit was not found. */ + if( !unit ) { + astError( AST__INTER, "MakeUnitAlias(Unit): Cannot find existing " + "units \"%s\" to associate with the alias \"%s\" (AST " + "internal programming error).", status, sym, alias ); + } +} + +static UnitNode *ModifyPrefix( UnitNode *old, int *status ) { +/* +* Name: +* ModifyPrefix + +* Purpose: +* Replace a MULT or DIV node with a LDVAR and suitable multiplier. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *ModifyPrefix( UnitNode *old, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function checks the supplied node. If it is a DIV or MULT node +* in which one argument is an LDVAR and the other is a constant, then +* its checks to see if the constant can be absorbed into the LDVAR by +* changing the multiplier in the LDVAR node. If so, it returns a new +* node which is an LDVAR with the modified multiplier. Otherwise it +* returns NULL. + +* Parameters: +* old +* Pointer to an existing UnitNode to be checked. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new UnitNode. + +* Notes: +* - A value of NULL will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + Multiplier *mult; + Multiplier *mmult; + UnitNode *ldcon; + UnitNode *ldvar; + UnitNode *newtree; + UnitNode *result; + double con; + double cmult; + double r; + double rmin; + int recip; + int changed; + +/* Initialise. */ + result = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* Indicate that we have not yet found any reason to return a changed + node. */ + changed = 0; + +/* Check the supplied node is a DIV or MULT node. */ + if( old->opcode == OP_DIV || old->opcode == OP_MULT ) { + +/* Get a copy of the supplied tree which we can modify safely. */ + newtree = CopyTree( old, status ); + +/* Identify the LDVAR argument (if any). */ + if( newtree->arg[ 0 ]->opcode == OP_LDVAR ) { + ldvar = newtree->arg[ 0 ]; + + } else if( newtree->arg[ 1 ]->opcode == OP_LDVAR ) { + ldvar = newtree->arg[ 1 ]; + + } else { + ldvar = NULL; + } + +/* Identify the LDCON argument (if any). */ + if( newtree->arg[ 0 ]->opcode == OP_LDCON ) { + ldcon = newtree->arg[ 0 ]; + + } else if( newtree->arg[ 1 ]->opcode == OP_LDCON ) { + ldcon = newtree->arg[ 1 ]; + + } else { + ldcon = NULL; + } + +/* If either was not found, return NULL. */ + if( !ldvar || !ldcon ) { + newtree = FreeTree( newtree, status ); + +/* Otherwise, extract the multiplier constant. If there is no multiplier, the + constant is 1.0. */ + } else { + cmult = ldvar->mult ? ldvar->mult->scale: 1.0; + +/* Extract the constant. */ + con = ldcon->con; + +/* Combine the multiplier and the constant. The resulting constant is a + factor which is used to multiply the LDVAR quantity. If the original + node is a DIV node in which the LDVAR is in the denominator, then + flag that we need to reciprocate the new MULT node which represents + "constant*LDVAR" before returning. */ + if( newtree->opcode == OP_MULT ) { + con = con*cmult; + recip = 0; + } else { + con = cmult/con; + recip = ( ldvar == newtree->arg[ 1 ] ); + } + +/* Find the closest known multiplier to the new constant. */ + rmin = ( con > 1 ) ? con : 1.0/con; + mmult = NULL; + mult = GetMultipliers( status ); + while( mult ) { + r = ( con > mult->scale) ? con/mult->scale : mult->scale/con; + if( r < rmin ) { + mmult = mult; + rmin = r; + } + mult = mult->next; + } + +/* Modify the constant to take account of the new multiplier chosen + above. "mmult" will be NULL if the best multiplier is unity. */ + if( mmult ) con = con/mmult->scale; + +/* If they have changed, associate the chosen multiplier with the LDVAR node, + and the constant with the LDCON node. */ + if( ldvar->mult != mmult ) { + ldvar->mult = mmult; + changed = 1; + } + + if( ldcon->con != con ) { + ldcon->con = con; + changed = 1; + } + +/* Unless the node is proportional to the reciprocal of the variable, the + new node should be a MULT node (it may originally have been a DIV). */ + if( !recip ) { + if( newtree->opcode != OP_MULT ){ + newtree->opcode = OP_MULT; + changed = 1; + } + +/* If the constant is 1.0 we can just return the LDVAR node by itself. */ + if( fabs( con - 1.0 ) < 1.0E-6 ) { + result = CopyTree( ldvar, status ); + newtree = FreeTree( newtree, status ); + changed = 1; + +/* Otherwise return the modified tree containing both LDVAR and LDCON nodes. */ + } else { + result = newtree; + } + +/* If the node is proportional to the reciprocal of the variable, the + new node will already be a DIV node and will have an LDCON as the first + argument (numerator) and an LDVAR as the second argument (denominator). */ + } else { + +/* The first argument (the numerator) should be the reciprocal of the constant + found above. */ + ldcon->con = 1.0/ldcon->con; + if( !astEQUAL( ldcon->con, old->arg[0]->con ) ) changed = 1; + +/* Return the modified tree containing both LDVAR and LDCON nodes. */ + result = newtree; + } + } + } + +/* If the new and old trees are equivalent, then we do not need to return + it. */ + if( !changed && result ) result = FreeTree( result, status ); + +/* Return the answer. */ + return result; +} + + +static UnitNode *NewNode( UnitNode *old, Oper code, int *status ) { +/* +* Name: +* NewNode + +* Purpose: +* Create and initialise a new UnitNode. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* UnitNode *NewNode( UnitNode *old, Oper code, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates and initialises a new UnitNode, or +* re-initialises an existing UnitNode to use a different op code. + +* Parameters: +* old +* Pointer to an existing UnitNode to be modified, or NULL to create +* a new UnitNode. +* code +* The op code for the new UnitNode. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* A pointer to the new UnitNode. + +* Notes: +* - A value of NULL will be returned if this function is invoked with +* the global error status set, or if it should fail for any reason. +*/ + +/* Local Variables: */ + UnitNode **args; + UnitNode *result; + int i; + +/* Initialise. */ + result = NULL; + args = NULL; + +/* Check the inherited status. */ + if( !astOK ) return result; + +/* If an existig UnitNode was supplied, free any memory used to hold + pointers to its arguments. */ + if( old ) { + old->arg = astFree( old->arg ); + result = old; + +/* Otherwise, allocate memory for a new structure. */ + } else { + result = astMalloc( sizeof( UnitNode ) ); + } + +/* Check the pointer can be used safely. */ + if( astOK ) { + +/* Initialise the members of the UnitNode structure. */ + result->opcode = code; + result->arg = NULL; + result->con = AST__BAD; + result->name = NULL; + result->unit = NULL; + result->mult = NULL; + result->narg = 0; + + switch( code ){ + case OP_LDPI: + result->con = PI; + break; + + case OP_LDE: + result->con = E; + break; + + case OP_LOG: + case OP_LN: + case OP_EXP: + case OP_SQRT: + result->narg = 1; + break; + + case OP_POW: + case OP_DIV: + case OP_MULT: + result->narg = 2; + break; + + default: + ; + } + +/* Allocate memory for the UnitNode pointers which will locate the + nodes forming the arguments to the new node. */ + args = astMalloc( (result->narg)*sizeof( UnitNode * ) ); + if( astOK ) { + result->arg = args; + +/* Initialise the argument pointers to NULL. */ + for( i = 0; i < result->narg; i++ ) args[ i ] = NULL; + } + } + +/* Free any result if an error occurred. */ + if( !astOK ) { + args = astFree( args ); + result = astFree( result ); + } + +/* Return the answer. */ + return result; +} + +static void RemakeTree( UnitNode **node, int *status ) { +/* +* Name: +* RemakeTree + +* Purpose: +* Replace derived units within a tree of UnitNodes by basic units. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* void RemakeTree( UnitNode **node, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function searches for LDVAR nodes (i.e. references to unit +* symbols) within the given tree, and replaces each such node which +* refers to known derived unit with a sub-tree of nodes which +* define the derived unit in terms of known basic units. + +* Parameters: +* node +* The address of a pointer to the UnitNode at the head of the tree +* which is to be simplified. On exit the supplied tree is freed and a +* pointer to a new tree is placed at the given address. +* status +* Pointer to the inherited status variable. + +*/ + +/* Local Variables: */ + KnownUnit *unit; + int i; + UnitNode *newnode; + +/* Check inherited status. */ + if( !astOK ) return; + +/* Initially, we have no replacement node */ + newnode = NULL; + +/* If this is an LDVAR node... */ + if( (*node)->opcode == OP_LDVAR ) { + +/* If the LDVAR node has a multiplier associated with it, we need to + introduce a OP_MULT node to perform the scaling. */ + if( (*node)->mult ) { + newnode = NewNode( NULL, OP_MULT, status ); + if( astOK ) { + newnode->arg[0] = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + newnode->arg[0]->con = 1.0/(*node)->mult->scale; + +/* See if the node refers to a known unit. If not, or if the known unit + is a basic unit (i.e. not a derived unit) use the supplied node for + the second argument of the OP_MULT node (without the multiplier). + Otherwise, use a copy of the tree which defines the derived unit. */ + unit = (*node)->unit; + if( unit && unit->head ) { + newnode->arg[1] = CopyTree( unit->head, status ); + } else { + newnode->arg[1] = CopyTree( *node, status ); + if( astOK ) newnode->arg[1]->mult = NULL; + } + } + } + +/* If no multiplier is supplied, the replacement node is simply the tree + which defines the unscaled unit (if known), or the original node (if + unknown). */ + } else { + unit = (*node)->unit; + if( unit && unit->head ) newnode = CopyTree( unit->head, status ); + } + +/* If this is not an LDVAR Node, remake the sub-trees which form the + arguments of this node. */ + } else { + for( i = 0; i < (*node)->narg; i++ ) { + RemakeTree( &((*node)->arg[ i ]), status ); + } + } + +/* If an error has occurred, free any new node. */ + if( !astOK ) newnode = FreeTree( newnode, status ); + +/* If we have a replacement node, free the supplied tree and return a + pointer to the new tree. */ + if( newnode ) { + FreeTree( *node, status ); + *node = newnode; + } +} + +static int ReplaceNode( UnitNode *target, UnitNode *old, UnitNode *new, int *status ) { +/* +* Name: +* ReplaceNode + +* Purpose: +* Replace a node within a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int ReplaceNode( UnitNode *target, UnitNode *old, UnitNode *new, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function replaces a specified node within a tree of UnitNodes +* with another given node. The original node is freed if found. + +* Parameters: +* target +* A pointer to the UnitNode at the head of the tree containing the +* node to be replaced. +* old +* A pointer to the UnitNode to be replaced. +* new +* A pointer to the UnitNode to replace "old". +* status +* Pointer to the inherited status variable. + +* Return Value: +* Non-zero if the "old" node was found and replaced (in which case +* the "old" node will have been freed). + +* Notes: +* - It is assumed that the "old" node occurs at most once within the +* target tree. +* - The node at the head of the target tree is not compared with the +* "old" node. It is assumed the called will already have done this. +* - A value of zero is returned if an error has already occurred, or +* if this function fails for any reason. + +*/ + +/* Local Variables: */ + int i; + int result; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Loop round the arguments of the node at the head of the target tree. + Break out of the loop as soone as the old node is found. */ + for( i = 0; i < target->narg; i++ ) { + +/* If this argument is the node to be replaced, free the old one and store + the new one, and then leave the loop. */ + if( target->arg[ i ] == old ) { + FreeTree( old, status ); + target->arg[ i ] = new; + result = 1; + break; + +/* Otherwise use this function recursively to search for the old node + within the current argument. */ + } else { + if( ReplaceNode( target->arg[ i ], old, new, status ) ) break; + } + } + +/* If an error has occurred, return zero. */ + if( !astOK ) result = 0; + +/* Return the answer. */ + return result; +} + +static int SimplifyTree( UnitNode **node, int std, int *status ) { +/* +* Name: +* SimplifyTree + +* Purpose: +* Simplify a tree of UnitNodes. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int SimplifyTree( UnitNode **node, int std, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* This function simplifies a tree of UnitNodes. It is assumed that +* all the OP_LDVAR nodes in the tree refer to the same basic unit. +* A primary purpose of this function is to standardise the tree so +* that trees which implement equivalent transformations but which +* have different structures can be compared (for instance, so that +* "2*x" and "x*2" are treated as equal trees). If "std" is non-zero, +* reducing the complexity of the tree is only of secondary importance. +* This explains why some "simplifications" actually produced trees which +* are more complicated. + +* Parameters: +* node +* The address of a pointer to the UnitNode at the head of the tree +* which is to be simplified. On exit the supplied tree is freed and a +* pointer to a new tree is placed at the given address. +* std +* If non-zero, perform standardisations. Otherwise only perform +* genuine simplifications. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if some change was made to the tree. + +*/ + +/* Local Variables: */ + int i; + UnitNode *newnode; + UnitNode *node1; + UnitNode *node2; + Oper op; + UnitNode **factors; + double *powers; + int nfactor; + double coeff; + int result; + +/* Initialise */ + result = 0; + +/* Check inherited status. */ + if( !astOK ) return result; + +/* Initiallially, we have no replacement node. */ + newnode = NULL; + +/* First replace any complex constant expressions any corresponding + OP_LDCON nodes. */ + FixConstants( node, 0, status ); + +/* Simplify the sub-trees corresponding to the arguments of the node at + the head of the supplied tree. */ + for( i = 0; i < (*node)->narg; i++ ) { + if( SimplifyTree( &( (*node)->arg[ i ] ), std, status ) ) result = 1; + } + +/* Now do specific simplifications appropriate to the nature of the node at + the head of the tree. */ + op = (*node)->opcode; + +/* Natural log */ +/* =========== */ +/* We standardise argument powers into coefficients of the LN value. */ + if( op == OP_LN ) { + +/* If the argument is a OP_EXP node, they cancel out. Return a copy of the + argument of OP_EXP node. */ + if( (*node)->arg[ 0 ]->opcode == OP_EXP ) { + newnode = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + +/* If the argument is an OP_POW node, rearrange the nodes to represent + k*ln(x) instead of ln(x**k) (note pow nodes always have a constant + exponent - this is checked in InvertConstants). SQRT arguments will + not occur because they will have been changed into POW nodes when the + arguments of the supplied head node were simplified above. */ + } else if( std && (*node)->arg[ 0 ]->opcode == OP_POW ) { + newnode = NewNode( NULL, OP_MULT, status ); + node1 = CopyTree( (*node)->arg[ 0 ]->arg[ 1 ], status ); + node2 = NewNode( NULL, OP_LN, status ); + if( astOK ) { + node2->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = node2; + } + } + +/* Common log */ +/* ========== */ +/* We standardise natural logs into common logs. */ + } else if( op == OP_LOG ) { + if( std ) { + newnode = NewNode( NULL, OP_DIV, status ); + node1 = NewNode( NULL, OP_LN, status ); + node2 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node1->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ], status ); + node2->con = log( 10.0 ); + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = node2; + } + } + +/* Exponential */ +/* =========== */ +/* We prefer to minimise the number of EXP nodes, so, for instance, we do not + change "exp(x*y)" to "exp(x)+exp(y)" (and the code for ADD nodes does + the inverse conversion). */ + } else if( op == OP_EXP ) { + +/* If the argument is an OP_LN node, they cancel out. Return a copy of the + argument of the OP_LN node. Common log arguments will not occur because + they will have been changed into natural logs when the arguments of + the supplied head node were simplified above. */ + if( (*node)->arg[ 0 ]->opcode == OP_LN ) { + newnode = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + } + +/* Square root */ +/* =========== */ +/* We standardise sqrt nodes into pow nodes. */ + } else if( op == OP_SQRT ) { + if( std ) { + newnode = NewNode( NULL, OP_POW, status ); + node1 = CopyTree( (*node)->arg[ 0 ], status ); + node2 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node2->con = 0.5; + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = node2; + } + } + +/* Exponentiation */ +/* ============== */ +/* We want to simplfy factors. So, for instance, (x*y)**k is converted to + (x**k)*(y**k). */ + } else if( op == OP_POW ) { + +/* If the first argument is an OP_EXP node, then change "(e**x)**k" into + "e**(k*x)" */ + if( (*node)->arg[ 0 ]->opcode == OP_EXP ) { + newnode = NewNode( NULL, OP_EXP, status ); + node1 = NewNode( NULL, OP_MULT, status ); + if( astOK ) { + node1->arg[ 0 ] = CopyTree( (*node)->arg[ 1 ], status ); + node1->arg[ 1 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + newnode->arg[ 0 ] = node1; + } + +/* "x**0" can be replaced by 1.0 */ + } else if( (*node)->arg[ 1 ]->con == 0.0 ) { + newnode = NewNode( NULL, OP_LDCON, status ); + if( astOK ) newnode->con = 1.0; + +/* "x**1" can be replaced by x */ + } else if( astEQUAL( (*node)->arg[ 1 ]->con, 1.0 ) ) { + newnode = CopyTree( (*node)->arg[ 0 ], status ); + +/* If the first argument is an OP_POW node, then change "(x**k1)**k2" into + "x**(k1*k2)" */ + } else if( (*node)->arg[ 0 ]->opcode == OP_POW ) { + newnode = NewNode( NULL, OP_POW, status ); + node1 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node1->con = ( (*node)->arg[ 0 ]->arg[ 1 ]->con )* + ( (*node)->arg[ 1 ]->con ); + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + newnode->arg[ 1 ] = node1; + } + +/* If the first argument is an OP_MULT node, then change "(x*y)**k" into + "(x**(k))*(y**(k))" */ + } else if( std && (*node)->arg[ 0 ]->opcode == OP_MULT ) { + newnode = NewNode( NULL, OP_MULT, status ); + node1 = NewNode( NULL, OP_POW, status ); + if( astOK ) { + node1->arg[ 1 ] = CopyTree( (*node)->arg[ 1 ], status ); + node2 = CopyTree( node1, status ); + node1->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 0 ], status ); + node2->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ]->arg[ 1 ], status ); + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = node2; + } + } + +/* Division. */ +/* ========= */ +/* We standardise divisions into corresponding multiplications. */ + } else if( op == OP_DIV ) { + +/* Division by 1 is removed. */ + if( astEQUAL( (*node)->arg[ 1 ]->con, 1.0 ) ){ + newnode = CopyTree( (*node)->arg[ 0 ], status ); + +/* Division by any other constant (except zero) is turned into a + multiplication by the reciprocal constant. */ + } else if( (*node)->arg[ 1 ]->con != AST__BAD ) { + if( (*node)->arg[ 1 ]->con != 0.0 ) { + newnode = NewNode( NULL, OP_MULT, status ); + node1 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node1->con = 1.0/(*node)->arg[ 1 ]->con; + newnode->arg[ 0 ] = node1; + newnode->arg[ 1 ] = CopyTree( (*node)->arg[ 0 ], status ); + } + } else { + astError( AST__BADUN, "Simplifying a units expression" + "requires a division by zero." , status); + } + +/* Other divisions "x/y" are turned into "x*(y**(-1))" */ + } else if( std ) { + newnode = NewNode( NULL, OP_MULT, status ); + node1 = NewNode( NULL, OP_POW, status ); + node2 = NewNode( NULL, OP_LDCON, status ); + if( astOK ) { + node2->con = -1.0; + node1->arg[ 0 ] = CopyTree( (*node)->arg[ 1 ], status ); + node1->arg[ 1 ] = node2; + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 0 ], status ); + newnode->arg[ 1 ] = node1; + } + } + +/* Multiplication */ +/* ============== */ + } else if( op == OP_MULT ) { + +/* If the right hand argument is constant, swap the arguments. */ + if( (*node)->arg[ 1 ]->con != AST__BAD ) { + newnode = NewNode( NULL, OP_MULT, status ); + if( astOK ) { + newnode->arg[ 0 ] = CopyTree( (*node)->arg[ 1 ], status ); + newnode->arg[ 1 ] = CopyTree( (*node)->arg[ 0 ], status ); + } + +/* Multiplication by zero produces a constant zero. */ + } else if( (*node)->arg[ 0 ]->con == 0.0 ){ + newnode = NewNode( NULL, OP_LDCON, status ); + if( astOK ) newnode->con = 0.0; + +/* Multiplication by 1 is removed. */ + } else if( astEQUAL( (*node)->arg[ 0 ]->con, 1.0 ) ){ + newnode = CopyTree( (*node)->arg[ 1 ], status ); + +/* For other MULT nodes, analyse the tree to find a list of all its + factors with an associated power for each one, and an overall constant + coefficient. */ + } else if( std ) { + FindFactors( (*node), &factors, &powers, &nfactor, &coeff, status ); + +/* Produce a new tree from these factors. The factors are standardised by + ordering them alphabetically (after conversion to a character string). */ + newnode = CombineFactors( factors, powers, nfactor, coeff, status ); + +/* Free resources */ + factors = astFree( factors ); + powers = astFree( powers ); + + } + } + +/* If we have produced a new node which is identical to the old node, + free it. Otherwise, indicate we have made some changes. */ + if( newnode ) { + if( !CmpTree( newnode, *node, 1, status ) ) { + newnode = FreeTree( newnode, status ); + } else { + result = 1; + } + } + +/* If an error has occurred, free any new node. */ + if( !astOK ) newnode = FreeTree( newnode, status ); + +/* If we have a replacement node, free the supplied tree and return a + pointer to the new tree. */ + if( newnode ) { + FreeTree( *node, status ); + *node = newnode; + } + +/* If the above produced some change, try re-simplifying the tree. */ + if( result ) SimplifyTree( node, std, status ); + +/* Return the result. */ + return result; + +} + +static int SplitUnit( const char *str, int ls, const char *u, int cs, + Multiplier **mult, int *l, int *status ) { +/* +* Name: +* SplitUnit + +* Purpose: +* Split a given string into unit name and multiplier. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int SplitUnit( const char *str, int ls, const char *u, int cs, +* Multiplier **mult, int *l, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* Returns non-zer0 if the supplied string ends with the supplied unit +* name or label, and any leading string is a known multiplier. + +* Parameters: +* str +* The string to test, typically containing a multiplier and a unit +* symbol or label. +* ls +* Number of characters to use from "str" (not including trailing null) +* u +* Pointer to the unit label or symbol string to be searched for. +* cs +* If non-zero, the test for "u" is case insensitive. +* mult +* Address of a location at which to return the multiplier at the +* start of the supplied string. NULL is returned if the supplied +* string does not match the supplied unit, or if the string +* includes no multiplier. +* l +* Address of an int in which to return the length of "u". +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Non-zero if "str" ends with "u" and starts with a null string or a +* known multiplier string. + +*/ + +/* Local Variables: */ + int ret; + int lm; + int lu; + +/* Initialise */ + ret = 0; + *mult = NULL; + *l = 0; + +/* Check inherited status. */ + if( !astOK ) return ret; + +/* Find the number of characters in the supplied unit label or symbol. The + difference between lu and ls must be the length of the multiplier. */ + lu = strlen( u ); + lm = ls - lu; + +/* Make sure "str" is not shorter than "u" */ + if( lm >= 0 ) { + +/* Compare the end of "str" against "u" */ + if( cs ? !strncmp( str + lm, u, lu ) : + !Ustrncmp( str + lm, u, lu, status ) ) { + ret = 1; + +/* If "str" ends with "u", see if it starts with a known multiplier */ + if( lm > 0 ) { + ret = 0; + *mult = GetMultipliers( status ); + while( *mult ) { + if( (*mult)->symlen == lm && !strncmp( str, (*mult)->sym, lm ) ) { + ret = 1; + break; + } + *mult = (*mult)->next; + } + +/* If not, try again using case-insensitive matching. */ + if( !ret ) { + *mult = GetMultipliers( status ); + while( *mult ) { + if( (*mult)->symlen == lm && !Ustrncmp( str, (*mult)->sym, lm, status ) ) { + ret = 1; + break; + } + *mult = (*mult)->next; + } + } + +/* If not, try again using case-insensitive matching against the + multiplier label. */ + if( !ret ) { + *mult = GetMultipliers( status ); + while( *mult ) { + if( (*mult)->lablen == lm && !Ustrncmp( str, (*mult)->label, lm, status ) ) { + ret = 1; + break; + } + *mult = (*mult)->next; + } + } + + } + } + } + + *l = lu; + return ret; +} + +double astUnitAnalyser_( const char *in, double powers[9], int *status ){ +/* +*+ +* Name: +* astUnitAnalyser + +* Purpose: +* Perform a dimensional analysis of a unti string. + +* Type: +* Protected function. + +* Synopsis: +* #include "unit.h" +* double astUnitAnalyser_( const char *in, double powers[9] ) + +* Class Membership: +* Unit member function. + +* Description: +* This function parses the supplied units string if possible, and +* returns a set of pwoers and a scaling factor which represent the +* units string. + +* Parameters: +* in +* A string representation of the units, for instance "km/h". +* powers +* An array in which are returned the powers for each of the following +* basic units (in the order shown): kilogramme, metre, second, radian, +* Kelvin, count, adu, photon, magnitude, pixel. If the supplied unit +* does not depend on a given basic unit a value of 0.0 will be returned +* in the array. The returns values represent a system of units which is +* a scaled form of the supplied units, expressed in the basic units of +* m, kg, s, rad, K, count, adu, photon, mag and pixel. For instance, a +* returned array of [1,0,-2,0,0,0,0,0,0] would represent "m/s**2". + +* Returned Value: +* A scaling factor for the supplied units. The is the value, in the +* units represented by the returned powers, which corresponds to a +* value of 1.0 in the supplied units. + +* Notes: +* - An error will be reported if the units string cannot be parsed +* or refers to units or functions which cannot be analysed in this way. +* - AST__BAD is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + UnitNode *in_tree; + double result; + +/* Initialise */ + result = AST__BAD; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Parse the input units string, producing a tree of UnitNodes which + represents the input units. A pointer to the UnitNode at the head of + the tree is returned if succesfull. Report a context message if this + fails. */ + in_tree = CreateTree( in, 1, 1, status ); + if( in_tree ) { + +/* Analyse the tree */ + if( !DimAnal( in_tree, powers, &result, status ) && astOK ) { + result = AST__BAD; + astError( AST__BADUN, "astUnitAnalyser: Error analysing input " + "units string '%s' (it may contain unsupported " + "functions or dimensionless units).", status, in ); + } + +/* Free the tree. */ + in_tree = FreeTree( in_tree, status ); + + } else if( astOK ) { + astError( AST__BADUN, "astUnitAnalyser: Error parsing input " + "units string '%s'.", status, in ); + } + +/* Return the result */ + return result; +} + +const char *astUnitLabel_( const char *sym, int *status ){ +/* +*+ +* Name: +* astUnitLabel + +* Purpose: +* Return a string label for a given unit symbol. + +* Type: +* Protected function. + +* Synopsis: +* #include "unit.h" +* const char *astUnitLabel( const char *sym ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a pointer to a constant string containing a +* descriptive label for the unit specified by the given unit symbol. + +* Parameters: +* sym +* A string holing a known unit symbol. + +* Returned Value: +* A pointer to constant string holding a descriptive label for the +* supplied unit. A NULL pointer is returned (without error) if the +* supplied unit is unknown. + +* Notes: +* - A NULL pointer is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + const char *result; + KnownUnit *unit; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Ensure descriptions of the known units are available. */ + unit = GetKnownUnits( 1, status ); + +/* Loop through the chain of known units looking for a unit with a symbol + equal to the supplied string. If found, store a pointer to its label + and break out of the loop. */ + while( unit ) { + if( !strcmp( sym, unit->sym ) ) { + result = unit->label; + break; + } + + unit = unit->next; + } + +/* Return the answer. */ + return result; +} + +AstMapping *astUnitMapper_( const char *in, const char *out, + const char *in_lab, char **out_lab, int *status ){ +/* +*+ +* Name: +* astUnitMapper + +* Purpose: +* Create a Mapping between two system of units. + +* Type: +* Protected function. + +* Synopsis: +* #include "unit.h" +* AstMapping *astUnitMapper( const char *in, const char *out, +* const char *in_lab, char **out_lab ) + +* Class Membership: +* Unit member function. + +* Description: +* This function creates a Mapping between two specified system of +* units. It also modifes a supplied label (which is typically +* the axis label associated with the input units) so that it includes +* any functional change implied by the supplied "in" and "out" units. + +* Parameters: +* in +* A string representation of the input units, for instance "km/h". +* See "Unit Representations:" below. +* out +* A string representation of the output units, for instance "m/s". +* See "Unit Representations:" below. +* in_lab +* A label describing the quantity associated with the input units. +* If the "in" string is the Units attribute of an Axis, then +* "in_lab" should be the Label of the same Axis. May be supplied +* NULL in which case "out_lab" is ignored. +* out_lab +* The address at which to return a pointer to a label describing the +* quantity associated with the output units. For instance, if the +* input and output units are "Hz" and "sqrt(Hz)", and the input +* label is "Frequency", then the returned output label will be +* "sqrt( Frequency )". The returned label is stored in dynamically +* allocated memory which should be freed (using astFree) when no longer +* needed. + +* Returned Value: +* A pointer to a Mapping which can be used to transform values in the +* "in" system of units into the "out" system of units. The Mapping +* will have 1 input and 1 output. + +* Unit Representations: +* The string supplied for "in" and "out" should represent a system of +* units following the recommendations of the FITS WCS paper I +* "Representation of World Coordinates in FITS" (Greisen & Calabretta). +* Various commonly used variants are also allowed. +* +* To summarise, a string describing a system of units should be an +* algebraic expression which combines one or more named units. The +* following functions and operators may be used within these algebraic +* expressions: +* +* - "*": multiplication. A period "." or space " " may also be used +* to represent multiplication (a period is only interpreted as a +* multiplication operator if it is not positioned between two digits, +* and a space is only interpreted as a multiplication operator if it +* occurs between two operands). +* - "/": division. +* - "**": exponentiation. The exponent (i.e. the operand following the +* exponentiation operator) must be a constant. The symbol "^" is also +* interpreted as an exponentiation operator. Exponentiation is also +* implied by an integer following a unit name without any separator +* (e.g. "cm2" is "cm^2"). +* - log(): Common logarithm. +* - ln(): Natural logarithm. +* - sqrt(): Square root. +* - exp(): Exponential. +* +* Function names are case insensitive. White space may be included +* within an expression (note that white space between two operands +* will be interpreted as a muiltiplication operator as described +* above). Parentheses may be used to indicate the order in which +* expressions are to be evaluated (normal mathematical precedence is +* used otherwise). The following symbols may be used to represent +* constants: +* +* - "pi" +* - "e" +* +* These symbols are also case in-sensitive. +* +* The above operators and functions are used to combine together one +* or more "unit symbols". The following base unit symbols are recognised: +* +* - "m": metre. +* - "g": gram. +* - "s": second. +* - "rad": radian. +* - "sr": steradian. +* - "K": Kelvin. +* - "mol": mole. +* - "cd": candela. +* +* The following symbols for units derived fro the above basic units are +* recognised: +* +* - "sec": second (1 s) +* - "Hz": Hertz (1/s). +* - "N": Newton (kg m/s**2). +* - "J": Joule (N m). +* - "W": Watt (J/s). +* - "C": Coulomb (A s). +* - "V": Volt (J/C). +* - "Pa": Pascal (N/m**2). +* - "Ohm": Ohm (V/A). +* - "S": Siemens (A/V). +* - "F": Farad (C/V). +* - "Wb": Weber (V s). +* - "T": Tesla (Wb/m**2). +* - "H": Henry (Wb/A). +* - "lm": lumen (cd sr). +* - "lx": lux (lm/m**2). +* - "deg": degree (pi/180 rad). +* - "arcmin": arc-minute (1/60 deg). +* - "arcsec": arc-second (1/3600 deg). +* - "mas": milli-arcsecond (1/3600000 deg). +* - "min": minute (60 s). +* - "h": hour (3600 s). +* - "d": day (86400 s). +* - "yr": year (31557600 s). +* - "a": year (31557600 s). +* - "eV": electron-Volt (1.60217733E-19 J). +* - "erg": erg (1.0E-7 J). +* - "Ry": Rydberg (13.605692 eV). +* - "solMass": solar mass (1.9891E30 kg). +* - "u": unified atomic mass unit (1.6605387E-27 kg). +* - "solLum": solar luminosity (3.8268E26 W). +* - "Angstrom": Angstrom (1.0E-10 m). +* - "Ang": Angstrom +* - "A": Ampere +* - "micron": micron (1.0E-6 m). +* - "solRad": solar radius (6.9599E8 m). +* - "AU": astronomical unit (1.49598E11 m). +* - "lyr": light year (9.460730E15 m). +* - "pc": parsec (3.0867E16 m). +* - "count": count. +* - "ct": count. +* - "adu": analogue-to-digital converter unit. +* - "photon": photon. +* - "ph": photon. +* - "Jy": Jansky (1.0E-26 W /m**2 /Hz). +* - "Jan": Jansky +* - "mag": magnitude. +* - "G": Gauss (1.0E-4 T). +* - "pixel": pixel. +* - "pix": pixel. +* - "barn": barn (1.0E-28 m**2). +* - "D": Debye (1.0E-29/3 C.m). +* +* In addition, any other unknown unit symbol may be used (but of course +* no mapping will be possible between unknown units). +* +* Unit symbols may be preceded with a numerical constant (for +* instance "1000 m") or a standard multiplier symbol (for instance "km") +* to represent some multiple of the unit. The following standard +* multipliers are recognised: +* +* - "d": deci (1.0E-1) +* - "c": centi (1.0E-2) +* - "m": milli (1.0E-3) +* - "u": micro (1.0E-6) +* - "n": nano (1.0E-9) +* - "p": pico (1.0E-12) +* - "f": femto (1.0E-15) +* - "a": atto (1.0E-18) +* - "z": zepto (1.0E-21) +* - "y": yocto (1.0E-24) +* - "da": deca (1.0E1) +* - "h": hecto (1.0E2) +* - "k": kilo (1.0E3) +* - "M": mega (1.0E6) +* - "G": giga (1.0E9) +* - "T": tera (1.0E12) +* - "P": peta (1.0E15) +* - "E": exa (1.0E18) +* - "Z": zetta (1.0E21) +* - "Y": yotta (1.0E24) + +* Notes: +* - NULL values are returned without error if the supplied units are +* incompatible (for instance, if the input and output units are "kg" +* and "m" ). +* - NULL values are returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + AstMapping *result; + UnitNode **units; + UnitNode *in_tree; + UnitNode *intemp; + UnitNode *inv; + UnitNode *labtree; + UnitNode *newtest; + UnitNode *out_tree; + UnitNode *outtemp; + UnitNode *src; + UnitNode *testtree; + UnitNode *tmp; + UnitNode *totaltree; + UnitNode *totlabtree; + const char *c; + const char *exp; + int i; + int nc; + int nunits; + int ipass; + +/* Initialise */ + result = NULL; + if( in_lab ) *out_lab = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* A quick check for a common simple case: if the two strings are + identical, return a UnitMap.*/ + if( !strcmp( in, out ) ) { + if( in_lab ) *out_lab = astStore( NULL, in_lab, strlen( in_lab ) + 1 ); + return (AstMapping *) astUnitMap( 1, "", status ); + } + +/* More initialisation. */ + in_tree = NULL; + out_tree = NULL; + units = NULL; + +/* Parse the input units string, producing a tree of UnitNodes which + represents the input units. A pointer to the UnitNode at the head of + the tree is returned if succesfull. Report a context message if this + fails. The returned tree contains branch nodes which correspond to + operators or functions, and leaf nodes which represent constant values + or named basic units (m, s, g, K, etc). Each branch node has one or more + "arguments" (i.e. child nodes) which are operated on or combined by + the branch node in some way to produce the nodes "value". This value + is then used as an argument for the node's parent node (if any). If + the string supplied by the user refers to any known derived units (e.g. "N", + Newton) then each such unit is represented in the returned tree by a + complete sub-tree in which the head node corresponds to the derived + unit (e.g. "N") and the leaf nodes correspond to the basic units needed + to define the derived unit ( for instance, "m", "s" and "g" - metres, + seconds and grammes), or numerical constants. Thus every leaf node in the + returned tree will be a basic unit (i.e. a unit which is not defined in + terms of other units), or a numerical constant. */ + in_tree = CreateTree( in, 1, 1, status ); + if( !astOK ) astError( AST__BADUN, "astUnitMapper: Error parsing input " + "units string '%s'.", status, in ); + +/* Do the same for the output units. */ + if( astOK ) { + out_tree = CreateTree( out, 1, 1, status ); + if( !astOK ) astError( AST__BADUN, "astUnitMapper: Error parsing output " + "units string '%s'.", status, out ); + } + +/* If a blank string is supplied for both input and output units, then + assume a UnitMap is the appropriate Mapping. */ + if( !in_tree && !out_tree && astOK ) { + result = (AstMapping *) astUnitMap( 1, "", status ); + if( in_lab ) *out_lab = astStore( NULL, in_lab, strlen( in_lab ) + 1 ); + +/* Otherwise, if we have both input and output trees... */ + } else if( in_tree && out_tree && astOK ) { + +/* Locate all the basic units used within either of these two trees. An + array is formed in which each element is a pointer to a UnitNode + contained within one of the trees created above. Each basic unit + referred to in either tree will have a single entry in this array + (even if the unit is referred to more than once). */ + units = NULL; + nunits = 0; + LocateUnits( in_tree, &units, &nunits, status ); + LocateUnits( out_tree, &units, &nunits, status ); + +/* Due to the simple nature of the simplification process in SimplifyTree, + the following alogorithm sometimes fails to find a Mapping form input + to output units, but can find a Mapping from output to input units. + In this latter case, we can get the required Mapping from input to + output simply by inverting the Mapign from output to input. So try + first with the units in the original order. If this fails to find a + Mapping, try again with the units swapped, and note that the final + Mapping should be inverted before being used. */ + for( ipass = 0; ipass < 2; ipass++ ){ + if( ipass == 1 ) { + tmp = in_tree; + in_tree = out_tree; + out_tree = tmp; + } + +/* We are going to create a new tree of UnitNodes in which the head node + corresponds to the requested output units, and which has a single + non-constant leaf node corresponding to the input units. Initialise a + pointer to this new tree to indicate that it has not yet been created. */ + testtree = NULL; + +/* Loop round each basic unit used in the definition of either the input + or the output units (i.e. the elements of the array created above by + "LocateUnits"). The unit selected by this loop is referred to as the + "current" unit. On each pass through this loop, we create a tree which + is a candidate for the final required tree (the "test tree" pointed to + by the testtree pointer initialised above). In order for a mapping to + be possible between input and output units, the test tree created on + each pass through this loop must be equivalent to the test tree for the + previous pass (in other words, all the test trees must be equivalent). + We break out of the loop (and return a NULL Mapping) as soon as we find + a test tree which differs from the previous test tree. */ + for( i = 0; i < nunits; i++ ) { + +/* Create copies of the trees describing the input and output units, in which + all units other than the current unit are set to a constant value of 1. + This is done by replacing OP_LDVAR nodes (i.e. nodes which "load" the + value of a named basic unit) by OP_LDCON nodes (i.e. nodes which load + a specified constant value) in the tree copy. */ + intemp = FixUnits( in_tree, units[ i ], status ); + outtemp = FixUnits( out_tree, units[ i ], status ); + +/* Simplify these trees. An important side-effect of this simplification + is that trees are "standardised" which allows them to be compared for + equivalence. A single mathematical expression can often be represented + in many different ways (for instance "A/B" is equivalent to "(B**(-1))*A"). + Standardisation is a process of forcing all equivalent representations + into a single "standard" form. Without standardisation, trees representing + the above two expressions would not be considered to be equivalent + since thy would contain different nodes and have different structures. + As a consequence of this standardisation, the "simplification" performed + by SimplifyTree can sometimes actually make the tree more complicated + (in terms of the number of nodes in the tree). */ + SimplifyTree( &intemp, 1, status ); + SimplifyTree( &outtemp, 1, status ); + +/* If either of the simplified trees does not depend on the current unit, + then the node at the head of the simplified tree will have a constant + value (because all the units other than the current unit have been fixed + to a constant value of 1.0 above by FixUnits, leaving only the current + unit to vary in value). If both simplified trees are constants, then + neither tree depends on the current basic unit (i.e. references to the + current basic unit cancel out within each string expression - for + instance if converting from "m.s.Hz" to "km" and the current unit + is "s", then the "s.Hz" term will cause the "s" units to cancel out). In + this case ignore this basic unit and pass on to the next. */ + if( outtemp->con != AST__BAD && intemp->con != AST__BAD ) { + +/* If just one simplified tree is constant, then the two units cannot + match since one depends on the current basic unit and the other does + not. Free any test tree from previous passes and break out of the loop. */ + } else if( outtemp->con != AST__BAD || intemp->con != AST__BAD ) { + intemp = FreeTree( intemp, status ); + outtemp = FreeTree( outtemp, status ); + testtree = FreeTree( testtree, status ); + break; + +/* If neither simplified tree is constant, both depend on the current + basic unit and so we can continue to see if their dependencies are + equivalent. */ + } else { + +/* We are going to create a new tree which is the inverse of the above + simplified "intemp" tree. That is, the new tree will have a head node + corresponding to the current unit, and a single non-constant leaf node + corresponding to the input units. Create an OP_LDVAR node which can be + used as the leaf node for this inverted tree. If the input tree is + inverted successfully, this root node becomes part of the inverted tree, + and so does not need to be freed explicitly (it will be freed when the + inverted tree is freed). */ + src = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) src->name = astStore( NULL, "input_units", 12 ); + +/* Now produce the inverted input tree. If the tree cannot be inverted, a + null pointer is returned. Check for this. Otherwise a pointer to the + UnitNode at the head of the inverted tree is returned. */ + inv = InvertTree( intemp, src, status ); + if( inv ) { + +/* Concatenate this tree (which goes from "input units" to "current unit") + with the simplified output tree (which goes from "current unit" to + "output units"), to get a new tree which goes from input units to output + units. */ + totaltree = ConcatTree( inv, outtemp, status ); + +/* Simplify this tree. */ + SimplifyTree( &totaltree, 1, status ); + +/* Compare this simplified tree with the tree produced for the previous + unit (if any). If they differ, we cannot map between the supplied + units so annul the test tree and break out of the loop. If this is the + first unit to be tested, use the total tree as the test tree for the + next unit. */ + if( testtree ) { + if( CmpTree( totaltree, testtree, 0, status ) ) testtree = FreeTree( testtree, status ); + totaltree = FreeTree( totaltree, status ); + if( !testtree ) break; + } else { + testtree = totaltree; + } + } + +/* If the input tree was inverted, free the inverted tree. */ + if( inv ) { + inv = FreeTree( inv, status ); + +/* If the input tree could not be inverted, we cannot convert between input + and output units. Free the node which was created to be the root of the + inverted tree (and which has consequently not been incorporated into the + inverted tree), free any testtree and break out of the loop. */ + } else { + src = FreeTree( src, status ); + testtree = FreeTree( testtree, status ); + break; + } + } + +/* Free the other trees. */ + intemp = FreeTree( intemp, status ); + outtemp = FreeTree( outtemp, status ); + + } + +/* If all the basic units used by either of the supplied system of units + produced the same test tree, leave the "swap in and out units" loop. */ + if( testtree ) break; + + } + +/* If the input and output units have been swapped, swap them back to + their original order, and invert the test tree (if there is one). */ + if( ipass > 0 ) { + tmp = in_tree; + in_tree = out_tree; + out_tree = tmp; + if( testtree ) { + src = NewNode( NULL, OP_LDVAR, status ); + if( astOK ) src->name = astStore( NULL, "input_units", 12 ); + newtest = InvertTree( testtree, src, status ); + FreeTree( testtree, status ); + testtree = newtest; + if( !newtest ) src = FreeTree( src, status ); + } + } + +/* If all the basic units used by either of the supplied system of units + produced the same test tree, create a Mapping which is equivalent to the + test tree and return it. */ + if( testtree ) { + result = MakeMapping( testtree, status ); + +/* We now go on to produce the output axis label from the supplied input + axis label. Get a tree of UnitNodes which describes the supplied label + associated with the input axis. The tree will have single OP_LDVAR node + corresponding to the basic label (i.e. the label without any single + argument functions or exponentiation operators applied). */ + if( in_lab && astOK ) { + +/* Get a pointer to the first non-blank character, and store the number of + characters to examine (this excludes any trailing white space). */ + exp = in_lab; + while( isspace( *exp ) ) exp++; + c = exp + strlen( exp ) - 1; + while( c >= exp && isspace( *c ) ) c--; + nc = c - exp + 1; + +/* Create the tree. */ + labtree = MakeLabelTree( exp, nc, status ); + if( astOK ) { + +/* Concatenate this tree (which goes from "basic label" to "input label") + with the test tree found above (which goes from "input units" to "output + units"), to get a tree which goes from basic label to output label. */ + totlabtree = ConcatTree( labtree, testtree, status ); + +/* Simplify this tree. */ + SimplifyTree( &totlabtree, 1, status ); + +/* Create the output label from this tree. */ + *out_lab = (char *) MakeExp( totlabtree, 0, 1, status ); + +/* Free the trees. */ + totlabtree = FreeTree( totlabtree, status ); + labtree = FreeTree( labtree, status ); + +/* Report a context error if the input label could not be parsed. */ + } else { + astError( AST__BADUN, "astUnitMapper: Error parsing axis " + "label '%s'.", status, in_lab ); + } + } + +/* Free the units tree. */ + testtree = FreeTree( testtree, status ); + + } + } + +/* Free resources. */ + in_tree = FreeTree( in_tree, status ); + out_tree = FreeTree( out_tree, status ); + units = astFree( units ); + +/* If an error has occurred, annul the returned Mapping. */ + if( !astOK ) { + result = astAnnul( result ); + if( in_lab ) *out_lab = astFree( *out_lab ); + } + +/* Return the result. */ + return result; + +} + +const char *astUnitNormaliser_( const char *in, int *status ){ +/* +*+ +* Name: +* astUnitNormalizer + +* Purpose: +* Normalise a unit string into FITS-WCS format. + +* Type: +* Protected function. + +* Synopsis: +* #include "unit.h" +* const char *astUnitNormaliser( const char *in ) + +* Class Membership: +* Unit member function. + +* Description: +* This function returns a standard FITS-WCS form of the supplied unit +* string. + +* Parameters: +* in +* A string representation of the units, for instance "km/h". + +* Returned Value: +* A pointer to a dynamically allocated string holding the normalized +* unit string. It should be freed using astFree when no longer needed. + +* Notes: +* - An error will be reported if the units string cannot be parsed. +* - NULL is returned if this function is invoked with the +* global error status set or if it should fail for any reason. +*- +*/ + +/* Local Variables: */ + UnitNode *in_tree; + double dval; + const char *result; + +/* Initialise */ + result = NULL; + +/* Check the global error status. */ + if ( !astOK ) return result; + +/* Parse the input units string, producing a tree of UnitNodes which + represents the input units. A pointer to the UnitNode at the head of + the tree is returned if succesfull. Report a context message if this + fails. */ + in_tree = CreateTree( in, 0, 1, status ); + if( in_tree ) { + +/* Simplify the units expression, only doing genuine simplifications. */ + SimplifyTree( &in_tree, 1, status ); + +/* Invert literal constant unit multipliers. This is because a constant of + say 1000 for a unit of "m" means "multiply the value in metres by 1000", + but a unit string of "1000 m" means "value in units of 1000 m" (i.e. + *divide* the value in metres by 1000). */ + InvertConstants( &in_tree, status ); + +/* Convert the tree into string form. */ + result = MakeExp( in_tree, 2, 1, status ); + +/* If the result is a constant value, return a blank string. */ + if( 1 == astSscanf( result, "%lg", &dval ) ) { + *((char *) result) = 0; + } + +/* Free the tree. */ + in_tree = FreeTree( in_tree, status ); + + } else { + astError( AST__BADUN, "astUnitNormaliser: Error parsing input " + "units string '%s'.", status, in ); + } + +/* Return the result */ + return result; +} + +static int Ustrcmp( const char *a, const char *b, int *status ){ +/* +* Name: +* Ustrcmp + +* Purpose: +* A case blind version of strcmp. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int Ustrcmp( const char *a, const char *b, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* Returns 0 if there are no differences between the two strings, and 1 +* otherwise. Comparisons are case blind. + +* Parameters: +* a +* Pointer to first string. +* b +* Pointer to second string. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the strings match, otherwise one. + +* Notes: +* - This function does not consider the sign of the difference between +* the two strings, whereas "strcmp" does. +* - This function attempts to execute even if an error has occurred. + +*/ + +/* Local Variables: */ + const char *aa; /* Pointer to next "a" character */ + const char *bb; /* Pointer to next "b" character */ + int ret; /* Returned value */ + +/* Initialise the returned value to indicate that the strings match. */ + ret = 0; + +/* Initialise pointers to the start of each string. */ + aa = a; + bb = b; + +/* Loop round each character. */ + while( 1 ){ + +/* We leave the loop if either of the strings has been exhausted. */ + if( !(*aa ) || !(*bb) ){ + +/* If one of the strings has not been exhausted, indicate that the + strings are different. */ + if( *aa || *bb ) ret = 1; + +/* Break out of the loop. */ + break; + +/* If neither string has been exhausted, convert the next characters to + upper case and compare them, incrementing the pointers to the next + characters at the same time. If they are different, break out of the + loop. */ + } else { + + if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ + ret = 1; + break; + } + + } + + } + +/* Return the result. */ + return ret; + +} + +static int Ustrncmp( const char *a, const char *b, size_t n, int *status ){ +/* +* Name: +* Ustrncmp + +* Purpose: +* A case blind version of strncmp. + +* Type: +* Private function. + +* Synopsis: +* #include "unit.h" +* int Ustrncmp( const char *a, const char *b, size_t n, int *status ) + +* Class Membership: +* Unit member function. + +* Description: +* Returns 0 if there are no differences between the first "n" +* characters of the two strings, and 1 otherwise. Comparisons are +* case blind. + +* Parameters: +* a +* Pointer to first string. +* b +* Pointer to second string. +* n +* The maximum number of characters to compare. +* status +* Pointer to the inherited status variable. + +* Returned Value: +* Zero if the strings match, otherwise one. + +* Notes: +* - This function does not consider the sign of the difference between +* the two strings, whereas "strncmp" does. +* - This function attempts to execute even if an error has occurred. + +*/ + +/* Local Variables: */ + const char *aa; /* Pointer to next "a" character */ + const char *bb; /* Pointer to next "b" character */ + int i; /* Character index */ + int ret; /* Returned value */ + + +/* Initialise the returned value to indicate that the strings match. */ + ret = 0; + +/* Check pointer have been supplied. */ + if( !a || !b ) return ret; + +/* Initialise pointers to the start of each string. */ + aa = a; + bb = b; + +/* Compare up to "n" characters. */ + for( i = 0; i < (int) n; i++ ){ + +/* We leave the loop if either of the strings has been exhausted. */ + if( !(*aa ) || !(*bb) ){ + +/* If one of the strings has not been exhausted, indicate that the + strings are different. */ + if( *aa || *bb ) ret = 1; + +/* Break out of the loop. */ + break; + +/* If neither string has been exhausted, convert the next characters to + upper case and compare them, incrementing the pointers to the next + characters at the same time. If they are different, break out of the + loop. */ + } else { + + if( toupper( (int) *(aa++) ) != toupper( (int) *(bb++) ) ){ + ret = 1; + break; + } + + } + + } + +/* Return the result. */ + return ret; + +} + + + + + + + + + + + + +/* The rest of this file contains functions which are of use for debugging + this module. They are usually commented out. + +static const char *DisplayTree( UnitNode *node, int ind ) { + int i; + char buf[200]; + const char *result; + char *a; + const char *arg[ 2 ]; + int rl; + int slen; + const opsym[ 100 ]; + + result = ""; + + for( i = 0; i < ind; i++ ) buf[ i ] = ' '; + buf[ ind ] = 0; + + if( !node ) { + printf( "%s <null>\n", buf ); + } else { + + printf( "%s Code: '%s' (%d)\n", buf, OpName( node->opcode ), node->opcode ); + printf( "%s Narg: %d\n", buf, node->narg ); + printf( "%s Constant: %g\n", buf, node->con ); + printf( "%s Name: %s\n", buf, node->name?node->name:"" ); + printf( "%s Unit: %s\n", buf, node->unit?node->unit->sym:"" ); + printf( "%s Mult: %s\n", buf, node->mult?node->mult->sym:"" ); + + OpSym( node, opsym ); + slen = strlen( opsym ); + rl = slen; + + if( node->narg == 0 ) { + result = astMalloc( rl + 1 ); + if( astOK ) strcpy( (char *) result, opsym ); + + } else if( node->narg == 1 ) { + rl += 2; + printf( "%s Arg 0:\n", buf ); + arg[ 0 ] = DisplayTree( (node->arg)[ 0 ], ind + 2 ); + rl += strlen( arg[ 0 ] ); + + result = astMalloc( rl + 1 ); + if( astOK ) { + a = (char *) result; + strcpy( a, opsym ); + a += slen; + *(a++) = '('; + strcpy( a, arg[0] ); + a += strlen( arg[ 0 ] ); + *(a++) = ')'; + } + + } else { + rl += 4; + for( i = 0; i < node->narg; i++ ) { + printf( "%s Arg %d:\n", buf, i ); + arg[ i ] = DisplayTree( (node->arg)[ i ], ind + 2 ); + rl += strlen( arg[ i ] ); + } + + result = astMalloc( rl + 1 ); + if( astOK ) { + a = (char *) result; + *(a++) = '('; + strcpy( a, arg[0] ); + a += strlen( arg[ 0 ] ); + *(a++) = ')'; + strcpy( a, opsym ); + a += slen; + *(a++) = '('; + strcpy( a, arg[1] ); + a += strlen( arg[ 1 ] ); + *(a++) = ')'; + } + } + } + + if( !astOK ) { + astFree( (void *) result ); + result = ""; + } + + return result; +} + +static const char *OpName( Oper op ) { + const char *name; + + if( op == OP_LDCON ) { + name = "LDCON"; + } else if( op == OP_LDVAR ) { + name = "LDVAR"; + } else if( op == OP_LOG ) { + name = "LOG"; + } else if( op == OP_LN ) { + name = "LN"; + } else if( op == OP_EXP ) { + name = "EXP"; + } else if( op == OP_SQRT ) { + name = "SQRT"; + } else if( op == OP_POW ) { + name = "POW"; + } else if( op == OP_DIV ) { + name = "DIV"; + } else if( op == OP_MULT ) { + name = "MULT"; + } else if( op == OP_LDPI ) { + name = "LDPI"; + } else if( op == OP_LDE ) { + name = "LDE"; + } else if( op == OP_NULL ) { + name = "NULL"; + } else { + name = "<unknown op code>"; + } + + return name; +} + +static void OpSym( UnitNode *node, char *buff ) { + const char *sym = NULL; + + if( node->con != AST__BAD ) { + sprintf( buff, "%g", node->con ); + + } else if( node->opcode == OP_LDVAR ) { + sym = node->name; + + } else if( node->opcode == OP_LOG ) { + sym = "log"; + + } else if( node->opcode == OP_LN ) { + sym = "ln"; + + } else if( node->opcode == OP_EXP ) { + sym = "exp"; + + } else if( node->opcode == OP_SQRT ) { + sym = "sqrt"; + + } else if( node->opcode == OP_POW ) { + sym = "**"; + + } else if( node->opcode == OP_DIV ) { + sym = "/"; + + } else if( node->opcode == OP_MULT ) { + sym = "*"; + + } else if( node->opcode == OP_NULL ) { + sym = "NULL"; + + } else { + sym = "<unknown op code>"; + } + + if( sym ) strcpy( buff, sym ); +} + +static const char *TreeExp( UnitNode *node ) { + char buff[ 100 ]; + char buff2[ 100 ]; + + if( node->narg == 0 ) { + OpSym( node, buff ); + + } else if( node->narg == 1 ) { + OpSym( node, buff2 ); + sprintf( buff, "%s(%s)", buff2, TreeExp( node->arg[ 0 ] ) ); + + } else if( node->narg == 2 ) { + OpSym( node, buff2 ); + sprintf( buff, "(%s)%s(%s)", TreeExp( node->arg[ 0 ] ), buff2, + TreeExp( node->arg[ 1 ] ) ); + } + + return astStore( NULL, buff, strlen( buff ) + 1 ); +} + +*/ + + + + |