diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-11-02 19:11:06 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-11-02 19:11:06 (GMT) |
commit | 33c55bd916dff8c4932b01c7db58f0103ac31c31 (patch) | |
tree | a4cdca3287dd2df5247ce8079c424ffa438b4c2e /ast/unit.c | |
parent | 4121637f3d41d6dc23e6543a445b5a3aed9e6ddc (diff) | |
download | blt-33c55bd916dff8c4932b01c7db58f0103ac31c31.zip blt-33c55bd916dff8c4932b01c7db58f0103ac31c31.tar.gz blt-33c55bd916dff8c4932b01c7db58f0103ac31c31.tar.bz2 |
update ast
Diffstat (limited to 'ast/unit.c')
-rw-r--r-- | ast/unit.c | 6218 |
1 files changed, 0 insertions, 6218 deletions
diff --git a/ast/unit.c b/ast/unit.c deleted file mode 100644 index bec4b0a..0000000 --- a/ast/unit.c +++ /dev/null @@ -1,6218 +0,0 @@ -/* -* 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 ); -} - -*/ - - - - |