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 #ifndef __UNITS_CONVERTER_H__ 8 #define __UNITS_CONVERTER_H__ 9 10 #include "cmemory.h" 11 #include "measunit_impl.h" 12 #include "unicode/errorcode.h" 13 #include "unicode/stringpiece.h" 14 #include "unicode/uobject.h" 15 #include "units_converter.h" 16 #include "units_data.h" 17 18 U_NAMESPACE_BEGIN 19 namespace units { 20 21 /* Internal Structure */ 22 23 // Constants corresponding to unitConstants in CLDR's units.xml. 24 enum Constants { 25 CONSTANT_FT2M, // ft_to_m 26 CONSTANT_PI, // PI 27 CONSTANT_GRAVITY, // Gravity of earth (9.80665 m/s^2), "g". 28 CONSTANT_G, // Newtonian constant of gravitation, "G". 29 CONSTANT_GAL_IMP2M3, // Gallon imp to m3 30 CONSTANT_LB2KG, // Pound to Kilogram 31 CONSTANT_GLUCOSE_MOLAR_MASS, 32 CONSTANT_ITEM_PER_MOLE, 33 34 // Must be the last element. 35 CONSTANTS_COUNT 36 }; 37 38 // These values are a hard-coded subset of unitConstants in the units 39 // resources file. A unit test checks that all constants in the resource 40 // file are at least recognised by the code. Derived constants' values or 41 // hard-coded derivations are not checked. 42 // In ICU4J, these constants live in UnitConverter.Factor.getConversionRate(). 43 static const double constantsValues[CONSTANTS_COUNT] = { 44 0.3048, // CONSTANT_FT2M 45 411557987.0 / 131002976.0, // CONSTANT_PI 46 9.80665, // CONSTANT_GRAVITY 47 6.67408E-11, // CONSTANT_G 48 0.00454609, // CONSTANT_GAL_IMP2M3 49 0.45359237, // CONSTANT_LB2KG 50 180.1557, // CONSTANT_GLUCOSE_MOLAR_MASS 51 6.02214076E+23, // CONSTANT_ITEM_PER_MOLE 52 }; 53 54 typedef enum Signum { 55 NEGATIVE = -1, 56 POSITIVE = 1, 57 } Signum; 58 59 /* Represents a conversion factor */ 60 struct U_I18N_API Factor { 61 double factorNum = 1; 62 double factorDen = 1; 63 double offset = 0; 64 bool reciprocal = false; 65 66 // Exponents for the symbolic constants 67 int32_t constantExponents[CONSTANTS_COUNT] = {}; 68 69 void multiplyBy(const Factor &rhs); 70 void divideBy(const Factor &rhs); 71 72 // Apply the power to the factor. 73 void power(int32_t power); 74 75 // Apply SI or binary prefix to the Factor. 76 void applyPrefix(UMeasurePrefix unitPrefix); 77 78 // Does an in-place substition of the "symbolic constants" based on 79 // constantExponents (resetting the exponents). 80 // 81 // In ICU4J, see UnitConverter.Factor.getConversionRate(). 82 void substituteConstants(); 83 }; 84 85 struct U_I18N_API ConversionInfo { 86 double conversionRate; 87 double offset; 88 bool reciprocal; 89 }; 90 91 /* 92 * Adds a single factor element to the `Factor`. e.g "ft3m", "2.333" or "cup2m3". But not "cup2m3^3". 93 */ 94 void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Signum sigNum, 95 Factor &factor, UErrorCode &status); 96 97 /** 98 * Represents the conversion rate between `source` and `target`. 99 */ 100 struct U_I18N_API ConversionRate : public UMemory { 101 const MeasureUnitImpl source; 102 const MeasureUnitImpl target; 103 double factorNum = 1; 104 double factorDen = 1; 105 double sourceOffset = 0; 106 double targetOffset = 0; 107 bool reciprocal = false; 108 ConversionRateConversionRate109 ConversionRate(MeasureUnitImpl &&source, MeasureUnitImpl &&target) 110 : source(std::move(source)), target(std::move(target)) {} 111 }; 112 113 enum Convertibility { 114 RECIPROCAL, 115 CONVERTIBLE, 116 UNCONVERTIBLE, 117 }; 118 119 MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source, 120 const ConversionRates &conversionRates, 121 UErrorCode &status); 122 123 /** 124 * Check if the convertibility between `source` and `target`. 125 * For example: 126 * `meter` and `foot` are `CONVERTIBLE`. 127 * `meter-per-second` and `second-per-meter` are `RECIPROCAL`. 128 * `meter` and `pound` are `UNCONVERTIBLE`. 129 * 130 * NOTE: 131 * Only works with SINGLE and COMPOUND units. If one of the units is a 132 * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. 133 */ 134 Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source, 135 const MeasureUnitImpl &target, 136 const ConversionRates &conversionRates, 137 UErrorCode &status); 138 139 /** 140 * Converts from a source `MeasureUnit` to a target `MeasureUnit`. 141 * 142 * NOTE: 143 * Only works with SINGLE and COMPOUND units. If one of the units is a 144 * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. 145 */ 146 class U_I18N_API UnitsConverter : public UMemory { 147 public: 148 /** 149 * Constructor of `UnitConverter`. 150 * NOTE: 151 * - source and target must be under the same category 152 * - e.g. meter to mile --> both of them are length units. 153 * NOTE: 154 * This constructor creates an instance of `ConversionRates` internally. 155 * 156 * @param sourceIdentifier represents the source unit identifier. 157 * @param targetIdentifier represents the target unit identifier. 158 * @param status 159 */ 160 UnitsConverter(StringPiece sourceIdentifier, StringPiece targetIdentifier, UErrorCode &status); 161 162 /** 163 * Constructor of `UnitConverter`. 164 * NOTE: 165 * - source and target must be under the same category 166 * - e.g. meter to mile --> both of them are length units. 167 * 168 * @param source represents the source unit. 169 * @param target represents the target unit. 170 * @param ratesInfo Contains all the needed conversion rates. 171 * @param status 172 */ 173 UnitsConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target, 174 const ConversionRates &ratesInfo, UErrorCode &status); 175 176 /** 177 * Compares two single units and returns 1 if the first one is greater, -1 if the second 178 * one is greater and 0 if they are equal. 179 * 180 * NOTE: 181 * Compares only single units that are convertible. 182 */ 183 static int32_t compareTwoUnits(const MeasureUnitImpl &firstUnit, const MeasureUnitImpl &SecondUnit, 184 const ConversionRates &ratesInfo, UErrorCode &status); 185 186 /** 187 * Convert a measurement expressed in the source unit to a measurement 188 * expressed in the target unit. 189 * 190 * @param inputValue the value to be converted. 191 * @return the converted value. 192 */ 193 double convert(double inputValue) const; 194 195 /** 196 * The inverse of convert(): convert a measurement expressed in the target 197 * unit to a measurement expressed in the source unit. 198 * 199 * @param inputValue the value to be converted. 200 * @return the converted value. 201 */ 202 double convertInverse(double inputValue) const; 203 204 ConversionInfo getConversionInfo() const; 205 206 private: 207 ConversionRate conversionRate_; 208 209 /** 210 * Initialises the object. 211 */ 212 void init(const ConversionRates &ratesInfo, UErrorCode &status); 213 }; 214 215 } // namespace units 216 U_NAMESPACE_END 217 218 #endif //__UNITS_CONVERTER_H__ 219 220 #endif /* #if !UCONFIG_NO_FORMATTING */ 221