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

Last change on this file since 593 was 593, checked in by dkearney, 17 years ago

added miles as units understood by rappture

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