source: trunk/src/core/RpUnits.cc @ 613

Last change on this file since 613 was 613, checked in by nkissebe, 17 years ago

RpLibrary?.cc, RpUnits.cc: include <algorithm> for MS compiler
RpLibrary?.cc: specify std:: namespace when calling transform
RpLibrary?.cc: MSC++ doesn't have timezone in struct tm

File size: 88.3 KB
Line 
1/*
2 * ----------------------------------------------------------------------
3 *  RpUnits.cc
4 *
5 *   Data Members and member functions for the RpUnits class
6 *
7 * ======================================================================
8 *  AUTHOR:  Derrick Kearney, Purdue University
9 *  Copyright (c) 2004-2007  Purdue Research Foundation
10 *
11 *  See the file "license.terms" for information on usage and
12 *  redistribution of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 * ======================================================================
14 */
15
16#include "RpUnits.h"
17#include <algorithm>
18
19// dict pointer
20// set the dictionary to be case insensitive for seaches and storage
21RpDict<std::string,RpUnits*,RpUnits::_key_compare>* RpUnits::dict =
22    new RpDict<std::string,RpUnits*,RpUnits::_key_compare>(
23        RPUNITS_CASE_INSENSITIVE);
24
25// install predefined units
26static RpUnitsPreset loader;
27
28/**********************************************************************/
29// METHOD: define()
30/// Define a unit type to be stored as a Rappture Unit.
31/**
32 */
33
34RpUnits *
35RpUnits::define(    const std::string units,
36                    const RpUnits* basis,
37                    const std::string type,
38                    bool metric,
39                    bool caseInsensitive    ) {
40
41    RpUnits* newRpUnit = NULL;
42
43    std::string searchStr = units;
44    std::string sendStr = "";
45    int len = searchStr.length();
46    int idx = len-1;
47    double exponent = 1;
48
49    RpUnitsTypes::RpUnitsTypesHint hint = NULL;
50
51    if (units.empty()) {
52        // raise error, user sent null units!
53        return NULL;
54    }
55
56    // check to see if the user is trying to trick me!
57    if ( (basis) && (units == basis->getUnits()) ) {
58        // dont trick me!
59        return NULL;
60    }
61
62    // check to see if the said unit can already be found in the dictionary
63    hint = RpUnitsTypes::getTypeHint(type);
64    if (RpUnits::find(units,hint)) {
65        return NULL;
66    }
67
68    //default exponent
69    exponent = 1;
70
71    // check to see if there is an exponent at the end
72    // of the search string
73    idx = RpUnits::grabExponent(searchStr, &exponent);
74    searchStr.erase(idx);
75
76    // move idx pointer back to where last character was found
77    idx--;
78
79    if ( searchStr[0] == '/') {
80        // need to negate all of the previous exponents
81        exponent = -1*exponent;
82        sendStr = searchStr.c_str()+1;
83    }
84    else {
85        // sendStr = searchStr.substr(idx+1,);
86        // we have a unit string to parse
87        sendStr = searchStr;
88    }
89
90    newRpUnit = new RpUnits(    sendStr,
91                                exponent,
92                                basis,
93                                type,
94                                metric,
95                                caseInsensitive );
96    if (newRpUnit) {
97        insert(newRpUnit->getUnitsName(),newRpUnit);
98    }
99
100    // return a copy of the new object to user
101    return newRpUnit;
102}
103
104/**********************************************************************/
105// METHOD: incarnate()
106/// Link two RpUnits where the entity is an incarnate of the abstraction
107/**
108 */
109
110int
111RpUnits::incarnate(const RpUnits* abstraction, const RpUnits* entity) {
112
113    int retVal = 1;
114
115    abstraction->connectIncarnation(entity);
116    entity->connectIncarnation(abstraction);
117
118    retVal = 0;
119    return retVal;
120}
121
122/**********************************************************************/
123// METHOD: grabExponent()
124/// Return exponent from a units string containing a unit name and exponent
125/**
126 */
127
128int
129RpUnits::grabExponent(const std::string& inStr, double* exp) {
130
131    int len = inStr.length();
132    int idx = len - 1;
133
134    *exp = 1;
135
136    while (isdigit(inStr[idx])) {
137        idx--;
138    }
139
140    if ( (inStr[idx] == '+') || (inStr[idx] == '-') ) {
141        idx--;
142    }
143
144    idx++;
145
146    if (idx != len) {
147        // process the exponent.
148        *exp = strtod(inStr.c_str()+idx,NULL);
149    }
150
151    return idx;
152}
153
154/**********************************************************************/
155// METHOD: grabUnitString()
156/// Return units name from a units string containing a unit name and exponent
157/**
158 * this function will be the cause of problems related to adding symbols like %
159 */
160
161int
162RpUnits::grabUnitString ( const std::string& inStr ) {
163
164    int idx = inStr.length() - 1;
165
166    while ((idx >= 0) && isalpha(inStr[idx])) {
167        idx--;
168    }
169
170    // move the index forward one position to
171    // represent the start of the unit string
172    idx++;
173
174    return idx;
175}
176
177/**********************************************************************/
178// METHOD: grabUnits()
179/// Search for the provided units exponent pair in the dictionary.
180/**
181 */
182
183int
184RpUnits::grabUnits (    std::string inStr,
185                        int* offset,
186                        const RpUnits** unit,
187                        const RpUnits** prefix   ) {
188
189    int len = inStr.length();
190    std::string preStr = "";
191
192    if ( (unit == NULL) || (prefix == NULL) ) {
193        // incorrect function call, return error
194        return -1;
195    }
196
197    *unit = NULL;
198    *prefix = NULL;
199
200    while ( ! inStr.empty() ) {
201        *unit = RpUnits::find(inStr,&RpUnitsTypes::hintTypeNonPrefix);
202        if (*unit) {
203            *offset = len - inStr.length();
204
205            if ((*unit)->metric) {
206                RpUnits::checkMetricPrefix(preStr,offset,prefix);
207            }
208
209            break;
210        }
211        preStr = preStr + inStr.substr(0,1);
212        inStr.erase(0,1);
213    }
214
215    return 0;
216}
217
218/**********************************************************************/
219// METHOD: checkMetrixPrefix()
220/// Compare a string with available metric prefixes
221/**
222 * The metric prefix only has one or two letters before the main unit.
223 * We take in the string of characters before the found unit and search
224 * for the two characters closest to the found unit. If those two
225 * characters are not found in the dictionary as a prefix, then we erase
226 * the 0th character, and search for the 1th character. The 1th character
227 * is the character closest to the found unit. If it is found as a prefix
228 * in the dictionary, it is returned. If no prefix is found, NULL is
229 * returned.
230 */
231
232int
233RpUnits::checkMetricPrefix  (   std::string inStr,
234                                int* offset,
235                                const RpUnits** prefix   ) {
236
237    int inStrLen = 0;
238    std::string searchStr = "";
239
240    inStrLen = inStr.length();
241
242    if (inStrLen == 0) {
243        // no prefix to search for, exit
244        return 0;
245    }
246
247    if (prefix == NULL) {
248        // incorrect function call, return error
249        return -1;
250    }
251
252
253    if (inStrLen > 2) {
254        searchStr = inStr.substr( inStrLen-2 );
255    }
256    else {
257        searchStr = inStr;
258    }
259
260    *prefix = NULL;
261
262    *prefix = RpUnits::find(searchStr,&RpUnitsTypes::hintTypePrefix);
263    if ( (*prefix) == NULL ) {
264        // the two letter prefix was not found,
265        // try the one letter prefix
266        searchStr.erase(0,1);
267        *prefix = RpUnits::find(searchStr,&RpUnitsTypes::hintTypePrefix);
268    }
269
270    if (*prefix != NULL) {
271        // if a prefix was found, adjust the offset to reflect
272        // the need to erase the prefix as well as the unit name
273        *offset = *offset - searchStr.length();
274    }
275
276    return 0;
277}
278
279/**********************************************************************/
280// METHOD: getType()
281/// Return the type of an RpUnits object.
282/**
283 */
284std::string
285RpUnits::getType() const {
286    return this->type;
287}
288
289/**********************************************************************/
290// METHOD: getCI()
291/// Return the case insensitivity of an RpUnits object.
292/**
293 */
294bool
295RpUnits::getCI() const {
296    return this->ci;
297}
298
299/**********************************************************************/
300// METHOD: getCompatible()
301/// Return a list of units compatible with this RpUnits object.
302/**
303 */
304std::list<std::string>
305RpUnits::getCompatible(double expMultiplier) const {
306
307    std::list<std::string> compatList;
308    std::list<std::string> basisCompatList;
309    std::list<std::string> incarnationCompatList;
310    std::stringstream myName;
311    std::stringstream otherName;
312    std::string otherBasisName  = "";
313    std::string blank           = "";
314    double otherExp             = 1.0;
315    double myExp                = 1.0;
316    double incExp               = 1.0;
317    convEntry* myConversions    = this->convList;
318    incarnationEntry* myIncarnations = this->incarnationList;
319    const RpUnits * basis       = NULL;
320
321    myName.str("");
322    myName << getUnits();
323    myExp = getExponent() * expMultiplier;
324
325    if (this->basis) {
326        basisCompatList = this->basis->getCompatible(expMultiplier);
327        compatList.merge(basisCompatList);
328    }
329    else {
330        // only basis units should be in here
331        //
332        // run through the conversion list
333        // for each entry, look at the name
334        // if the name is not equal to the name of this RpUnits object,
335        // store the fromPtr->getUnitsName() into compatList
336        // else store the toPtr->getUnitsName() into compatList
337        //
338        while (myConversions != NULL) {
339
340            otherName.str("");
341            // otherName << myConversions->conv->toPtr->getUnitsName();
342            otherName << myConversions->conv->toPtr->getUnits();
343            otherExp = myConversions->conv->toPtr->getExponent();
344            basis = myConversions->conv->toPtr->basis;
345
346            if (myName.str() == otherName.str()) {
347                otherName.str("");
348                // otherName << myConversions->conv->fromPtr->getUnitsName();
349                otherName << myConversions->conv->fromPtr->getUnits();
350                otherExp = myConversions->conv->fromPtr->getExponent();
351                basis = myConversions->conv->fromPtr->basis;
352            }
353
354            // check to see if they are the same basis,
355            // no need to list all of the metric conversions.
356            if (basis) {
357                if (basis->getUnitsName() == myName.str()) {
358                    // do not add this unit to the conversion
359                    // because its a derived unit.
360                    myConversions = myConversions->next;
361                    continue;
362                }
363            }
364
365            // adjust the exponent as requested by fxn caller
366            otherExp = otherExp * expMultiplier;
367
368            // adjust the other units name to match exponent
369            if ( (otherExp > 0) && (otherExp != 1) ) {
370                otherName << otherExp;
371            }
372            else if (otherExp < 0) {
373                otherName.str("/"+otherName.str());
374                if (otherExp < -1) {
375                    otherName.seekp(0,std::ios_base::end);
376                    otherName << otherExp*-1;
377                }
378            }
379
380            // add the other unit's name to the list of compatible units
381            compatList.push_back(otherName.str());
382            // compatList.push_back(otherName);
383
384            // advance to the next conversion
385            myConversions = myConversions->next;
386        }
387
388        // now go throught the incarnation list to see if there are other
389        // compatible units listed there.
390        while (myIncarnations != NULL) {
391            incExp = myIncarnations->unit->getExponent();
392            if (incExp == myExp) {
393                incarnationCompatList = myIncarnations->unit->getCompatible();
394                compatList.merge(incarnationCompatList);
395                break;
396            }
397            else if ((-1.0*incExp) == myExp) {
398                incarnationCompatList = myIncarnations->unit->getCompatible(-1);
399                compatList.merge(incarnationCompatList);
400                break;
401            }
402            else if (   (myExp == int(myExp))   &&
403                        (incExp == int(incExp)) &&
404                        ( (int(myExp)%int(incExp)) == 0) &&
405                        ( (myExp/incExp) != 1)  &&
406                        ( (myExp/incExp) != -1) &&
407                        ( myExp != 1 )          &&
408                        ( myExp != -1 )         &&
409                        ( incExp != 1 )         &&
410                        ( incExp != -1 )        ) {
411                incarnationCompatList = myIncarnations->unit->getCompatible(myExp/incExp);
412                compatList.merge(incarnationCompatList);
413                break;
414            }
415            else {
416                // do nothing
417            }
418            myIncarnations = myIncarnations->next;
419        }
420    }
421
422    // adjust the exponent as requested by fxn caller
423    // myExp = myExp * expMultiplier;
424
425    // adjust the other units name to match exponent
426    if ( (expMultiplier > 0) && (expMultiplier != 1) ) {
427        // myName << expMultiplier;
428        myName << myExp;
429    }
430    else if (expMultiplier < 0) {
431        myName.str("/"+myName.str());
432        if (myExp < -1) {
433            myName.seekp(0,std::ios_base::end);
434            myName << myExp*-1;
435        }
436    }
437
438    compatList.push_back(myName.str());
439    compatList.sort();
440    compatList.unique();
441    return compatList;
442
443}
444
445
446
447
448/**********************************************************************/
449// METHOD: define()
450/// Define a unit conversion with one arg double function pointers.
451/**
452 */
453
454RpUnits *
455RpUnits::define(  const RpUnits* from,
456                  const RpUnits* to,
457                  double (*convForwFxnPtr)(double),
458                  double (*convBackFxnPtr)(double)  ) {
459
460    // this is kinda the wrong way to get the job done...
461    // how do we only create 1 conversion object and share it between atleast two RpUnits
462    // objs so that when the RpUnits objs are deleted, we are not trying to delete already
463    // deleted memory.
464    // so for the sake of safety we get the following few lines of code.
465
466    conversion* conv1 = NULL;
467    conversion* conv2 = NULL;
468
469    if (from && to) {
470
471        conv1 = new conversion (from,to,convForwFxnPtr,convBackFxnPtr);
472        conv2 = new conversion (from,to,convForwFxnPtr,convBackFxnPtr);
473
474        from->connectConversion(conv1);
475        to->connectConversion(conv2);
476    }
477
478    return NULL;
479}
480
481/**********************************************************************/
482// METHOD: define()
483/// Define a unit conversion with two arg double function pointers.
484/**
485 */
486
487RpUnits *
488RpUnits::define(  const RpUnits* from,
489                  const RpUnits* to,
490                  double (*convForwFxnPtr)(double,double),
491                  double (*convBackFxnPtr)(double,double)) {
492
493    // this is kinda the wrong way to get the job done...
494    // how do we only create 1 conversion object and share it between
495    // atleast two RpUnits objs so that when the RpUnits objs are
496    // deleted, we are not trying to delete already deleted memory.
497    // so for the sake of safety we get the following few lines of code.
498
499    conversion* conv1 = NULL;
500    conversion* conv2 = NULL;
501
502    if (from && to) {
503        conv1 = new conversion (from,to,convForwFxnPtr,convBackFxnPtr);
504        conv2 = new conversion (from,to,convForwFxnPtr,convBackFxnPtr);
505
506        from->connectConversion(conv1);
507        to->connectConversion(conv2);
508    }
509
510    return NULL;
511}
512
513/**********************************************************************/
514// METHOD: define()
515/// Define a unit conversion with two arg void* function pointers.
516/**
517 */
518
519RpUnits *
520RpUnits::define(  const RpUnits* from,
521                  const RpUnits* to,
522                  void* (*convForwFxnPtr)(void*, void*),
523                  void* convForwData,
524                  void* (*convBackFxnPtr)(void*, void*),
525                  void* convBackData) {
526
527    // this is kinda the wrong way to get the job done...
528    // how do we only create 1 conversion object and share it between at
529    // least two RpUnits objs so that when the RpUnits objs are deleted,
530    // we are not trying to delete already deleted memory.
531    // so for the sake of safety we get the following few lines of code.
532
533    conversion* conv1 = NULL;
534    conversion* conv2 = NULL;
535
536    if (from && to) {
537        conv1 = new conversion ( from, to, convForwFxnPtr,convForwData,
538                                 convBackFxnPtr,convBackData);
539        conv2 = new conversion ( from,to,convForwFxnPtr,convForwData,
540                                 convBackFxnPtr,convBackData);
541
542        from->connectConversion(conv1);
543        to->connectConversion(conv2);
544    }
545
546    return NULL;
547}
548
549
550/**********************************************************************/
551// METHOD: getUnits()
552/// Report the text portion of the units of this object back to caller.
553/**
554 * \sa {getUnitsName}
555 */
556
557std::string
558RpUnits::getUnits() const {
559
560    return units;
561}
562
563/**********************************************************************/
564// METHOD: getUnitsName()
565/// Report the full name of the units of this object back to caller.
566/**
567 * Reports the full text and exponent of the units represented by this
568 * object, back to the caller. Note that if the exponent == 1, no
569 * exponent will be printed.
570 */
571
572std::string
573RpUnits::getUnitsName(int flags) const {
574
575    std::stringstream unitText;
576    double exponent;
577
578    exponent = getExponent();
579
580    if ( (RPUNITS_ORIG_EXP & flags) == RPUNITS_POS_EXP)  {
581        if (exponent < 0) {
582            exponent = exponent * -1;
583        }
584    }
585    else if ( (RPUNITS_ORIG_EXP & flags) == RPUNITS_NEG_EXP)  {
586        if (exponent > 0) {
587            exponent = exponent * -1;
588        }
589    }
590
591    if (exponent == 1) {
592        unitText << units;
593    }
594    else {
595        unitText << units << exponent;
596    }
597
598    return (std::string(unitText.str()));
599}
600
601/**********************************************************************/
602// METHOD: getSearchName()
603/// Report the units name used when searching through the units dictionary.
604/**
605 * Reports the search name used to store and retrieve this object from
606 * the units dictionary.
607 */
608
609std::string
610RpUnits::getSearchName() const {
611
612    std::string searchName = getUnitsName();
613
614    std::transform( searchName.begin(),
615                    searchName.end(),
616                    searchName.begin(),
617                    tolower );
618
619    return (searchName);
620}
621
622/**********************************************************************/
623// METHOD: getExponent()
624/// Report the exponent of the units of this object back to caller.
625/**
626 * Reports the exponent of the units represented by this
627 * object, back to the caller. Note that if the exponent == 1, no
628 * exponent will be printed.
629 */
630
631double
632RpUnits::getExponent() const {
633
634    return exponent;
635}
636
637/**********************************************************************/
638// METHOD: getBasis()
639/// Retrieve the RpUnits object representing the basis of this object.
640/**
641 * Returns a pointer to a RpUnits object which, on success, points to the
642 * RpUnits object that is the basis of the calling object.
643 */
644
645const RpUnits *
646RpUnits::getBasis() const {
647
648    return basis;
649}
650
651/**********************************************************************/
652// METHOD: setMetric()
653/// Set the metric flag of the object.
654/**
655 * Set the metric flag of the object
656 */
657
658RpUnits&
659RpUnits::setMetric(bool newVal) {
660
661    metric = newVal;
662    return *this;
663}
664
665/**********************************************************************/
666// METHOD: makeBasis()
667/// Convert a value into its RpUnits's basis.
668/**
669 *  convert the current unit to its basis units
670 *
671 *  Return Codes
672 *      0) no error (could also mean or no prefix was found)
673 *          in some cases, this means the value is in its basis format
674 *      1) the prefix found does not have a built in factor associated.
675 *
676 */
677
678double
679RpUnits::makeBasis(double value, int* result) const {
680
681    double retVal = value;
682
683    if (result) {
684        *result = 0;
685    }
686
687    if (basis == NULL) {
688        // this unit is a basis
689        // do nothing
690    }
691    else {
692        retVal = convert(basis,value,result);
693    }
694
695    return retVal;
696}
697
698/**********************************************************************/
699// METHOD: makeBasis()
700/// Convert a value into its RpUnits's basis.
701/**
702 *
703 */
704
705const RpUnits&
706RpUnits::makeBasis(double* value, int* result) const {
707    double retVal = *value;
708    int convResult = 1;
709
710    if (basis == NULL) {
711        // this unit is a basis
712        // do nothing
713    }
714    else {
715        retVal = convert(basis,retVal,&convResult);
716    }
717
718    if ( (convResult == 0) ) {
719        *value = retVal;
720    }
721
722    if (result) {
723        *result = convResult;
724    }
725
726    return *this;
727}
728
729/**********************************************************************/
730// METHOD: makeMetric()
731/// Define a unit type to be stored as a Rappture Unit.
732/**
733 *  static int makeMetric(RpUnits * basis);
734 *  create the metric attachments for the given basis.
735 *  should only be used if this unit is of metric type
736 */
737
738/*
739int
740RpUnits::makeMetric(RpUnits* basis) {
741
742    if (!basis) {
743        return 0;
744    }
745
746    basis->setMetric(true);
747
748    return 0;
749}
750*/
751
752/**********************************************************************/
753// METHOD: find()
754/// Find a simple RpUnits Object from the provided string.
755/**
756 */
757
758const RpUnits*
759RpUnits::find(std::string key,
760        RpDict<std::string,RpUnits*,_key_compare>::RpDictHint hint ) {
761
762    RpDictEntry<std::string,RpUnits*,_key_compare>*
763        unitEntry = &(dict->getNullEntry());
764    RpDictEntry<std::string,RpUnits*,_key_compare>*
765        nullEntry = &(dict->getNullEntry());
766    double exponent = 1;
767    int idx = 0;
768    std::stringstream tmpKey;
769
770    if (key[0] == '/') {
771        // check to see if there is an exponent at the end
772        // of the search string
773        idx = RpUnits::grabExponent(key, &exponent);
774        tmpKey << key.substr(1,idx-1) << (-1*exponent);
775        key = tmpKey.str();
776    }
777
778    if (unitEntry == nullEntry) {
779        // pass 1 - look for the unit name as it was stated by the user
780        // dict->toggleCI();
781        unitEntry = &(dict->find(key,hint,!RPUNITS_CASE_INSENSITIVE));
782        // dict->toggleCI();
783    }
784
785    if (unitEntry == nullEntry) {
786        // pass 2 - use case insensitivity to look for the unit
787        unitEntry = &(dict->find(key,hint,RPUNITS_CASE_INSENSITIVE));
788    }
789
790    if ( (!unitEntry->isValid()) || (unitEntry == nullEntry) ) {
791        // unitEntry = NULL;
792        return NULL;
793    }
794
795    return *(unitEntry->getValue());
796}
797
798/**********************************************************************/
799// METHOD: validate()
800/// Split a string of units and check that each unit is available as an object
801/**
802 * Splits a string of units like cm2/kVns into a list of units like
803 * cm2, kV1, ns1 where an exponent is provided for each list entry.
804 * It checks to see that each unit actually exists as a valid defined unit.
805 * If the unit exists or can be interpreted, the function keeps parsing the
806 * string until it reaches the end of the string. If the function comes
807 * across a unit that is unrecognized or can not be interpreted, then it
808 * returns error (a non-zero value).
809 *
810 * if &compatList == NULL, no compatible list of units will be generated.
811 * this function does not do a good job of placing the available units
812 * back into the original formula. i still need to work on this.
813 */
814
815int
816RpUnits::validate ( std::string& inUnits,
817                    std::string& type,
818                    std::list<std::string>* compatList ) {
819
820    std::string sendUnitStr = "";
821    double exponent         = 1;
822    int err                 = 0;
823    const RpUnits* unit     = NULL;
824    std::list<std::string> basisCompatList;
825    std::list<std::string>::iterator compatListIter;
826    std::stringstream unitWExp;
827    RpUnitsList inUnitsList;
828    RpUnitsListIter inIter;
829
830    // err tells us if we encountered any unrecognized units
831    err = RpUnits::units2list(inUnits,inUnitsList,type);
832    RpUnits::list2units(inUnitsList,inUnits);
833    inIter = inUnitsList.begin();
834
835    while ( inIter != inUnitsList.end() ) {
836
837        unit = inIter->getUnitsObj();
838        exponent = inIter->getExponent();
839
840        // merge the compatible units
841        if (compatList) {
842
843            basisCompatList = unit->getCompatible(exponent);
844            compatList->merge(basisCompatList);
845        }
846
847        inIter++;
848    }
849
850    // clean out any duplicate entries.
851    if (compatList) {
852        compatList->unique();
853    }
854
855    return err;
856}
857
858
859/**********************************************************************/
860// METHOD: negateListExponents()
861/// Negate the exponents on every element in unitsList
862/**
863 */
864
865int
866RpUnits::negateListExponents(RpUnitsList& unitsList) {
867    RpUnitsListIter iter = unitsList.begin();
868    int nodeCnt = unitsList.size();
869
870    if (nodeCnt > 0) {
871        for (; iter != unitsList.end(); iter++) {
872            iter->negateExponent();
873            nodeCnt--;
874        }
875    }
876
877    return nodeCnt;
878}
879
880/**********************************************************************/
881// METHOD: negateExponent()
882/// Negate the exponent on the current RpUnitsListEntry
883/**
884 */
885
886void
887RpUnitsListEntry::negateExponent() const {
888    exponent = exponent * -1;
889    return;
890}
891
892/**********************************************************************/
893// METHOD: name()
894/// Provide the caller with the name of this object
895/**
896 */
897
898std::string
899RpUnitsListEntry::name(int flags) const {
900    std::stringstream name;
901    double myExp = exponent;
902
903    if ( (RPUNITS_ORIG_EXP & flags) == RPUNITS_POS_EXP)  {
904        if (myExp < 0) {
905            myExp = myExp * -1;
906        }
907    }
908    else if ( (RPUNITS_ORIG_EXP & flags) == RPUNITS_NEG_EXP)  {
909        if (myExp > 0) {
910            myExp = myExp * -1;
911        }
912    }
913
914    if (prefix != NULL) {
915        name << prefix->getUnits();
916    }
917
918    name << unit->getUnits();
919
920    if ((RPUNITS_ORIG_EXP & flags) == RPUNITS_STRICT_NAME) {
921        // if the user asks for strict naming,
922        // always place the exponent on the name
923        name << myExp;
924    }
925    else if (myExp != 1.0) {
926        // if the user does not ask for strict naming,
927        // check to see if the exponent == 1.
928        // If not, then add exponent to name
929        name << myExp;
930    }
931
932    return std::string(name.str());
933}
934
935/**********************************************************************/
936// METHOD: getBasis()
937/// Provide the caller with the basis of the RpUnits object being stored
938/**
939 */
940
941const RpUnits*
942RpUnitsListEntry::getBasis() const {
943    return unit->getBasis();
944}
945
946/**********************************************************************/
947// METHOD: getUnitsObj()
948/// Return the RpUnits Object from a RpUnitsListEntry.
949/**
950 */
951
952const RpUnits*
953RpUnitsListEntry::getUnitsObj() const {
954    return unit;
955}
956
957/**********************************************************************/
958// METHOD: getExponent()
959/// Return the exponent of an RpUnitsListEntry.
960/**
961 */
962
963double
964RpUnitsListEntry::getExponent() const {
965    return exponent;
966}
967
968/**********************************************************************/
969// METHOD: getPrefix()
970/// Return the prefix of an RpUnitsListEntry.
971/**
972 */
973
974const RpUnits*
975RpUnitsListEntry::getPrefix() const {
976    return prefix;
977}
978
979/**********************************************************************/
980// METHOD: printList()
981/// Traverse a RpUnitsList and print out the name of each element.
982/**
983 */
984
985int
986RpUnits::printList(RpUnitsList& unitsList) {
987    RpUnitsListIter iter = unitsList.begin();
988    int nodeCnt = unitsList.size();
989
990    if (nodeCnt > 0) {
991        for (; iter != unitsList.end(); iter++) {
992            std::cout << iter->name() << " ";
993            nodeCnt--;
994        }
995        std::cout << std::endl;
996    }
997
998    return nodeCnt;
999}
1000
1001/**********************************************************************/
1002// METHOD: units2list()
1003/// Split a string of units into a list of units with exponents and prefixes.
1004/**
1005 * Splits a string of units like cm2/kVns into a list of units like
1006 * cm2, kV-1, ns-1 where an exponent is provided for each list entry.
1007 * List entries are found by comparing units strings to the names
1008 * in the dictionary.
1009 */
1010
1011int
1012RpUnits::units2list ( const std::string& inUnits,
1013                      RpUnitsList& outList,
1014                      std::string& type ) {
1015
1016    std::string myInUnits   = inUnits;
1017    std::stringstream sendUnitStr;
1018    double exponent         = 1;
1019    int offset              = 0;
1020    int idx                 = 0;
1021    int last                = 0;
1022    int err                 = 0;
1023    const RpUnits* unit     = NULL;
1024    const RpUnits* prefix   = NULL;
1025
1026
1027    while ( !myInUnits.empty() ) {
1028
1029        // check to see if we came across a '/' character
1030        last = myInUnits.length()-1;
1031        if (myInUnits[last] == '/') {
1032            type = myInUnits[last] + type;
1033            myInUnits.erase(last);
1034            // multiply previous exponents by -1
1035            if ( ! outList.empty() ) {
1036                RpUnits::negateListExponents(outList);
1037            }
1038            continue;
1039        }
1040
1041        // check to see if we came across a '*' character
1042        if (myInUnits[last] == '*') {
1043            // type = myInUnits[last] + type;
1044            // ignore * because we assume everything is multiplied together
1045            myInUnits.erase(last);
1046            continue;
1047        }
1048
1049        // get the exponent
1050        offset = RpUnits::grabExponent(myInUnits,&exponent);
1051        myInUnits.erase(offset);
1052        idx = offset - 1;
1053        last = myInUnits.length()-1;
1054        if (last == -1) {
1055            // the string is empty, units were not correctly entered
1056            err = 1;
1057            break;
1058        }
1059
1060        // grab the largest string we can find
1061        offset = RpUnits::grabUnitString(myInUnits);
1062
1063        // if offset > length, then the grabUnitString went through the whole
1064        // string and did not find a good string we could use as units.
1065        // this generally means the string was filled with non alphabetical
1066        // symbols like *&^%$#@!)(~`{}[]:;"'?/><,.-_=+\ or |
1067
1068        if (offset > last) {
1069            err = 1;
1070            // erase the last offending character
1071            myInUnits.erase(last);
1072            // reset our vars and try again
1073            idx = 0;
1074            offset = 0;
1075            exponent = 1;
1076            continue;
1077        }
1078        else {
1079            idx = offset;
1080        }
1081
1082        // figure out if we have some defined units in that string
1083        sendUnitStr.str(myInUnits.substr(offset,std::string::npos));
1084        grabUnits(sendUnitStr.str(),&offset,&unit,&prefix);
1085        if (unit) {
1086            // a unit was found
1087            // add this unit to the list
1088            // erase the found unit's name from our search string
1089            outList.push_front(RpUnitsListEntry(unit,exponent,prefix));
1090            if (type.compare("") == 0) {
1091                type = unit->getType();
1092            }
1093            else if (type[0] == '/') {
1094                type = unit->getType() + type;
1095            }
1096            else {
1097                type = unit->getType() + "*" + type;
1098            }
1099            myInUnits.erase(idx+offset);
1100        }
1101        else {
1102            // we came across a unit we did not recognize
1103            // raise error and delete character for now
1104            err = 1;
1105            myInUnits.erase(idx);
1106        }
1107
1108        /*
1109        // if the exponent != 1,-1 then do a second search
1110        // for the unit+exponent string that might be defined.
1111        // this is to cover the case were we have defined conversions
1112        // m3<->gal, m3<->L but m is defined
1113        if ( (exponent != 1) && (exponent != -1) ) {
1114            sendUnitStr.str("");
1115            sendUnitStr << unit->getUnits() << exponent;
1116            unit = grabUnits(sendUnitStr.str(),&offset);
1117            if (unit) {
1118                // a unit was found
1119                // add this unit to the list
1120                outList.push_front(RpUnitsListEntry(unit,1.0));
1121            }
1122            else {
1123                // we came across a unit we did not recognize
1124                // do nothing
1125            }
1126        }
1127        */
1128
1129        // reset our vars
1130        idx = 0;
1131        offset = 0;
1132        exponent = 1;
1133    }
1134
1135    return err;
1136}
1137
1138/**********************************************************************/
1139// METHOD: list2units()
1140/// Join a list of units into a string with proper exponents.
1141/**
1142 * Joins a list of units like cm2, kV-1, ns-1, creating a string
1143 * like cm2/kVns.
1144 */
1145
1146int
1147RpUnits::list2units ( RpUnitsList& inList,
1148                      std::string& outUnitsStr) {
1149
1150    RpUnitsListIter inListIter;
1151    std::string inUnits     = "";
1152    double exp              = 0;
1153    int err                 = 0;
1154    std::string numerator   = "";
1155    std::string denominator = "";
1156
1157    inListIter = inList.begin();
1158
1159    while (inListIter != inList.end()) {
1160        exp = inListIter->getExponent();
1161        if (exp > 0) {
1162            numerator += inListIter->name();
1163        }
1164        else if (exp < 0) {
1165            denominator += inListIter->name(RPUNITS_POS_EXP);
1166        }
1167        else {
1168            // we shouldn't get units with exponents of zero
1169        }
1170        inListIter++;
1171    }
1172
1173    outUnitsStr = numerator;
1174    if ( denominator.compare("") != 0 ) {
1175        outUnitsStr += "/" + denominator;
1176    }
1177
1178    return err;
1179}
1180
1181/**********************************************************************/
1182// METHOD: compareListEntryBasis()
1183/// Compare two RpUnits objects to see if they are related by a basis
1184/**
1185 * One step in converting between Rappture Units Objects is to check
1186 * to see if the conversion is an intra-basis conversion. Intra-basis
1187 * conversions include those where all conversions are done within
1188 * the same basis.
1189 *
1190 * Examples of intra-basis conversions include:
1191 *     m -> cm  ( meters to centimeters )
1192 *     cm -> m  ( centimeters to meters )
1193 *     cm -> nm ( centimenters to nanometers )
1194 */
1195
1196int RpUnits::compareListEntryBasis ( RpUnitsList& fromList,
1197                                     RpUnitsListIter& fromIter,
1198                                     RpUnitsListIter& toIter ) {
1199
1200    const RpUnits* toBasis = NULL;
1201    const RpUnits* fromBasis = NULL;
1202    int retVal = 1;
1203    double fromExp = 0;
1204    double toExp = 0;
1205
1206    fromIter = fromList.begin();
1207
1208    // get the basis of the object being stored
1209    // if the basis is NULL, then we'll compare the object
1210    // itself because the object is the basis.
1211    toBasis = toIter->getBasis();
1212    if (toBasis == NULL) {
1213        toBasis = toIter->getUnitsObj();
1214    }
1215
1216    toExp   = toIter->getExponent();
1217
1218    while ( fromIter != fromList.end() ) {
1219
1220        fromExp = fromIter->getExponent();
1221
1222        // in order to convert, exponents must be equal.
1223        if (fromExp == toExp) {
1224
1225            // get the basis of the object being stored
1226            // if the basis is NULL, then we'll compare the object
1227            // itself because the object is the basis.
1228            fromBasis = fromIter->getBasis();
1229            if (fromBasis == NULL) {
1230                fromBasis = fromIter->getUnitsObj();
1231            }
1232
1233            if (toBasis == fromBasis) {
1234                // conversion needed between 2 units of the same basis.
1235                // these two units could actually be the same unit (m->m)
1236                retVal = 0;
1237                break;
1238            }
1239        }
1240
1241        fromIter++;
1242    }
1243
1244    return retVal;
1245}
1246
1247/**********************************************************************/
1248// METHOD: compareListEntrySearch()
1249/// this function will soon be removed.
1250/**
1251 */
1252
1253int RpUnits::compareListEntrySearch ( RpUnitsList& fromList,
1254                                     RpUnitsListIter& fromIter,
1255                                     RpUnitsListIter& toIter ) {
1256
1257    const RpUnits* toBasis = NULL;
1258    const RpUnits* fromBasis = NULL;
1259    int retVal = 1;
1260
1261    fromIter = fromList.begin();
1262
1263    // get the basis of the object being stored
1264    // if the basis is NULL, then we'll compare the object
1265    // itself because the object is the basis.
1266    toBasis = toIter->getBasis();
1267    if (toBasis == NULL) {
1268        toBasis = toIter->getUnitsObj();
1269    }
1270
1271    while ( fromIter != fromList.end() ) {
1272
1273        // get the basis of the object being stored
1274        // if the basis is NULL, then we'll compare the object
1275        // itself because the object is the basis.
1276        fromBasis = fromIter->getBasis();
1277        if (fromBasis == NULL) {
1278            fromBasis = fromIter->getUnitsObj();
1279        }
1280
1281        if (toBasis == fromBasis) {
1282            // conversion needed between 2 units of the same basis.
1283            // these two units could actually be the same unit (m->m)
1284            retVal = 0;
1285            break;
1286        }
1287
1288        fromIter++;
1289    }
1290
1291    return retVal;
1292}
1293
1294/**********************************************************************/
1295// METHOD: convert()
1296/// Convert between RpUnits return a string value with or without units
1297/**
1298 * Convert function so people can just send in two strings and
1299 * we'll see if the units exists and do a conversion
1300 * Example:
1301 *     strVal = RpUnits::convert("300K","C",1);
1302 *
1303 * Returns a string with or without units.
1304 */
1305
1306
1307std::string
1308RpUnits::convert (  std::string val,
1309                    std::string toUnitsName,
1310                    int showUnits,
1311                    int* result ) {
1312
1313    RpUnitsList toUnitsList;
1314    RpUnitsList fromUnitsList;
1315
1316    RpUnitsListIter toIter;
1317    RpUnitsListIter fromIter;
1318    RpUnitsListIter tempIter;
1319
1320    const RpUnits* toUnits = NULL;
1321    const RpUnits* toPrefix = NULL;
1322    const RpUnits* fromUnits = NULL;
1323    const RpUnits* fromPrefix = NULL;
1324
1325    std::string tmpNumVal = "";
1326    std::string fromUnitsName = "";
1327    std::string convVal = "";
1328    std::string type = "";     // junk var used because units2list requires it
1329    std::string retStr = "";
1330    double origNumVal = 0;
1331    double numVal = 0;
1332    double toExp = 0;
1333    double fromExp = 0;
1334    int convErr = 0;
1335    std::stringstream outVal;
1336
1337    double copies = 0;
1338
1339    std::list<std::string> compatList;
1340    std::string listStr;
1341
1342    convertList cList;
1343    convertList totalConvList;
1344
1345
1346    // set  default result flag/error code
1347    if (result) {
1348        *result = 0;
1349    }
1350
1351    // search our string to see where the numeric part stops
1352    // and the units part starts
1353    //
1354    //  convert("5J", "neV") => 3.12075e+28neV
1355    //  convert("3.12075e+28neV", "J") => 4.99999J
1356    // now we can actually get the scientific notation portion of the string.
1357    //
1358
1359    convErr = unitSlice(val,fromUnitsName,numVal);
1360    origNumVal = numVal;
1361
1362    if (convErr != 0) {
1363        // no conversion was done.
1364        // number in incorrect format probably.
1365        if (result) {
1366            *result = 1;
1367        }
1368        return val;
1369    }
1370
1371    if (toUnitsName.empty())  {
1372        // there were no units in the input
1373        // string or no conversion needed
1374        // assume fromUnitsName = toUnitsName
1375        // return the correct value
1376        if (result) {
1377            *result = 0;
1378        }
1379
1380        if (showUnits == RPUNITS_UNITS_ON) {
1381            outVal << numVal << fromUnitsName;
1382        }
1383        else {
1384            outVal << numVal;
1385        }
1386
1387        return std::string(outVal.str());
1388    }
1389
1390    // check if the fromUnitsName is empty or
1391    // if the fromUnitsName == toUnitsName
1392    // these are conditions where no conversion is needed
1393    if ( (fromUnitsName.empty()) || (toUnitsName == fromUnitsName) )  {
1394        // there were no units in the input
1395        // string or no conversion needed
1396        // assume fromUnitsName = toUnitsName
1397        // return the correct value
1398        if (result) {
1399            *result = 0;
1400        }
1401
1402        if (showUnits == RPUNITS_UNITS_ON) {
1403            outVal << numVal << toUnitsName;
1404        }
1405        else {
1406            outVal << numVal;
1407        }
1408
1409        return std::string(outVal.str());
1410    }
1411
1412    convErr = RpUnits::units2list(toUnitsName,toUnitsList,type);
1413    if (convErr) {
1414        if (result) {
1415            *result = convErr;
1416        }
1417        retStr = "Unrecognized units: \"" + toUnitsName + "\". Please specify valid Rappture Units";
1418        return retStr;
1419    }
1420
1421    convErr = RpUnits::units2list(fromUnitsName,fromUnitsList,type);
1422    if (convErr) {
1423        if (result) {
1424            *result = convErr;
1425        }
1426        type = "";
1427        RpUnits::validate(toUnitsName,type,&compatList);
1428        list2str(compatList,listStr);
1429        retStr = "Unrecognized units: \"" + fromUnitsName
1430                + "\".\nShould be units of type " + type + " (" + listStr + ")";
1431        return retStr;
1432    }
1433
1434    fromIter = fromUnitsList.begin();
1435    toIter = toUnitsList.begin();
1436
1437    while ( (toIter != toUnitsList.end()) && (fromIter != fromUnitsList.end()) && (!convErr) ) {
1438        fromUnits = fromIter->getUnitsObj();
1439        fromPrefix = fromIter->getPrefix();
1440        toUnits = toIter->getUnitsObj();
1441        toPrefix = toIter->getPrefix();
1442
1443        cList.clear();
1444
1445        if (fromPrefix != NULL) {
1446            cList.push_back(fromPrefix->convList->conv->convForwFxnPtr);
1447        }
1448
1449        convErr = fromUnits->getConvertFxnList(toUnits, cList);
1450
1451        if (toPrefix != NULL) {
1452            cList.push_back(toPrefix->convList->conv->convBackFxnPtr);
1453        }
1454
1455        if (convErr == 0) {
1456
1457            toExp = toIter->getExponent();
1458            fromExp = fromIter->getExponent();
1459
1460            if (fromExp == toExp) {
1461                copies = fromExp;
1462                if (fromExp < 0) {
1463                    copies = copies * -1.00;
1464                    totalConvList.push_back(&invert);
1465                }
1466                while (copies > 0) {
1467                    combineLists(totalConvList,cList);
1468                    copies--;
1469                }
1470                if (fromExp < 0) {
1471                    totalConvList.push_back(&invert);
1472                }
1473            }
1474            else {
1475                // currently we cannot handle conversions of
1476                // units where the exponents are different
1477                convErr++;
1478            }
1479
1480        }
1481
1482        if (convErr == 0) {
1483            // successful conversion reported
1484            // remove the elements from the lists
1485            tempIter = toIter;
1486            toIter++;
1487            toUnitsList.erase(tempIter);
1488
1489            tempIter = fromIter;
1490            fromUnitsList.erase(tempIter);
1491            fromIter = fromUnitsList.begin();
1492        }
1493        else {
1494            // no conversion available?
1495            fromIter++;
1496            if (fromIter == fromUnitsList.end()) {
1497
1498                fromIter = fromUnitsList.begin();
1499                toIter++;
1500
1501                if (toIter == toUnitsList.end())  {
1502
1503                    toIter = toUnitsList.begin();
1504
1505                    // raise error that there was an
1506                    // unrecognized conversion request
1507
1508                    convErr++;
1509                    retStr = "Conversion unavailable: (";
1510                    while (fromIter != fromUnitsList.end()) {
1511                        /*
1512                        if (fromIter != fromUnitsList.begin()) {
1513                            retStr += " or ";
1514                        }
1515                        */
1516                        retStr += fromIter->name();
1517                        fromIter++;
1518                    }
1519                    retStr += ") -> (";
1520
1521                    // tempIter = toIter;
1522
1523                    while (toIter != toUnitsList.end()) {
1524                        retStr += toIter->name();
1525                        toIter++;
1526                    }
1527                    retStr += ")";
1528
1529                    type = "";
1530                    RpUnits::validate(toUnitsName,type,&compatList);
1531                    list2str(compatList,listStr);
1532                    retStr += "\nPlease enter units of type "
1533                                + type + " (" + listStr + ")";
1534
1535
1536                    // exit and report the error
1537
1538                    /*
1539                    toIter = tempIter;
1540                    toIter++;
1541                    toUnitsList.erase(tempIter);
1542                    */
1543                }
1544                else {
1545                    // keep searching for units to convert
1546                    // until we are out of units in the
1547                    // fromUnitsList and toUnitsList.
1548
1549                    convErr = 0;
1550                }
1551            }
1552            else {
1553                // keep searching for units to convert
1554                // until we are out of units in the
1555                // fromUnitsList and toUnitsList.
1556
1557                convErr = 0;
1558            }
1559        }
1560    }
1561
1562
1563
1564    if (convErr == 0) {
1565        // if ( (fromIter != fromUnitsList.end()) || (toIter != toUnitsList.end()) ) {
1566        if ( fromUnitsList.size() || toUnitsList.size() ) {
1567            // raise error that there was an
1568            // unrecognized conversion request
1569
1570            convErr++;
1571            retStr = "unmatched units in conversion: (";
1572
1573            fromIter = fromUnitsList.begin();
1574            while (fromIter != fromUnitsList.end()) {
1575                retStr += fromIter->name();
1576                fromIter++;
1577            }
1578
1579            if (fromUnitsList.size() && toUnitsList.size()) {
1580                retStr += ") -> (";
1581            }
1582
1583            toIter = toUnitsList.begin();
1584            while (toIter != toUnitsList.end()) {
1585                retStr += toIter->name();
1586                toIter++;
1587            }
1588            retStr += ")";
1589            type = "";
1590            RpUnits::validate(toUnitsName,type,&compatList);
1591            list2str(compatList,listStr);
1592            retStr += "\nPlease enter units of type "
1593                        + type + " (" + listStr + ")";
1594
1595        }
1596        else {
1597            // apply the conversion and check for errors
1598            convErr = applyConversion (&numVal, totalConvList);
1599            if (convErr == 0) {
1600                // outVal.flags(std::ios::fixed);
1601                // outVal.precision(10);
1602                if (showUnits == RPUNITS_UNITS_ON) {
1603                    outVal << numVal << toUnitsName;
1604                }
1605                else {
1606                    outVal << numVal;
1607                }
1608                retStr = outVal.str();
1609            }
1610            else {
1611
1612            }
1613        }
1614    }
1615
1616    if ( (result) && (*result == 0) ) {
1617        *result = convErr;
1618    }
1619
1620    return retStr;
1621
1622}
1623
1624/**********************************************************************/
1625// METHOD: convert()
1626/// Convert between RpUnits return a string value with or without units
1627/**
1628 * Returns a string value with or without units.
1629 */
1630
1631std::string
1632RpUnits::convert ( const  RpUnits* toUnits,
1633                   double val,
1634                   int showUnits,
1635                   int* result )  const {
1636
1637    double retVal = convert(toUnits,val,result);
1638    std::stringstream unitText;
1639
1640
1641    if (showUnits == RPUNITS_UNITS_ON) {
1642        unitText << retVal << toUnits->getUnitsName();
1643    }
1644    else {
1645        unitText << retVal;
1646    }
1647
1648    return (std::string(unitText.str()));
1649
1650}
1651
1652/**********************************************************************/
1653// METHOD: convert()
1654/// Convert between RpUnits using an RpUnits Object to describe toUnit.
1655/**
1656 * User function to convert a value to the provided RpUnits* toUnits
1657 * if it exists as a conversion from the basis
1658 * example
1659 *      cm.convert(meter,10)
1660 *      cm.convert(angstrum,100)
1661 *
1662 * Returns a double value without units.
1663 */
1664
1665double
1666RpUnits::convert(const RpUnits* toUnit, double val, int* result) const {
1667
1668    // currently we convert this object to its basis and look for the
1669    // connection to the toUnit object from the basis.
1670
1671    double value = val;
1672    const RpUnits* toBasis = toUnit->getBasis();
1673    const RpUnits* fromUnit = this;
1674    const RpUnits* dictToUnit = NULL;
1675    convEntry *p;
1676    int my_result = 0;
1677
1678    RpUnitsTypes::RpUnitsTypesHint hint = NULL;
1679
1680    // set *result to a default value
1681    if (result) {
1682        *result = 1;
1683    }
1684
1685    // guard against converting to the units you are converting from...
1686    // ie. meters->meters
1687    if (this->getUnitsName() == toUnit->getUnitsName()) {
1688        if (result) {
1689            *result = 0;
1690        }
1691        return val;
1692    }
1693
1694    // convert unit to the basis
1695    // makeBasis(&value);
1696    // trying to avoid the recursive way of converting to the basis.
1697    // need to rethink this.
1698    //
1699    if ( (basis) && (basis->getUnitsName() != toUnit->getUnitsName()) ) {
1700        value = convert(basis,value,&my_result);
1701        if (my_result == 0) {
1702            fromUnit = basis;
1703        }
1704    }
1705
1706    // find the toUnit in our dictionary.
1707    // if the toUnits has a basis, we need to search for the basis
1708    // and convert between basis' and then convert again back to the
1709    // original unit.
1710    if ( (toBasis) && (toBasis->getUnitsName() != fromUnit->getUnitsName()) ) {
1711        hint = RpUnitsTypes::getTypeHint(toBasis->getType());
1712        dictToUnit = find(toBasis->getUnitsName(), hint);
1713    }
1714    else {
1715        hint = RpUnitsTypes::getTypeHint(toUnit->getType());
1716        dictToUnit = find(toUnit->getUnitsName(), hint);
1717    }
1718
1719    // did we find the unit in the dictionary?
1720    if (dictToUnit == NULL) {
1721        // toUnit was not found in the dictionary
1722        return val;
1723    }
1724
1725    // search through the conversion list to find
1726    // the conversion to the toUnit.
1727
1728    if (basis) {
1729        p = basis->convList;
1730    }
1731    else {
1732        p = this->convList;
1733    }
1734
1735    if (p == NULL) {
1736        // there are no conversions
1737        return val;
1738    }
1739
1740    // loop through our conversion list looking for the correct conversion
1741    do {
1742
1743        if ( (p->conv->toPtr == dictToUnit) && (p->conv->fromPtr == fromUnit) ) {
1744            // we found our conversion
1745            // call the function pointer with value
1746
1747            // this should probably be re thought out
1748            // the problem is that convForwFxnPtr has the conversion for a
1749            // one arg conv function pointer and convForwFxnPtrDD has the
1750            // conversion for a two arg conv function pointer
1751            // need to make this simpler, more logical maybe only allow 2 arg
1752            if (       (p->conv->convForwFxnPtr)
1753                    && (! p->conv->convForwFxnPtrDD) ) {
1754
1755                value = p->conv->convForwFxnPtr(value);
1756            }
1757            else if (  (p->conv->convForwFxnPtrDD)
1758                    && (! p->conv->convForwFxnPtr) ) {
1759
1760                value =
1761                    p->conv->convForwFxnPtrDD(value, fromUnit->getExponent());
1762            }
1763
1764            // check to see if we converted to the actual requested unit
1765            // or to the requested unit's basis.
1766            // if we converted to the requested unit's basis. we need to
1767            // do one last conversion from the requested unit's basis back
1768            // to the requested unit.
1769            if ( (toBasis) && (toBasis->getUnitsName() != fromUnit->getUnitsName()) ) {
1770                my_result = 0;
1771                value = toBasis->convert(toUnit,value,&my_result);
1772                if (my_result != 0) {
1773                    if (result) {
1774                        *result += 1;
1775                    }
1776                }
1777            }
1778
1779            // change the result code to zero, a conversion was performed
1780            // (we think)... its ture that it is possible to get to this
1781            // point and have skipped the conversion because the
1782            // conversion object was not properly created...
1783            // ie. both fxn ptrs were null or neither fxn ptr was null
1784            //
1785            if (result && (*result == 1)) {
1786                *result = 0;
1787            }
1788            break;
1789        }
1790
1791        if ( (p->conv->toPtr == fromUnit) && (p->conv->fromPtr == dictToUnit) ) {
1792            // we found our conversion
1793            // call the function pointer with value
1794
1795            // this should probably be re thought out
1796            // the problem is that convForwFxnPtr has the conversion for a
1797            // one arg conv function pointer and convForwFxnPtrDD has the
1798            // conversion for a two arg conv function pointer
1799            // need to make this simpler, more logical maybe only allow 2 arg
1800            if (       (p->conv->convBackFxnPtr)
1801                    && (! p->conv->convBackFxnPtrDD) ) {
1802
1803                value = p->conv->convBackFxnPtr(value);
1804            }
1805            else if (  (p->conv->convBackFxnPtrDD)
1806                    && (! p->conv->convBackFxnPtr) ) {
1807
1808                value =
1809                    p->conv->convBackFxnPtrDD(value, fromUnit->getExponent());
1810            }
1811
1812            // check to see if we converted to the actual requested unit
1813            // or to the requested unit's basis.
1814            // if we converted to the requested unit's basis. we need to
1815            // do one last conversion from the requested unit's basis back
1816            // to the requested unit.
1817            if ( (toBasis) && (toBasis->getUnitsName() != fromUnit->getUnitsName()) ) {
1818                my_result = 0;
1819                value = toBasis->convert(toUnit,value,&my_result);
1820                if (my_result != 0) {
1821                    if (result) {
1822                        *result += 1;
1823                    }
1824                }
1825            }
1826
1827            // change the result code to zero, a conversion was performed
1828            // (we think)... its ture that it is possible to get to this
1829            // point and have skipped the conversion because the
1830            // conversion object was not properly created...
1831            // ie. both fxn ptrs were null or neither fxn ptr was null
1832            //
1833            if (result && (*result == 1)) {
1834                *result = 0;
1835            }
1836            break;
1837        }
1838
1839        p = p->next;
1840
1841    } while (p != NULL);
1842
1843
1844    if ( p == NULL) {
1845        // we did not find the conversion
1846        if (result) {
1847            *result += 1;
1848        }
1849        return val;
1850    }
1851
1852    // we found the conversion.
1853    // return the converted value.
1854    return value;
1855
1856}
1857
1858
1859/**********************************************************************/
1860// METHOD: convert()
1861/// Convert a value between RpUnits using user defined conversions
1862/**
1863 */
1864
1865void*
1866RpUnits::convert(const RpUnits* toUnit, void* val, int* result) const {
1867
1868    // currently we convert this object to its basis and look for the
1869    // connection ot the toUnit object from the basis.
1870
1871    void* value = val;
1872    const RpUnits* toBasis = toUnit->getBasis();
1873    const RpUnits* fromUnit = this;
1874    const RpUnits* dictToUnit = NULL;
1875    convEntry *p;
1876    int my_result = 0;
1877
1878    RpUnitsTypes::RpUnitsTypesHint hint = NULL;
1879
1880    // set *result to a default value
1881    if (result) {
1882        *result = 1;
1883    }
1884
1885    // guard against converting to the units you are converting from...
1886    // ie. meters->meters
1887    if (this->getUnitsName() == toUnit->getUnitsName()) {
1888        if (result) {
1889            *result = 0;
1890        }
1891        return val;
1892    }
1893
1894    // convert unit to the basis
1895    // makeBasis(&value);
1896    // trying to avoid the recursive way of converting to the basis.
1897    // need to rethink this.
1898    //
1899    if ( (basis) && (basis->getUnitsName() != toUnit->getUnitsName()) ) {
1900        value = convert(basis,value,&my_result);
1901        if (my_result == 0) {
1902            fromUnit = basis;
1903        }
1904    }
1905
1906    // find the toUnit in our dictionary.
1907    // if the toUnits has a basis, we need to search for the basis
1908    // and convert between basis' and then convert again back to the
1909    // original unit.
1910    if ( (toBasis) && (toBasis->getUnitsName() != fromUnit->getUnitsName()) ) {
1911        hint = RpUnitsTypes::getTypeHint(toBasis->getType());
1912        dictToUnit = find(toBasis->getUnitsName(), hint);
1913    }
1914    else {
1915        hint = RpUnitsTypes::getTypeHint(toUnit->getType());
1916        dictToUnit = find(toUnit->getUnitsName(), hint);
1917    }
1918
1919    // did we find the unit in the dictionary?
1920    if (dictToUnit == NULL) {
1921        // toUnit was not found in the dictionary
1922        return val;
1923    }
1924
1925    // search through the conversion list to find
1926    // the conversion to the toUnit.
1927
1928    if (basis) {
1929        p = basis->convList;
1930    }
1931    else {
1932        p = this->convList;
1933    }
1934
1935    if (p == NULL) {
1936        // there are no conversions
1937        return val;
1938    }
1939
1940    // loop through our conversion list looking for the correct conversion
1941    do {
1942
1943        if ( (p->conv->toPtr == dictToUnit) && (p->conv->fromPtr == fromUnit) ) {
1944            // we found our conversion
1945            // call the function pointer with value
1946
1947            value = p->conv->convForwFxnPtrVoid(p->conv->convForwData,value);
1948
1949            // check to see if we converted to the actual requested unit
1950            // or to the requested unit's basis.
1951            // if we converted to the requested unit's basis. we need to
1952            // do one last conversion from the requested unit's basis back
1953            // to the requested unit.
1954            if ( (toBasis) && (toBasis->getUnitsName() != fromUnit->getUnitsName()) ) {
1955                my_result = 0;
1956                value = toBasis->convert(toUnit,value,&my_result);
1957                if (my_result != 0) {
1958                    if (result) {
1959                        *result += 1;
1960                    }
1961                }
1962            }
1963
1964            // change the result code to zero, a conversion was performed
1965            // (we think)... its ture that it is possible to get to this
1966            // point and have skipped the conversion because the
1967            // conversion object was not properly created...
1968            // ie. both fxn ptrs were null or neither fxn ptr was null
1969            //
1970            if (result && (*result == 1)) {
1971                *result = 0;
1972            }
1973            break;
1974        }
1975
1976        if ( (p->conv->toPtr == fromUnit) && (p->conv->fromPtr == dictToUnit) ) {
1977            // we found our conversion
1978            // call the function pointer with value
1979
1980            value = p->conv->convBackFxnPtrVoid(p->conv->convBackData,value);
1981
1982            // check to see if we converted to the actual requested unit
1983            // or to the requested unit's basis.
1984            // if we converted to the requested unit's basis. we need to
1985            // do one last conversion from the requested unit's basis back
1986            // to the requested unit.
1987            if ( (toBasis) && (toBasis->getUnitsName() != fromUnit->getUnitsName()) ) {
1988                my_result = 0;
1989                value = toBasis->convert(toUnit,value,&my_result);
1990                if (my_result != 0) {
1991                    if (result) {
1992                        *result += 1;
1993                    }
1994                }
1995            }
1996
1997            // change the result code to zero, a conversion was performed
1998            // (we think)... its ture that it is possible to get to this
1999            // point and have skipped the conversion because the
2000            // conversion object was not properly created...
2001            // ie. both fxn ptrs were null or neither fxn ptr was null
2002            //
2003            if (result && (*result == 1)) {
2004                *result = 0;
2005            }
2006            break;
2007        }
2008
2009        p = p->next;
2010
2011    } while (p != NULL);
2012
2013
2014    if ( p == NULL) {
2015        // we did not find the conversion
2016        if (result) {
2017            *result += 1;
2018        }
2019        return val;
2020    }
2021
2022    // we found the conversion.
2023    // return the converted value.
2024    return value;
2025
2026}
2027
2028/**********************************************************************/
2029// METHOD: getConvertFxnList()
2030/// Return list of fxn pointers for converting two simple RpUnits objects.
2031/**
2032 * Return the conversion list that will convert from this RpUnits
2033 * object to the provided toUnits object if the conversion is defined
2034 * example
2035 *      cm.getConvertFxnList(meter,cList)
2036 *      cm.getConvertFxnList(angstrum,cList)
2037 *
2038 * Returns a list of conversion objects, represented by cList,
2039 * on success that a value can be applied to. The return value
2040 * will be zero (0).
2041 * Returns non-zero value on failure.
2042 */
2043
2044int
2045RpUnits::getConvertFxnList(const RpUnits* toUnit, convertList& cList) const {
2046
2047    // currently we convert this object to its basis and look for the
2048    // connection to the toUnit object from the basis.
2049
2050    const RpUnits* toBasis = toUnit->getBasis();
2051    const RpUnits* fromUnit = this;
2052    const RpUnits* dictToUnit = NULL;
2053    convEntry *p;
2054    int result = 0;
2055
2056    // guard against converting to the units you are converting from...
2057    // ie. meters->meters
2058    if (this->getUnitsName() == toUnit->getUnitsName()) {
2059        return result;
2060    }
2061
2062    // convert unit to the basis
2063    // makeBasis(&value);
2064    // trying to avoid the recursive way of converting to the basis.
2065    // need to rethink this.
2066    //
2067    if ( (basis) && (basis->getUnitsName() != toUnit->getUnitsName()) ) {
2068        result = fromUnit->getConvertFxnList(basis,cList);
2069        if (result == 0) {
2070            fromUnit = basis;
2071        }
2072        else {
2073            // exit because an error occured while
2074            // trying to convert to the basis
2075            return result;
2076        }
2077    }
2078
2079    // find the toUnit in our dictionary.
2080    // if the toUnits has a basis, we need to search for the basis
2081    // and convert between basis' and then convert again back to the
2082    // original unit.
2083    if ( (toBasis) && (toBasis->getUnitsName() != fromUnit->getUnitsName()) ) {
2084        dictToUnit = find(  toBasis->getUnitsName(),
2085                            &RpUnitsTypes::hintTypeNonPrefix );
2086    }
2087    else {
2088        dictToUnit = find(  toUnit->getUnitsName(),
2089                            &RpUnitsTypes::hintTypeNonPrefix );
2090    }
2091
2092    // did we find the unit in the dictionary?
2093    if (dictToUnit == NULL) {
2094        // toUnit was not found in the dictionary
2095        result = 1;
2096        return result;
2097    }
2098
2099    // search through the conversion list to find
2100    // the conversion to the toUnit.
2101
2102    if (basis) {
2103        p = basis->convList;
2104    }
2105    else {
2106        p = this->convList;
2107    }
2108
2109    if (p == NULL) {
2110        // there are no conversions
2111        result = 1;
2112        return result;
2113    }
2114
2115    // loop through our conversion list looking for the correct conversion
2116    do {
2117
2118        if ( (p->conv->toPtr == dictToUnit) && (p->conv->fromPtr == fromUnit) ) {
2119            // we found our conversion
2120            // call the function pointer with value
2121
2122            // this should probably be re thought out
2123            // the problem is that convForwFxnPtr has the conversion for a
2124            // one arg conv function pointer and convForwFxnPtrDD has the
2125            // conversion for a two arg conv function pointer
2126            // need to make this simpler, more logical maybe only allow 2 arg
2127            if (       (p->conv->convForwFxnPtr)
2128                    && (! p->conv->convForwFxnPtrDD) ) {
2129
2130                // value = p->conv->convForwFxnPtr(value);
2131                cList.push_back(p->conv->convForwFxnPtr);
2132            }
2133            /*
2134            else if (  (p->conv->convForwFxnPtrDD)
2135                    && (! p->conv->convForwFxnPtr) ) {
2136
2137                // value = p->conv->convForwFxnPtrDD(value, fromUnit->getExponent());
2138                cList.pushback(conv);
2139            }
2140            */
2141
2142            // check to see if we converted to the actual requested unit
2143            // or to the requested unit's basis.
2144            // if we converted to the requested unit's basis. we need to
2145            // do one last conversion from the requested unit's basis back
2146            // to the requested unit.
2147            if ( (toBasis) && (toBasis->getUnitsName() != fromUnit->getUnitsName()) ) {
2148                result += toBasis->getConvertFxnList(toUnit,cList);
2149            }
2150
2151            break;
2152        }
2153
2154        if ( (p->conv->toPtr == fromUnit) && (p->conv->fromPtr == dictToUnit) ) {
2155            // we found our conversion
2156            // call the function pointer with value
2157
2158            // this should probably be re thought out
2159            // the problem is that convForwFxnPtr has the conversion for a
2160            // one arg conv function pointer and convForwFxnPtrDD has the
2161            // conversion for a two arg conv function pointer
2162            // need to make this simpler, more logical maybe only allow 2 arg
2163            if (       (p->conv->convBackFxnPtr)
2164                    && (! p->conv->convBackFxnPtrDD) ) {
2165
2166                // value = p->conv->convBackFxnPtr(value);
2167                cList.push_back(p->conv->convBackFxnPtr);
2168            }
2169            /*
2170            else if (  (p->conv->convBackFxnPtrDD)
2171                    && (! p->conv->convBackFxnPtr) ) {
2172
2173                // value = p->conv->convBackFxnPtrDD(value, fromUnit->getExponent());
2174                cList.pushback(conv);
2175            }
2176            */
2177
2178            // check to see if we converted to the actual requested unit
2179            // or to the requested unit's basis.
2180            // if we converted to the requested unit's basis. we need to
2181            // do one last conversion from the requested unit's basis back
2182            // to the requested unit.
2183            if ( (toBasis) && (toBasis->getUnitsName() != fromUnit->getUnitsName()) ) {
2184                result += toBasis->getConvertFxnList(toUnit,cList);
2185            }
2186
2187            break;
2188        }
2189
2190        p = p->next;
2191
2192    } while (p != NULL);
2193
2194
2195    if ( p == NULL) {
2196        // we did not find the conversion
2197        result += 1;
2198    }
2199
2200    // return the converted value and result flag
2201    return result;
2202}
2203
2204/**********************************************************************/
2205// METHOD: applyConversion()
2206/// Apply a list of conversions in cList to the value val
2207/**
2208 * Apply a list of conversions, represented by cList, to the value
2209 * val.
2210 *
2211 * Returns an integer value of zero (0) on success
2212 * Returns non-zero value on failure.
2213 */
2214
2215int
2216RpUnits::applyConversion(double* val, convertList& cList) {
2217
2218    convertList::iterator iter;
2219
2220    if(val == NULL) {
2221        return 1;
2222    }
2223
2224    for(iter = cList.begin(); iter != cList.end(); iter++)
2225    {
2226        *val = (*iter)(*val);
2227    }
2228
2229    return 0;
2230}
2231
2232/**********************************************************************/
2233// METHOD: combineLists()
2234/// combine two convertLists in an orderly fasion
2235/**
2236 *
2237 * elements of l2 are pushed onto l1 in the same order in which it
2238 * exists in l2. l1 is changed in this function.
2239 *
2240 * Returns an integer value of zero (0) on success
2241 * Returns non-zero value on failure.
2242 */
2243
2244int
2245RpUnits::combineLists(convertList& l1, convertList& l2) {
2246
2247    for (convertList::iterator iter = l2.begin(); iter != l2.end(); iter++) {
2248        l1.push_back(*iter);
2249    }
2250    return 0;
2251
2252}
2253
2254/**********************************************************************/
2255// METHOD: printList()
2256/// print a list
2257/**
2258 *
2259 * Returns an integer value of zero (0) on success
2260 * Returns non-zero value on failure.
2261 */
2262
2263int
2264RpUnits::printList(convertList& l1) {
2265
2266    for (convertList::iterator iter = l1.begin(); iter != l1.end(); iter++) {
2267        printf("%x\n", int((*iter)));
2268    }
2269    return 0;
2270
2271}
2272
2273/**********************************************************************/
2274// METHOD: insert()
2275/// Place an RpUnits Object into the Rappture Units Dictionary.
2276/**
2277 * Return whether the inserted key was new with a non-zero
2278 * value, or if the key already existed with a value of zero.
2279 */
2280
2281int
2282insert(std::string key,RpUnits* val) {
2283
2284    int newRecord = 0;
2285    RpUnitsTypes::RpUnitsTypesHint hint = NULL;
2286
2287    if (val == NULL) {
2288        return -1;
2289    }
2290
2291    hint = RpUnitsTypes::getTypeHint(val->getType());
2292
2293    RpUnits::dict->set(key,val,hint,&newRecord,val->getCI());
2294
2295    return newRecord;
2296}
2297
2298/**********************************************************************/
2299// METHOD: connectConversion()
2300/// Attach conversion information to a RpUnits Object.
2301/**
2302 */
2303
2304void
2305RpUnits::connectConversion(conversion* conv) const {
2306
2307    convEntry* p = convList;
2308
2309    if (p == NULL) {
2310        convList = new convEntry (conv,NULL,NULL);
2311    }
2312    else {
2313        while (p->next != NULL) {
2314            p = p->next;
2315        }
2316
2317        p->next = new convEntry (conv,p,NULL);
2318    }
2319
2320}
2321
2322/**********************************************************************/
2323// METHOD: connectIncarnation()
2324/// Attach incarnation object information to a RpUnits Object.
2325/**
2326 */
2327
2328void
2329RpUnits::connectIncarnation(const RpUnits* unit) const {
2330
2331    incarnationEntry* p = incarnationList;
2332
2333    if (p == NULL) {
2334        incarnationList = new incarnationEntry (unit,NULL,NULL);
2335    }
2336    else {
2337        while (p->next != NULL) {
2338            p = p->next;
2339        }
2340
2341        p->next = new incarnationEntry (unit,p,NULL);
2342    }
2343
2344}
2345
2346/**********************************************************************/
2347// METHOD: addPresets()
2348/// Add a specific set of predefined units to the dictionary
2349/**
2350 */
2351
2352int
2353RpUnits::addPresets (const std::string group) {
2354    int retVal = -1;
2355    if (group.compare("all") == 0) {
2356        retVal = RpUnitsPreset::addPresetAll();
2357    }
2358    else if (group.compare(RP_TYPE_ENERGY) == 0) {
2359        retVal = RpUnitsPreset::addPresetEnergy();
2360    }
2361    else if (group.compare(RP_TYPE_LENGTH) == 0) {
2362        retVal = RpUnitsPreset::addPresetLength();
2363    }
2364    else if (group.compare(RP_TYPE_TEMP) == 0) {
2365        retVal = RpUnitsPreset::addPresetTemp();
2366    }
2367    else if (group.compare(RP_TYPE_TIME) == 0) {
2368        retVal = RpUnitsPreset::addPresetTime();
2369    }
2370    else if (group.compare(RP_TYPE_VOLUME) == 0) {
2371        retVal = RpUnitsPreset::addPresetVolume();
2372    }
2373    else if (group.compare(RP_TYPE_ANGLE) == 0) {
2374        retVal = RpUnitsPreset::addPresetAngle();
2375    }
2376    else if (group.compare(RP_TYPE_MASS) == 0) {
2377        retVal = RpUnitsPreset::addPresetMass();
2378    }
2379    else if (group.compare(RP_TYPE_PREFIX) == 0) {
2380        retVal = RpUnitsPreset::addPresetPrefix();
2381    }
2382    else if (group.compare(RP_TYPE_PRESSURE) == 0) {
2383        retVal = RpUnitsPreset::addPresetPressure();
2384    }
2385    else if (group.compare(RP_TYPE_MISC) == 0) {
2386        retVal = RpUnitsPreset::addPresetMisc();
2387    }
2388
2389    return retVal;
2390}
2391
2392/**********************************************************************/
2393// METHOD: addPresetAll()
2394/// Call all of the addPreset* functions.
2395/**
2396 *
2397 * Add all predefined units to the units dictionary
2398 * Return codes: 0 success, anything else is error
2399 */
2400
2401int
2402RpUnitsPreset::addPresetAll () {
2403
2404    int result = 0;
2405
2406    result += addPresetPrefix();
2407    result += addPresetTime();
2408    result += addPresetTemp();
2409    result += addPresetLength();
2410    result += addPresetEnergy();
2411    result += addPresetVolume();
2412    result += addPresetAngle();
2413    result += addPresetMass();
2414    result += addPresetPressure();
2415    result += addPresetConcentration();
2416    result += addPresetMisc();
2417
2418    return 0;
2419}
2420
2421
2422/**********************************************************************/
2423// METHOD: addPresetPrefix()
2424///
2425/**
2426 * Defines the following unit prefixes:
2427 *   deci        (d)
2428 *   centi       (c)
2429 *   milli       (m)
2430 *   micro       (u)
2431 *   nano        (n)
2432 *   pico        (p)
2433 *   femto       (f)
2434 *   atto        (a)
2435 *   deca        (da)
2436 *   hecto       (h)
2437 *   kilo        (k)
2438 *   mega        (M)
2439 *   giga        (G)
2440 *   tera        (T)
2441 *   peta        (P)
2442 *   exa         (E)
2443 *
2444 * Return codes: 0 success, anything else is error
2445 */
2446
2447int
2448RpUnitsPreset::addPresetPrefix () {
2449
2450    std::string type = RP_TYPE_PREFIX;
2451    RpUnits* basis = NULL;
2452
2453    RpUnits * deci  = NULL;
2454    RpUnits * centi = NULL;
2455    RpUnits * milli = NULL;
2456    RpUnits * micro = NULL;
2457    RpUnits * nano  = NULL;
2458    RpUnits * pico  = NULL;
2459    RpUnits * femto = NULL;
2460    RpUnits * atto  = NULL;
2461    RpUnits * deca  = NULL;
2462    RpUnits * hecto = NULL;
2463    RpUnits * kilo  = NULL;
2464    RpUnits * mega  = NULL;
2465    RpUnits * giga  = NULL;
2466    RpUnits * tera  = NULL;
2467    RpUnits * peta  = NULL;
2468    RpUnits * exa   = NULL;
2469
2470    deci  = RpUnits::define ( "d",  basis, type);
2471    centi = RpUnits::define ( "c",  basis, type);
2472    milli = RpUnits::define ( "m",  basis, type, !RPUNITS_METRIC,
2473                              !RPUNITS_CASE_INSENSITIVE);
2474    micro = RpUnits::define ( "u",  basis, type);
2475    nano  = RpUnits::define ( "n",  basis, type);
2476    pico  = RpUnits::define ( "p",  basis, type, !RPUNITS_METRIC,
2477                              !RPUNITS_CASE_INSENSITIVE);
2478    femto = RpUnits::define ( "f",  basis, type);
2479    atto  = RpUnits::define ( "a",  basis, type);
2480    deca  = RpUnits::define ( "da", basis, type);
2481    hecto = RpUnits::define ( "h",  basis, type);
2482    kilo  = RpUnits::define ( "k",  basis, type);
2483    mega  = RpUnits::define ( "M",  basis, type, !RPUNITS_METRIC,
2484                              !RPUNITS_CASE_INSENSITIVE);
2485    giga  = RpUnits::define ( "G",  basis, type);
2486    tera  = RpUnits::define ( "T",  basis, type);
2487    peta  = RpUnits::define ( "P",  basis, type, !RPUNITS_METRIC,
2488                              !RPUNITS_CASE_INSENSITIVE);
2489    exa  = RpUnits::define  ( "E",  basis, type);
2490
2491    // the use of the unit as the from and the to unit is a hack
2492    // that can be resolved by creating a RpPrefix object
2493    // the define() function cannot handle NULL as to unit.
2494    RpUnits::define ( deci,  deci , deci2base,  base2deci);
2495    RpUnits::define ( centi, centi, centi2base, base2centi);
2496    RpUnits::define ( milli, milli, milli2base, base2milli);
2497    RpUnits::define ( micro, micro, micro2base, base2micro);
2498    RpUnits::define ( nano,  nano , nano2base,  base2nano);
2499    RpUnits::define ( pico,  pico , pico2base,  base2pico);
2500    RpUnits::define ( femto, femto, femto2base, base2femto);
2501    RpUnits::define ( atto,  atto , atto2base,  base2atto);
2502    RpUnits::define ( deca,  deca , deca2base,  base2deca);
2503    RpUnits::define ( hecto, hecto, hecto2base, base2hecto);
2504    RpUnits::define ( kilo,  kilo , kilo2base,  base2kilo);
2505    RpUnits::define ( mega,  mega , mega2base,  base2mega);
2506    RpUnits::define ( giga,  giga , giga2base,  base2giga);
2507    RpUnits::define ( tera,  tera , tera2base,  base2tera);
2508    RpUnits::define ( peta,  peta , peta2base,  base2peta);
2509    RpUnits::define ( exa,   exa  , exa2base,   base2exa);
2510
2511    return 0;
2512}
2513
2514/**********************************************************************/
2515// METHOD: addPresetTime()
2516/// Add Time related units to the dictionary
2517/**
2518 * Defines the following units:
2519 *   seconds  (s)
2520 *   minutes  (min)
2521 *   hours    (h)
2522 *   days     (d)
2523 *
2524 *   month and year are not included because simple
2525 *   day->month conversions may be misleading
2526 *   month->year conversions may be included in the future
2527 *
2528 * Return codes: 0 success, anything else is error
2529 */
2530
2531int
2532RpUnitsPreset::addPresetTime () {
2533
2534    RpUnits* second    = NULL;
2535    RpUnits* minute    = NULL;
2536    RpUnits* hour      = NULL;
2537    RpUnits* day       = NULL;
2538
2539    second    = RpUnits::define("s", NULL, RP_TYPE_TIME, RPUNITS_METRIC);
2540    minute    = RpUnits::define("min", second, RP_TYPE_TIME);
2541    hour      = RpUnits::define("h", second, RP_TYPE_TIME);
2542    day       = RpUnits::define("d", second, RP_TYPE_TIME);
2543
2544    // add time definitions
2545
2546    RpUnits::define(second, minute, sec2min, min2sec);
2547    RpUnits::define(second, hour, sec2hour, hour2sec);
2548    RpUnits::define(second, day, sec2day, day2sec);
2549
2550    return 0;
2551}
2552
2553/**********************************************************************/
2554// METHOD: addPresetTemp()
2555/// Add Temperature related units to the dictionary
2556/**
2557 * Defines the following units:
2558 *   fahrenheit  (F)
2559 *   celcius     (C)
2560 *   kelvin      (K)
2561 *   rankine     (R)
2562 *
2563 * Return codes: 0 success, anything else is error
2564 */
2565
2566int
2567RpUnitsPreset::addPresetTemp () {
2568
2569    RpUnits* fahrenheit = NULL;
2570    RpUnits* celcius    = NULL;
2571    RpUnits* kelvin     = NULL;
2572    RpUnits* rankine    = NULL;
2573
2574    fahrenheit = RpUnits::define("F", NULL, RP_TYPE_TEMP);
2575    celcius    = RpUnits::define("C", NULL, RP_TYPE_TEMP);
2576    kelvin     = RpUnits::define("K", NULL, RP_TYPE_TEMP);
2577    rankine    = RpUnits::define("R", NULL, RP_TYPE_TEMP);
2578
2579    // add temperature definitions
2580    RpUnits::define(fahrenheit, celcius, fahrenheit2centigrade, centigrade2fahrenheit);
2581    RpUnits::define(celcius, kelvin, centigrade2kelvin, kelvin2centigrade);
2582    RpUnits::define(fahrenheit, kelvin, fahrenheit2kelvin, kelvin2fahrenheit);
2583    RpUnits::define(rankine, kelvin, rankine2kelvin, kelvin2rankine);
2584    RpUnits::define(fahrenheit, rankine, fahrenheit2rankine, rankine2fahrenheit);
2585    RpUnits::define(celcius, rankine, celcius2rankine, rankine2celcius);
2586
2587    return 0;
2588}
2589
2590/**********************************************************************/
2591// METHOD: addPresetLength()
2592/// Add Length related units to the dictionary
2593/**
2594 * Defines the following units:
2595 *   meters         (m)
2596 *   angstrom       (A)
2597 *   inch           (in)
2598 *   feet           (ft)
2599 *   yard           (yd)
2600 *
2601 * Return codes: 0 success, anything else is error
2602 */
2603
2604int
2605RpUnitsPreset::addPresetLength () {
2606
2607    RpUnits* meters     = NULL;
2608    RpUnits* angstrom   = NULL;
2609    RpUnits* inch       = NULL;
2610    RpUnits* feet       = NULL;
2611    RpUnits* yard       = NULL;
2612    RpUnits* mile       = NULL;
2613
2614    meters     = RpUnits::define("m", NULL, RP_TYPE_LENGTH, RPUNITS_METRIC);
2615    angstrom   = RpUnits::define("A", NULL, RP_TYPE_LENGTH);
2616    inch       = RpUnits::define("in", NULL, RP_TYPE_LENGTH);
2617    feet       = RpUnits::define("ft", inch, RP_TYPE_LENGTH);
2618    yard       = RpUnits::define("yd", inch, RP_TYPE_LENGTH);
2619    mile       = RpUnits::define("mi", inch, RP_TYPE_LENGTH);
2620
2621    // RpUnits::makeMetric(meters);
2622
2623    // add length definitions
2624    RpUnits::define(angstrom, meters, angstrom2meter, meter2angstrom);
2625    RpUnits::define(inch, feet, inch2feet, feet2inch);
2626    RpUnits::define(inch, yard, inch2yard, yard2inch);
2627    RpUnits::define(inch, meters, inch2meter, meter2inch);
2628    RpUnits::define(inch, mile, inch2mile, mile2inch);
2629
2630    return 0;
2631}
2632
2633/**********************************************************************/
2634// METHOD: addPresetEnergy()
2635/// Add Energy related units to the dictionary
2636/**
2637 * Defines the following units:
2638 *   electron Volt (eV)
2639 *   joule         (J)
2640 *
2641 * Return codes: 0 success, anything else is error
2642 */
2643
2644int
2645RpUnitsPreset::addPresetEnergy () {
2646
2647    RpUnits* eVolt      = NULL;
2648    RpUnits* joule      = NULL;
2649
2650    eVolt      = RpUnits::define("eV", NULL, RP_TYPE_ENERGY, RPUNITS_METRIC);
2651    joule      = RpUnits::define("J", NULL, RP_TYPE_ENERGY, RPUNITS_METRIC);
2652
2653    // add energy definitions
2654    RpUnits::define(eVolt,joule,electronVolt2joule,joule2electronVolt);
2655
2656    return 0;
2657}
2658
2659/**********************************************************************/
2660// METHOD: addPresetVolume()
2661/// Add Volume related units to the dictionary
2662/**
2663 * Defines the following units:
2664 *   cubic feet (ft3)
2665 *   us gallons (gal)
2666 *   liter      (L)
2667 *
2668 * Return codes: 0 success, anything else is error
2669 */
2670
2671int
2672RpUnitsPreset::addPresetVolume () {
2673
2674    // RpUnits* cubic_meter  = RpUnits::define("m3", NULL, RP_TYPE_VOLUME);
2675    // RpUnits* cubic_feet   = RpUnits::define("ft3", NULL, RP_TYPE_VOLUME);
2676    RpUnits* us_gallon    = NULL;
2677    RpUnits* liter        = NULL;
2678
2679    us_gallon    = RpUnits::define("gal", NULL, RP_TYPE_VOLUME);
2680    liter        = RpUnits::define("L", NULL, RP_TYPE_VOLUME, RPUNITS_METRIC);
2681
2682    /*
2683    // RpUnits::makeMetric(cubic_meter);
2684    const RpUnits* meter = NULL;
2685    const RpUnits* foot = NULL;
2686
2687    meter = RpUnits::find("m");
2688    if (meter && cubic_meter) {
2689        RpUnits::incarnate(meter,cubic_meter);
2690    }
2691    else {
2692        // raise an error, could not find meter unit
2693    }
2694
2695    foot = RpUnits::find("ft");
2696    if (foot && cubic_feet) {
2697        RpUnits::incarnate(foot,cubic_feet);
2698    }
2699    else {
2700        // raise an error, could not find meter unit
2701    }
2702    */
2703
2704    // RpUnits::makeMetric(liter);
2705
2706
2707    // add volume definitions
2708    // RpUnits::define(cubic_meter,cubic_feet,meter2feet,feet2meter);
2709    // RpUnits::define(cubic_meter,us_gallon,cubicMeter2usGallon,usGallon2cubicMeter);
2710    // RpUnits::define(cubic_feet,us_gallon,cubicFeet2usGallon,usGallon2cubicFeet);
2711    // RpUnits::define(cubic_meter,liter,cubicMeter2liter,liter2cubicMeter);
2712    // RpUnits::define(liter,us_gallon,liter2us_gallon,us_gallon2liter);
2713
2714    return 0;
2715}
2716
2717/**********************************************************************/
2718// METHOD: addPresetAngle()
2719/// Add Angle related units to the dictionary
2720/**
2721 * Defines the following units:
2722 *   degrees  (deg)
2723 *   gradians (grad)
2724 *   radians  (rad) (and metric extensions)
2725 *
2726 * Return codes: 0 success, anything else is error
2727 */
2728
2729int
2730RpUnitsPreset::addPresetAngle () {
2731
2732    RpUnits* degree  = NULL;
2733    RpUnits* gradian = NULL;
2734    RpUnits* radian  = NULL;
2735
2736    degree  = RpUnits::define("deg",  NULL, RP_TYPE_ANGLE);
2737    gradian = RpUnits::define("grad", NULL, RP_TYPE_ANGLE);
2738    radian  = RpUnits::define("rad",  NULL, RP_TYPE_ANGLE, RPUNITS_METRIC);
2739
2740    // add angle definitions
2741    RpUnits::define(degree,gradian,deg2grad,grad2deg);
2742    RpUnits::define(radian,degree,rad2deg,deg2rad);
2743    RpUnits::define(radian,gradian,rad2grad,grad2rad);
2744
2745    return 0;
2746}
2747
2748/**********************************************************************/
2749// METHOD: addPresetMass()
2750/// Add Mass related units to the dictionary
2751/**
2752 * Defines the following units:
2753 *   gram  (g)
2754 *
2755 * Return codes: 0 success, anything else is error
2756 */
2757
2758int
2759RpUnitsPreset::addPresetMass () {
2760
2761    RpUnits* gram  = NULL;
2762
2763    gram  = RpUnits::define("g", NULL, RP_TYPE_MASS, RPUNITS_METRIC);
2764
2765    return 0;
2766}
2767
2768/**********************************************************************/
2769// METHOD: addPresetPressure()
2770/// Add pressure related units to the dictionary
2771/**
2772 * http://www.ilpi.com/msds/ref/pressureunits.html
2773 *
2774 * Defines the following units:
2775 *   atmosphere             (atm)
2776 *   bar                    (bar)
2777 *   pascal                 (Pa)
2778 *   pounds/(in^2)          (psi)
2779 *   torr                   (torr)
2780 *   millimeters Mercury    (mmHg)
2781 *
2782 * mmHg was added because as a convenience to those who have not
2783 * yet switched over to the new representation of torr.
2784 *
2785 * Return codes: 0 success, anything else is error
2786 */
2787
2788int
2789RpUnitsPreset::addPresetPressure () {
2790
2791    RpUnits* atmosphere = NULL;
2792    RpUnits* bar        = NULL;
2793    RpUnits* pascal     = NULL;
2794    RpUnits* psi        = NULL;
2795    RpUnits* torr       = NULL;
2796    RpUnits* mmHg       = NULL;
2797
2798    atmosphere  = RpUnits::define("atm", NULL, RP_TYPE_PRESSURE);
2799    bar     = RpUnits::define("bar",  NULL, RP_TYPE_PRESSURE, RPUNITS_METRIC);
2800    pascal  = RpUnits::define("Pa",   NULL, RP_TYPE_PRESSURE, RPUNITS_METRIC);
2801    psi     = RpUnits::define("psi",  NULL, RP_TYPE_PRESSURE);
2802    torr    = RpUnits::define("torr", NULL, RP_TYPE_PRESSURE);
2803    mmHg    = RpUnits::define("mmHg", torr, RP_TYPE_PRESSURE);
2804
2805    RpUnits::define(bar,pascal,bar2Pa,Pa2bar);
2806    RpUnits::define(bar,atmosphere,bar2atm,atm2bar);
2807    RpUnits::define(bar,psi,bar2psi,psi2bar);
2808    RpUnits::define(bar,torr,bar2torr,torr2bar);
2809    RpUnits::define(pascal,atmosphere,Pa2atm,atm2Pa);
2810    RpUnits::define(pascal,torr,Pa2torr,torr2Pa);
2811    RpUnits::define(pascal,psi,Pa2psi,psi2Pa);
2812    RpUnits::define(torr,atmosphere,torr2atm,atm2torr);
2813    RpUnits::define(torr,psi,torr2psi,psi2torr);
2814    RpUnits::define(atmosphere,psi,atm2psi,psi2atm);
2815
2816    RpUnits::define(torr,mmHg,torr2mmHg,mmHg2torr);
2817
2818    return 0;
2819}
2820
2821/**********************************************************************/
2822// METHOD: addPresetConcentration()
2823/// Add concentration related units to the dictionary
2824/**
2825 * http://www.ilpi.com/msds/ref/pressureunits.html
2826 *
2827 * Defines the following units:
2828 *   pH    (pH)
2829 *   pOH    (pOH)
2830 *
2831 * Return codes: 0 success, anything else is error
2832 */
2833
2834int
2835RpUnitsPreset::addPresetConcentration () {
2836
2837    RpUnits* pH  = NULL;
2838    RpUnits* pOH = NULL;
2839
2840    pH  = RpUnits::define("pH",  NULL, RP_TYPE_CONC);
2841    pOH = RpUnits::define("pOH", NULL, RP_TYPE_CONC);
2842
2843    // add concentration definitions
2844    RpUnits::define(pH,pOH,pH2pOH,pOH2pH);
2845
2846    return 0;
2847}
2848
2849/**********************************************************************/
2850// METHOD: addPresetMisc()
2851/// Add Misc related units to the dictionary
2852/**
2853 * Defines the following units:
2854 *   mole  (mol)
2855 *
2856 * Return codes: 0 success, anything else is error
2857 */
2858
2859int
2860RpUnitsPreset::addPresetMisc () {
2861
2862    RpUnits* volt      = NULL;
2863    RpUnits* mole      = NULL;
2864    RpUnits* hertz     = NULL;
2865    RpUnits* becquerel = NULL;
2866
2867    volt      = RpUnits::define("V",  NULL, RP_TYPE_EPOT, RPUNITS_METRIC);
2868    mole      = RpUnits::define("mol",NULL, RP_TYPE_MISC, RPUNITS_METRIC);
2869    hertz     = RpUnits::define("Hz", NULL, RP_TYPE_MISC, RPUNITS_METRIC);
2870    becquerel = RpUnits::define("Bq", NULL, RP_TYPE_MISC, RPUNITS_METRIC);
2871
2872    // RpUnits* percent   = RpUnits::define("%",  NULL, RP_TYPE_MISC);
2873
2874    return 0;
2875}
2876
2877RpUnitsTypes::RpUnitsTypesHint
2878RpUnitsTypes::getTypeHint (std::string type) {
2879
2880    if (type.compare(RP_TYPE_ENERGY) == 0) {
2881        return &RpUnitsTypes::hintTypeEnergy;
2882    }
2883    else if (type.compare(RP_TYPE_EPOT) == 0) {
2884        return &RpUnitsTypes::hintTypeEPot;
2885    }
2886    else if (type.compare(RP_TYPE_LENGTH) == 0) {
2887        return &RpUnitsTypes::hintTypeLength;
2888    }
2889    else if (type.compare(RP_TYPE_TEMP) == 0) {
2890        return &RpUnitsTypes::hintTypeTemp;
2891    }
2892    else if (type.compare(RP_TYPE_TIME) == 0) {
2893        return &RpUnitsTypes::hintTypeTime;
2894    }
2895    else if (type.compare(RP_TYPE_VOLUME) == 0) {
2896        return &RpUnitsTypes::hintTypeVolume;
2897    }
2898    else if (type.compare(RP_TYPE_ANGLE) == 0) {
2899        return &RpUnitsTypes::hintTypeAngle;
2900    }
2901    else if (type.compare(RP_TYPE_MASS) == 0) {
2902        return &RpUnitsTypes::hintTypeMass;
2903    }
2904    else if (type.compare(RP_TYPE_PREFIX) == 0) {
2905        return &RpUnitsTypes::hintTypePrefix;
2906    }
2907    else if (type.compare(RP_TYPE_PRESSURE) == 0) {
2908        return &RpUnitsTypes::hintTypePressure;
2909    }
2910    else if (type.compare(RP_TYPE_CONC) == 0) {
2911        return &RpUnitsTypes::hintTypeConc;
2912    }
2913    else if (type.compare(RP_TYPE_MISC) == 0) {
2914        return &RpUnitsTypes::hintTypeMisc;
2915    }
2916    else {
2917        return NULL;
2918    }
2919};
2920
2921bool
2922RpUnitsTypes::hintTypePrefix   (   RpUnits* unitObj    ) {
2923
2924    bool retVal = false;
2925
2926    if ( (unitObj->getType()).compare(RP_TYPE_PREFIX) == 0 ) {
2927        retVal = true;
2928    }
2929
2930    return retVal;
2931}
2932
2933bool
2934RpUnitsTypes::hintTypeNonPrefix    (   RpUnits* unitObj    ) {
2935
2936    bool retVal = true;
2937
2938    if ( (unitObj->getType()).compare(RP_TYPE_PREFIX) == 0 ) {
2939        retVal = false;
2940    }
2941
2942    return retVal;
2943}
2944
2945bool
2946RpUnitsTypes::hintTypeEnergy   (   RpUnits* unitObj    ) {
2947
2948    bool retVal = false;
2949
2950    if ( (unitObj->getType()).compare(RP_TYPE_ENERGY) == 0 ) {
2951        retVal = true;
2952    }
2953
2954    return retVal;
2955}
2956
2957bool
2958RpUnitsTypes::hintTypeEPot   (   RpUnits* unitObj    ) {
2959
2960    bool retVal = false;
2961
2962    if ( (unitObj->getType()).compare(RP_TYPE_EPOT) == 0 ) {
2963        retVal = true;
2964    }
2965
2966    return retVal;
2967}
2968
2969bool
2970RpUnitsTypes::hintTypeLength   (   RpUnits* unitObj    ) {
2971
2972    bool retVal = false;
2973
2974    if ( (unitObj->getType()).compare(RP_TYPE_LENGTH) == 0 ) {
2975        retVal = true;
2976    }
2977
2978    return retVal;
2979}
2980
2981bool
2982RpUnitsTypes::hintTypeTemp   (   RpUnits* unitObj    ) {
2983
2984    bool retVal = false;
2985
2986    if ( (unitObj->getType()).compare(RP_TYPE_TEMP) == 0 ) {
2987        retVal = true;
2988    }
2989
2990    return retVal;
2991}
2992
2993bool
2994RpUnitsTypes::hintTypeTime   (   RpUnits* unitObj    ) {
2995
2996    bool retVal = false;
2997
2998    if ( (unitObj->getType()).compare(RP_TYPE_TIME) == 0 ) {
2999        retVal = true;
3000    }
3001
3002    return retVal;
3003}
3004
3005bool
3006RpUnitsTypes::hintTypeVolume   (   RpUnits* unitObj    ) {
3007
3008    bool retVal = false;
3009
3010    if ( (unitObj->getType()).compare(RP_TYPE_VOLUME) == 0 ) {
3011        retVal = true;
3012    }
3013
3014    return retVal;
3015}
3016
3017bool
3018RpUnitsTypes::hintTypeAngle   (   RpUnits* unitObj    ) {
3019
3020    bool retVal = false;
3021
3022    if ( (unitObj->getType()).compare(RP_TYPE_ANGLE) == 0 ) {
3023        retVal = true;
3024    }
3025
3026    return retVal;
3027}
3028
3029bool
3030RpUnitsTypes::hintTypeMass   (   RpUnits* unitObj    ) {
3031
3032    bool retVal = false;
3033
3034    if ( (unitObj->getType()).compare(RP_TYPE_MASS) == 0 ) {
3035        retVal = true;
3036    }
3037
3038    return retVal;
3039}
3040
3041bool
3042RpUnitsTypes::hintTypePressure   (   RpUnits* unitObj    ) {
3043
3044    bool retVal = false;
3045
3046    if ( (unitObj->getType()).compare(RP_TYPE_PRESSURE) == 0 ) {
3047        retVal = true;
3048    }
3049
3050    return retVal;
3051}
3052
3053bool
3054RpUnitsTypes::hintTypeConc   (   RpUnits* unitObj    ) {
3055
3056    bool retVal = false;
3057
3058    if ( (unitObj->getType()).compare(RP_TYPE_CONC) == 0 ) {
3059        retVal = true;
3060    }
3061
3062    return retVal;
3063}
3064
3065bool
3066RpUnitsTypes::hintTypeMisc   (   RpUnits* unitObj    ) {
3067
3068    bool retVal = false;
3069
3070    if ( (unitObj->getType()).compare(RP_TYPE_MISC) == 0 ) {
3071        retVal = true;
3072    }
3073
3074    return retVal;
3075}
3076
3077// -------------------------------------------------------------------- //
3078
3079/**********************************************************************/
3080// FUNCTION: list2str()
3081/// Convert a std::list<std::string> into a comma delimited std::string
3082/**
3083 * Iterates through a std::list<std::string> and returns a comma
3084 * delimited std::string containing the elements of the inputted std::list.
3085 *
3086 * Returns 0 on success, anything else is error
3087 */
3088
3089int
3090list2str (std::list<std::string>& inList, std::string& outString)
3091{
3092    int retVal = 1;  // return Value 0 is success, everything else is failure
3093    unsigned int counter = 0; // check if we hit all elements of inList
3094    std::list<std::string>::iterator inListIter; // list interator
3095
3096    inListIter = inList.begin();
3097
3098    while (inListIter != inList.end()) {
3099        if ( outString.empty() ) {
3100            outString = *inListIter;
3101        }
3102        else {
3103            outString =  outString + "," + *inListIter;
3104        }
3105
3106        // increment the iterator and loop counter
3107        inListIter++;
3108        counter++;
3109    }
3110
3111    if (counter == inList.size()) {
3112        retVal = 0;
3113    }
3114
3115    return retVal;
3116}
3117
3118/**********************************************************************/
3119// FUNCTION: unitSlice()
3120/// Convert a std::string into what might be the value and units
3121/**
3122 * Returns 0 on success, anything else is error
3123 */
3124
3125int
3126unitSlice (std::string inStr, std::string& outUnits, double& outVal)
3127{
3128    int retVal      = 0;  // return val 0 is success, otherwise failure
3129    char *endptr    = NULL;
3130
3131    outVal = strtod(inStr.c_str(), &endptr);
3132
3133    if ( (outVal == 0) && (endptr == inStr.c_str()) ) {
3134        // no conversion performed
3135        retVal = 1;
3136    }
3137    else {
3138    }
3139
3140    outUnits = std::string(endptr);
3141
3142    return retVal;
3143}
Note: See TracBrowser for help on using the repository browser.