• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // © 2020 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 
4 #include "unicode/utypes.h"
5 
6 #if !UCONFIG_NO_FORMATTING
7 
8 #include "charstr.h"
9 #include "cmemory.h"
10 #include "double-conversion-string-to-double.h"
11 #include "measunit_impl.h"
12 #include "putilimp.h"
13 #include "uassert.h"
14 #include "unicode/errorcode.h"
15 #include "unicode/localpointer.h"
16 #include "unicode/stringpiece.h"
17 #include "units_converter.h"
18 #include <algorithm>
19 #include <cmath>
20 #include <stdlib.h>
21 #include <utility>
22 
23 U_NAMESPACE_BEGIN
24 namespace units {
25 
multiplyBy(const Factor & rhs)26 void U_I18N_API Factor::multiplyBy(const Factor &rhs) {
27     factorNum *= rhs.factorNum;
28     factorDen *= rhs.factorDen;
29     for (int i = 0; i < CONSTANTS_COUNT; i++) {
30         constantExponents[i] += rhs.constantExponents[i];
31     }
32 
33     // NOTE
34     //  We need the offset when the source and the target are simple units. e.g. the source is
35     //  celsius and the target is Fahrenheit. Therefore, we just keep the value using `std::max`.
36     offset = std::max(rhs.offset, offset);
37 }
38 
divideBy(const Factor & rhs)39 void U_I18N_API Factor::divideBy(const Factor &rhs) {
40     factorNum *= rhs.factorDen;
41     factorDen *= rhs.factorNum;
42     for (int i = 0; i < CONSTANTS_COUNT; i++) {
43         constantExponents[i] -= rhs.constantExponents[i];
44     }
45 
46     // NOTE
47     //  We need the offset when the source and the target are simple units. e.g. the source is
48     //  celsius and the target is Fahrenheit. Therefore, we just keep the value using `std::max`.
49     offset = std::max(rhs.offset, offset);
50 }
51 
power(int32_t power)52 void U_I18N_API Factor::power(int32_t power) {
53     // multiply all the constant by the power.
54     for (int i = 0; i < CONSTANTS_COUNT; i++) {
55         constantExponents[i] *= power;
56     }
57 
58     bool shouldFlip = power < 0; // This means that after applying the absolute power, we should flip
59                                  // the Numerator and Denominator.
60 
61     factorNum = std::pow(factorNum, std::abs(power));
62     factorDen = std::pow(factorDen, std::abs(power));
63 
64     if (shouldFlip) {
65         // Flip Numerator and Denominator.
66         std::swap(factorNum, factorDen);
67     }
68 }
69 
applyPrefix(UMeasurePrefix unitPrefix)70 void U_I18N_API Factor::applyPrefix(UMeasurePrefix unitPrefix) {
71     if (unitPrefix == UMeasurePrefix::UMEASURE_PREFIX_ONE) {
72         // No need to do anything
73         return;
74     }
75 
76     int32_t prefixPower = umeas_getPrefixPower(unitPrefix);
77     double prefixFactor = std::pow((double)umeas_getPrefixBase(unitPrefix), (double)std::abs(prefixPower));
78     if (prefixPower >= 0) {
79         factorNum *= prefixFactor;
80     } else {
81         factorDen *= prefixFactor;
82     }
83 }
84 
substituteConstants()85 void U_I18N_API Factor::substituteConstants() {
86     for (int i = 0; i < CONSTANTS_COUNT; i++) {
87         if (this->constantExponents[i] == 0) {
88             continue;
89         }
90 
91         auto absPower = std::abs(this->constantExponents[i]);
92         Signum powerSig = this->constantExponents[i] < 0 ? Signum::NEGATIVE : Signum::POSITIVE;
93         double absConstantValue = std::pow(constantsValues[i], absPower);
94 
95         if (powerSig == Signum::NEGATIVE) {
96             this->factorDen *= absConstantValue;
97         } else {
98             this->factorNum *= absConstantValue;
99         }
100 
101         this->constantExponents[i] = 0;
102     }
103 }
104 
105 namespace {
106 
107 /* Helpers */
108 
109 using icu::double_conversion::StringToDoubleConverter;
110 
111 // TODO: Make this a shared-utility function.
112 // Returns `double` from a scientific number(i.e. "1", "2.01" or "3.09E+4")
strToDouble(StringPiece strNum,UErrorCode & status)113 double strToDouble(StringPiece strNum, UErrorCode &status) {
114     // We are processing well-formed input, so we don't need any special options to
115     // StringToDoubleConverter.
116     StringToDoubleConverter converter(0, 0, 0, "", "");
117     int32_t count;
118     double result = converter.StringToDouble(strNum.data(), strNum.length(), &count);
119     if (count != strNum.length()) {
120         status = U_INVALID_FORMAT_ERROR;
121     }
122 
123     return result;
124 }
125 
126 // Returns `double` from a scientific number that could has a division sign (i.e. "1", "2.01", "3.09E+4"
127 // or "2E+2/3")
strHasDivideSignToDouble(StringPiece strWithDivide,UErrorCode & status)128 double strHasDivideSignToDouble(StringPiece strWithDivide, UErrorCode &status) {
129     int divisionSignInd = -1;
130     for (int i = 0, n = strWithDivide.length(); i < n; ++i) {
131         if (strWithDivide.data()[i] == '/') {
132             divisionSignInd = i;
133             break;
134         }
135     }
136 
137     if (divisionSignInd >= 0) {
138         return strToDouble(strWithDivide.substr(0, divisionSignInd), status) /
139                strToDouble(strWithDivide.substr(divisionSignInd + 1), status);
140     }
141 
142     return strToDouble(strWithDivide, status);
143 }
144 
145 /*
146   Adds single factor to a `Factor` object. Single factor means "23^2", "23.3333", "ft2m^3" ...etc.
147   However, complex factor are not included, such as "ft2m^3*200/3"
148 */
addFactorElement(Factor & factor,StringPiece elementStr,Signum signum,UErrorCode & status)149 void addFactorElement(Factor &factor, StringPiece elementStr, Signum signum, UErrorCode &status) {
150     StringPiece baseStr;
151     StringPiece powerStr;
152     int32_t power =
153         1; // In case the power is not written, then, the power is equal 1 ==> `ft2m^1` == `ft2m`
154 
155     // Search for the power part
156     int32_t powerInd = -1;
157     for (int32_t i = 0, n = elementStr.length(); i < n; ++i) {
158         if (elementStr.data()[i] == '^') {
159             powerInd = i;
160             break;
161         }
162     }
163 
164     if (powerInd > -1) {
165         // There is power
166         baseStr = elementStr.substr(0, powerInd);
167         powerStr = elementStr.substr(powerInd + 1);
168 
169         power = static_cast<int32_t>(strToDouble(powerStr, status));
170     } else {
171         baseStr = elementStr;
172     }
173 
174     addSingleFactorConstant(baseStr, power, signum, factor, status);
175 }
176 
177 /*
178  * Extracts `Factor` from a complete string factor. e.g. "ft2m^3*1007/cup2m3*3"
179  */
extractFactorConversions(StringPiece stringFactor,UErrorCode & status)180 Factor extractFactorConversions(StringPiece stringFactor, UErrorCode &status) {
181     Factor result;
182     Signum signum = Signum::POSITIVE;
183     auto factorData = stringFactor.data();
184     for (int32_t i = 0, start = 0, n = stringFactor.length(); i < n; i++) {
185         if (factorData[i] == '*' || factorData[i] == '/') {
186             StringPiece factorElement = stringFactor.substr(start, i - start);
187             addFactorElement(result, factorElement, signum, status);
188 
189             start = i + 1; // Set `start` to point to the start of the new element.
190         } else if (i == n - 1) {
191             // Last element
192             addFactorElement(result, stringFactor.substr(start, i + 1), signum, status);
193         }
194 
195         if (factorData[i] == '/') {
196             signum = Signum::NEGATIVE; // Change the signum because we reached the Denominator.
197         }
198     }
199 
200     return result;
201 }
202 
203 // Load factor for a single source
loadSingleFactor(StringPiece source,const ConversionRates & ratesInfo,UErrorCode & status)204 Factor loadSingleFactor(StringPiece source, const ConversionRates &ratesInfo, UErrorCode &status) {
205     const auto conversionUnit = ratesInfo.extractConversionInfo(source, status);
206     if (U_FAILURE(status)) return Factor();
207     if (conversionUnit == nullptr) {
208         status = U_INTERNAL_PROGRAM_ERROR;
209         return Factor();
210     }
211 
212     Factor result = extractFactorConversions(conversionUnit->factor.toStringPiece(), status);
213     result.offset = strHasDivideSignToDouble(conversionUnit->offset.toStringPiece(), status);
214 
215     return result;
216 }
217 
218 // Load Factor of a compound source unit.
219 // In ICU4J, this is a pair of ConversionRates.getFactorToBase() functions.
loadCompoundFactor(const MeasureUnitImpl & source,const ConversionRates & ratesInfo,UErrorCode & status)220 Factor loadCompoundFactor(const MeasureUnitImpl &source, const ConversionRates &ratesInfo,
221                           UErrorCode &status) {
222 
223     Factor result;
224     for (int32_t i = 0, n = source.singleUnits.length(); i < n; i++) {
225         SingleUnitImpl singleUnit = *source.singleUnits[i];
226 
227         Factor singleFactor = loadSingleFactor(singleUnit.getSimpleUnitID(), ratesInfo, status);
228         if (U_FAILURE(status)) return result;
229 
230         // Prefix before power, because:
231         // - square-kilometer to square-meter: (1000)^2
232         // - square-kilometer to square-foot (approximate): (3.28*1000)^2
233         singleFactor.applyPrefix(singleUnit.unitPrefix);
234 
235         // Apply the power of the `dimensionality`
236         singleFactor.power(singleUnit.dimensionality);
237 
238         result.multiplyBy(singleFactor);
239     }
240 
241     return result;
242 }
243 
244 /**
245  * Checks if the source unit and the target unit are simple. For example celsius or fahrenheit. But not
246  * square-celsius or square-fahrenheit.
247  *
248  * NOTE:
249  *  Empty unit means simple unit.
250  *
251  * In ICU4J, this is ConversionRates.checkSimpleUnit().
252  */
checkSimpleUnit(const MeasureUnitImpl & unit,UErrorCode & status)253 UBool checkSimpleUnit(const MeasureUnitImpl &unit, UErrorCode &status) {
254     if (U_FAILURE(status)) return false;
255 
256     if (unit.complexity != UMEASURE_UNIT_SINGLE) {
257         return false;
258     }
259     if (unit.singleUnits.length() == 0) {
260         // Empty units means simple unit.
261         return true;
262     }
263 
264     auto singleUnit = *(unit.singleUnits[0]);
265 
266     if (singleUnit.dimensionality != 1 || singleUnit.unitPrefix != UMEASURE_PREFIX_ONE) {
267         return false;
268     }
269 
270     return true;
271 }
272 
273 /**
274  *  Extract conversion rate from `source` to `target`
275  */
276 // In ICU4J, this function is partially inlined in the UnitsConverter constructor.
loadConversionRate(ConversionRate & conversionRate,const MeasureUnitImpl & source,const MeasureUnitImpl & target,Convertibility unitsState,const ConversionRates & ratesInfo,UErrorCode & status)277 void loadConversionRate(ConversionRate &conversionRate, const MeasureUnitImpl &source,
278                         const MeasureUnitImpl &target, Convertibility unitsState,
279                         const ConversionRates &ratesInfo, UErrorCode &status) {
280     // Represents the conversion factor from the source to the target.
281     Factor finalFactor;
282 
283     // Represents the conversion factor from the source to the base unit that specified in the conversion
284     // data which is considered as the root of the source and the target.
285     Factor sourceToBase = loadCompoundFactor(source, ratesInfo, status);
286     Factor targetToBase = loadCompoundFactor(target, ratesInfo, status);
287 
288     // Merger Factors
289     finalFactor.multiplyBy(sourceToBase);
290     if (unitsState == Convertibility::CONVERTIBLE) {
291         finalFactor.divideBy(targetToBase);
292     } else if (unitsState == Convertibility::RECIPROCAL) {
293         finalFactor.multiplyBy(targetToBase);
294     } else {
295         status = UErrorCode::U_ARGUMENT_TYPE_MISMATCH;
296         return;
297     }
298 
299     finalFactor.substituteConstants();
300 
301     conversionRate.factorNum = finalFactor.factorNum;
302     conversionRate.factorDen = finalFactor.factorDen;
303 
304     // This code corresponds to ICU4J's ConversionRates.getOffset().
305     // In case of simple units (such as: celsius or fahrenheit), offsets are considered.
306     if (checkSimpleUnit(source, status) && checkSimpleUnit(target, status)) {
307         conversionRate.sourceOffset =
308             sourceToBase.offset * sourceToBase.factorDen / sourceToBase.factorNum;
309         conversionRate.targetOffset =
310             targetToBase.offset * targetToBase.factorDen / targetToBase.factorNum;
311     }
312     // TODO(icu-units#127): should we consider failure if there's an offset for
313     // a not-simple-unit? What about kilokelvin / kilocelsius?
314 
315     conversionRate.reciprocal = unitsState == Convertibility::RECIPROCAL;
316 }
317 
318 struct UnitIndexAndDimension : UMemory {
319     int32_t index = 0;
320     int32_t dimensionality = 0;
321 
UnitIndexAndDimensionunits::__anon1d97b1570111::UnitIndexAndDimension322     UnitIndexAndDimension(const SingleUnitImpl &singleUnit, int32_t multiplier) {
323         index = singleUnit.index;
324         dimensionality = singleUnit.dimensionality * multiplier;
325     }
326 };
327 
mergeSingleUnitWithDimension(MaybeStackVector<UnitIndexAndDimension> & unitIndicesWithDimension,const SingleUnitImpl & shouldBeMerged,int32_t multiplier)328 void mergeSingleUnitWithDimension(MaybeStackVector<UnitIndexAndDimension> &unitIndicesWithDimension,
329                                   const SingleUnitImpl &shouldBeMerged, int32_t multiplier) {
330     for (int32_t i = 0; i < unitIndicesWithDimension.length(); i++) {
331         auto &unitWithIndex = *unitIndicesWithDimension[i];
332         if (unitWithIndex.index == shouldBeMerged.index) {
333             unitWithIndex.dimensionality += shouldBeMerged.dimensionality * multiplier;
334             return;
335         }
336     }
337 
338     unitIndicesWithDimension.emplaceBack(shouldBeMerged, multiplier);
339 }
340 
mergeUnitsAndDimensions(MaybeStackVector<UnitIndexAndDimension> & unitIndicesWithDimension,const MeasureUnitImpl & shouldBeMerged,int32_t multiplier)341 void mergeUnitsAndDimensions(MaybeStackVector<UnitIndexAndDimension> &unitIndicesWithDimension,
342                              const MeasureUnitImpl &shouldBeMerged, int32_t multiplier) {
343     for (int32_t unit_i = 0; unit_i < shouldBeMerged.singleUnits.length(); unit_i++) {
344         auto singleUnit = *shouldBeMerged.singleUnits[unit_i];
345         mergeSingleUnitWithDimension(unitIndicesWithDimension, singleUnit, multiplier);
346     }
347 }
348 
checkAllDimensionsAreZeros(const MaybeStackVector<UnitIndexAndDimension> & dimensionVector)349 UBool checkAllDimensionsAreZeros(const MaybeStackVector<UnitIndexAndDimension> &dimensionVector) {
350     for (int32_t i = 0; i < dimensionVector.length(); i++) {
351         if (dimensionVector[i]->dimensionality != 0) {
352             return false;
353         }
354     }
355 
356     return true;
357 }
358 
359 } // namespace
360 
361 // Conceptually, this modifies factor: factor *= baseStr^(signum*power).
362 //
363 // baseStr must be a known constant or a value that strToDouble() is able to
364 // parse.
addSingleFactorConstant(StringPiece baseStr,int32_t power,Signum signum,Factor & factor,UErrorCode & status)365 void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Signum signum,
366                                         Factor &factor, UErrorCode &status) {
367     if (baseStr == "ft_to_m") {
368         factor.constantExponents[CONSTANT_FT2M] += power * signum;
369     } else if (baseStr == "ft2_to_m2") {
370         factor.constantExponents[CONSTANT_FT2M] += 2 * power * signum;
371     } else if (baseStr == "ft3_to_m3") {
372         factor.constantExponents[CONSTANT_FT2M] += 3 * power * signum;
373     } else if (baseStr == "in3_to_m3") {
374         factor.constantExponents[CONSTANT_FT2M] += 3 * power * signum;
375         factor.factorDen *= 12 * 12 * 12;
376     } else if (baseStr == "gal_to_m3") {
377         factor.factorNum *= 231;
378         factor.constantExponents[CONSTANT_FT2M] += 3 * power * signum;
379         factor.factorDen *= 12 * 12 * 12;
380     } else if (baseStr == "gal_imp_to_m3") {
381         factor.constantExponents[CONSTANT_GAL_IMP2M3] += power * signum;
382     } else if (baseStr == "G") {
383         factor.constantExponents[CONSTANT_G] += power * signum;
384     } else if (baseStr == "gravity") {
385         factor.constantExponents[CONSTANT_GRAVITY] += power * signum;
386     } else if (baseStr == "lb_to_kg") {
387         factor.constantExponents[CONSTANT_LB2KG] += power * signum;
388     } else if (baseStr == "glucose_molar_mass") {
389         factor.constantExponents[CONSTANT_GLUCOSE_MOLAR_MASS] += power * signum;
390     } else if (baseStr == "item_per_mole") {
391         factor.constantExponents[CONSTANT_ITEM_PER_MOLE] += power * signum;
392     } else if (baseStr == "meters_per_AU") {
393         factor.constantExponents[CONSTANT_METERS_PER_AU] += power * signum;
394     } else if (baseStr == "PI") {
395         factor.constantExponents[CONSTANT_PI] += power * signum;
396     } else if (baseStr == "sec_per_julian_year") {
397         factor.constantExponents[CONSTANT_SEC_PER_JULIAN_YEAR] += power * signum;
398     } else if (baseStr == "speed_of_light_meters_per_second") {
399         factor.constantExponents[CONSTANT_SPEED_OF_LIGHT_METERS_PER_SECOND] += power * signum;
400     } else {
401         if (signum == Signum::NEGATIVE) {
402             factor.factorDen *= std::pow(strToDouble(baseStr, status), power);
403         } else {
404             factor.factorNum *= std::pow(strToDouble(baseStr, status), power);
405         }
406     }
407 }
408 
409 /**
410  * Extracts the compound base unit of a compound unit (`source`). For example, if the source unit is
411  * `square-mile-per-hour`, the compound base unit will be `square-meter-per-second`
412  */
extractCompoundBaseUnit(const MeasureUnitImpl & source,const ConversionRates & conversionRates,UErrorCode & status)413 MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source,
414                                                    const ConversionRates &conversionRates,
415                                                    UErrorCode &status) {
416 
417     MeasureUnitImpl result;
418     if (U_FAILURE(status)) return result;
419 
420     const auto &singleUnits = source.singleUnits;
421     for (int i = 0, count = singleUnits.length(); i < count; ++i) {
422         const auto &singleUnit = *singleUnits[i];
423         // Extract `ConversionRateInfo` using the absolute unit. For example: in case of `square-meter`,
424         // we will use `meter`
425         const auto rateInfo =
426             conversionRates.extractConversionInfo(singleUnit.getSimpleUnitID(), status);
427         if (U_FAILURE(status)) {
428             return result;
429         }
430         if (rateInfo == nullptr) {
431             status = U_INTERNAL_PROGRAM_ERROR;
432             return result;
433         }
434 
435         // Multiply the power of the singleUnit by the power of the baseUnit. For example, square-hectare
436         // must be pow4-meter. (NOTE: hectare --> square-meter)
437         auto baseUnits =
438             MeasureUnitImpl::forIdentifier(rateInfo->baseUnit.toStringPiece(), status).singleUnits;
439         for (int32_t i = 0, baseUnitsCount = baseUnits.length(); i < baseUnitsCount; i++) {
440             baseUnits[i]->dimensionality *= singleUnit.dimensionality;
441             // TODO: Deal with SI-prefix
442             result.appendSingleUnit(*baseUnits[i], status);
443 
444             if (U_FAILURE(status)) {
445                 return result;
446             }
447         }
448     }
449 
450     return result;
451 }
452 
453 /**
454  * Determine the convertibility between `source` and `target`.
455  * For example:
456  *    `meter` and `foot` are `CONVERTIBLE`.
457  *    `meter-per-second` and `second-per-meter` are `RECIPROCAL`.
458  *    `meter` and `pound` are `UNCONVERTIBLE`.
459  *
460  * NOTE:
461  *    Only works with SINGLE and COMPOUND units. If one of the units is a
462  *    MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity.
463  */
extractConvertibility(const MeasureUnitImpl & source,const MeasureUnitImpl & target,const ConversionRates & conversionRates,UErrorCode & status)464 Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source,
465                                                 const MeasureUnitImpl &target,
466                                                 const ConversionRates &conversionRates,
467                                                 UErrorCode &status) {
468 
469     if (source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
470         target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
471         status = U_ARGUMENT_TYPE_MISMATCH;
472         return UNCONVERTIBLE;
473     }
474 
475     MeasureUnitImpl sourceBaseUnit = extractCompoundBaseUnit(source, conversionRates, status);
476     MeasureUnitImpl targetBaseUnit = extractCompoundBaseUnit(target, conversionRates, status);
477     if (U_FAILURE(status)) return UNCONVERTIBLE;
478 
479     MaybeStackVector<UnitIndexAndDimension> convertible;
480     MaybeStackVector<UnitIndexAndDimension> reciprocal;
481 
482     mergeUnitsAndDimensions(convertible, sourceBaseUnit, 1);
483     mergeUnitsAndDimensions(reciprocal, sourceBaseUnit, 1);
484 
485     mergeUnitsAndDimensions(convertible, targetBaseUnit, -1);
486     mergeUnitsAndDimensions(reciprocal, targetBaseUnit, 1);
487 
488     if (checkAllDimensionsAreZeros(convertible)) {
489         return CONVERTIBLE;
490     }
491 
492     if (checkAllDimensionsAreZeros(reciprocal)) {
493         return RECIPROCAL;
494     }
495 
496     return UNCONVERTIBLE;
497 }
498 
UnitsConverter(const MeasureUnitImpl & source,const MeasureUnitImpl & target,const ConversionRates & ratesInfo,UErrorCode & status)499 UnitsConverter::UnitsConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target,
500                                const ConversionRates &ratesInfo, UErrorCode &status)
501     : conversionRate_(source.copy(status), target.copy(status)) {
502     this->init(ratesInfo, status);
503 }
504 
UnitsConverter(StringPiece sourceIdentifier,StringPiece targetIdentifier,UErrorCode & status)505 UnitsConverter::UnitsConverter(StringPiece sourceIdentifier, StringPiece targetIdentifier,
506                                UErrorCode &status)
507     : conversionRate_(MeasureUnitImpl::forIdentifier(sourceIdentifier, status),
508                       MeasureUnitImpl::forIdentifier(targetIdentifier, status)) {
509     if (U_FAILURE(status)) {
510         return;
511     }
512 
513     ConversionRates ratesInfo(status);
514     this->init(ratesInfo, status);
515 }
516 
init(const ConversionRates & ratesInfo,UErrorCode & status)517 void UnitsConverter::init(const ConversionRates &ratesInfo, UErrorCode &status) {
518     if (U_FAILURE(status)) {
519         return;
520     }
521 
522     if (this->conversionRate_.source.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
523         this->conversionRate_.target.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
524         status = U_ARGUMENT_TYPE_MISMATCH;
525         return;
526     }
527 
528     Convertibility unitsState = extractConvertibility(this->conversionRate_.source,
529                                                       this->conversionRate_.target, ratesInfo, status);
530     if (U_FAILURE(status)) return;
531     if (unitsState == Convertibility::UNCONVERTIBLE) {
532         status = U_ARGUMENT_TYPE_MISMATCH;
533         return;
534     }
535 
536     loadConversionRate(conversionRate_, conversionRate_.source, conversionRate_.target, unitsState,
537                        ratesInfo, status);
538 
539 }
540 
compareTwoUnits(const MeasureUnitImpl & firstUnit,const MeasureUnitImpl & secondUnit,const ConversionRates & ratesInfo,UErrorCode & status)541 int32_t UnitsConverter::compareTwoUnits(const MeasureUnitImpl &firstUnit,
542                                         const MeasureUnitImpl &secondUnit,
543                                         const ConversionRates &ratesInfo, UErrorCode &status) {
544     if (U_FAILURE(status)) {
545         return 0;
546     }
547 
548     if (firstUnit.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
549         secondUnit.complexity == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
550         status = U_ARGUMENT_TYPE_MISMATCH;
551         return 0;
552     }
553 
554     Convertibility unitsState = extractConvertibility(firstUnit, secondUnit, ratesInfo, status);
555     if (U_FAILURE(status)) {
556         return 0;
557     }
558 
559     if (unitsState == Convertibility::UNCONVERTIBLE || unitsState == Convertibility::RECIPROCAL) {
560         status = U_ARGUMENT_TYPE_MISMATCH;
561         return 0;
562     }
563 
564     // Represents the conversion factor from the firstUnit to the base
565     // unit that specified in the conversion data which is considered as
566     // the root of the firstUnit and the secondUnit.
567     Factor firstUnitToBase = loadCompoundFactor(firstUnit, ratesInfo, status);
568     Factor secondUnitToBase = loadCompoundFactor(secondUnit, ratesInfo, status);
569 
570     firstUnitToBase.substituteConstants();
571     secondUnitToBase.substituteConstants();
572 
573     double firstUnitToBaseConversionRate = firstUnitToBase.factorNum / firstUnitToBase.factorDen;
574     double secondUnitToBaseConversionRate = secondUnitToBase.factorNum / secondUnitToBase.factorDen;
575 
576     double diff = firstUnitToBaseConversionRate - secondUnitToBaseConversionRate;
577     if (diff > 0) {
578         return 1;
579     }
580 
581     if (diff < 0) {
582         return -1;
583     }
584 
585     return 0;
586 }
587 
convert(double inputValue) const588 double UnitsConverter::convert(double inputValue) const {
589     double result =
590         inputValue + conversionRate_.sourceOffset; // Reset the input to the target zero index.
591     // Convert the quantity to from the source scale to the target scale.
592     result *= conversionRate_.factorNum / conversionRate_.factorDen;
593 
594     result -= conversionRate_.targetOffset; // Set the result to its index.
595 
596     if (conversionRate_.reciprocal) {
597         if (result == 0) {
598             return uprv_getInfinity();
599         }
600         result = 1.0 / result;
601     }
602 
603     return result;
604 }
605 
convertInverse(double inputValue) const606 double UnitsConverter::convertInverse(double inputValue) const {
607     double result = inputValue;
608     if (conversionRate_.reciprocal) {
609         if (result == 0) {
610             return uprv_getInfinity();
611         }
612         result = 1.0 / result;
613     }
614     result += conversionRate_.targetOffset;
615     result *= conversionRate_.factorDen / conversionRate_.factorNum;
616     result -= conversionRate_.sourceOffset;
617     return result;
618 }
619 
getConversionInfo() const620 ConversionInfo UnitsConverter::getConversionInfo() const {
621     ConversionInfo result;
622     result.conversionRate = conversionRate_.factorNum / conversionRate_.factorDen;
623     result.offset =
624         (conversionRate_.sourceOffset * (conversionRate_.factorNum / conversionRate_.factorDen)) -
625         conversionRate_.targetOffset;
626     result.reciprocal = conversionRate_.reciprocal;
627 
628     return result;
629 }
630 
631 } // namespace units
632 U_NAMESPACE_END
633 
634 #endif /* #if !UCONFIG_NO_FORMATTING */
635