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 CONSTANT_METERS_PER_AU, 34 CONSTANT_SEC_PER_JULIAN_YEAR, 35 CONSTANT_SPEED_OF_LIGHT_METERS_PER_SECOND, 36 CONSTANT_SHO_TO_M3, // https://en.wikipedia.org/wiki/Japanese_units_of_measurement 37 CONSTANT_TSUBO_TO_M2, // https://en.wikipedia.org/wiki/Japanese_units_of_measurement 38 CONSTANT_SHAKU_TO_M, // https://en.wikipedia.org/wiki/Japanese_units_of_measurement 39 CONSTANT_AMU, // Atomic Mass Unit https://www.nist.gov/pml/special-publication-811/nist-guide-si-chapter-5-units-outside-si#table7 40 41 // Must be the last element. 42 CONSTANTS_COUNT 43 }; 44 45 // These values are a hard-coded subset of unitConstants in the units 46 // resources file. A unit test checks that all constants in the resource 47 // file are at least recognised by the code. Derived constants' values or 48 // hard-coded derivations are not checked. 49 // In ICU4J, these constants live in UnitConverter.Factor.getConversionRate(). 50 static const double constantsValues[CONSTANTS_COUNT] = { 51 0.3048, // CONSTANT_FT2M 52 411557987.0 / 131002976.0, // CONSTANT_PI 53 9.80665, // CONSTANT_GRAVITY 54 6.67408E-11, // CONSTANT_G 55 0.00454609, // CONSTANT_GAL_IMP2M3 56 0.45359237, // CONSTANT_LB2KG 57 180.1557, // CONSTANT_GLUCOSE_MOLAR_MASS 58 6.02214076E+23, // CONSTANT_ITEM_PER_MOLE 59 149597870700, // CONSTANT_METERS_PER_AU 60 31557600, // CONSTANT_SEC_PER_JULIAN_YEAR 61 299792458, // CONSTANT_SPEED_OF_LIGHT_METERS_PER_SECOND 62 2401.0 / (1331.0 * 1000.0), 63 400.0 / 121.0, 64 4.0 / 121.0, 65 1.66053878283E-27, // CONSTANT_AMU 66 }; 67 68 typedef enum Signum { 69 NEGATIVE = -1, 70 POSITIVE = 1, 71 } Signum; 72 73 /* Represents a conversion factor */ 74 struct U_I18N_API Factor { 75 double factorNum = 1; 76 double factorDen = 1; 77 double offset = 0; 78 bool reciprocal = false; 79 80 // Exponents for the symbolic constants 81 int32_t constantExponents[CONSTANTS_COUNT] = {}; 82 83 void multiplyBy(const Factor &rhs); 84 void divideBy(const Factor &rhs); 85 86 // Apply the power to the factor. 87 void power(int32_t power); 88 89 // Apply SI or binary prefix to the Factor. 90 void applyPrefix(UMeasurePrefix unitPrefix); 91 92 // Does an in-place substitution of the "symbolic constants" based on 93 // constantExponents (resetting the exponents). 94 // 95 // In ICU4J, see UnitConverter.Factor.getConversionRate(). 96 void substituteConstants(); 97 }; 98 99 struct U_I18N_API ConversionInfo { 100 double conversionRate; 101 double offset; 102 bool reciprocal; 103 }; 104 105 /* 106 * Adds a single factor element to the `Factor`. e.g "ft3m", "2.333" or "cup2m3". But not "cup2m3^3". 107 */ 108 void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Signum sigNum, 109 Factor &factor, UErrorCode &status); 110 111 /** 112 * Represents the conversion rate between `source` and `target`. 113 */ 114 struct U_I18N_API ConversionRate : public UMemory { 115 const MeasureUnitImpl source; 116 const MeasureUnitImpl target; 117 double factorNum = 1; 118 double factorDen = 1; 119 double sourceOffset = 0; 120 double targetOffset = 0; 121 bool reciprocal = false; 122 ConversionRateConversionRate123 ConversionRate(MeasureUnitImpl &&source, MeasureUnitImpl &&target) 124 : source(std::move(source)), target(std::move(target)) {} 125 }; 126 127 enum Convertibility { 128 RECIPROCAL, 129 CONVERTIBLE, 130 UNCONVERTIBLE, 131 }; 132 133 MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source, 134 const ConversionRates &conversionRates, 135 UErrorCode &status); 136 137 /** 138 * Check if the convertibility between `source` and `target`. 139 * For example: 140 * `meter` and `foot` are `CONVERTIBLE`. 141 * `meter-per-second` and `second-per-meter` are `RECIPROCAL`. 142 * `meter` and `pound` are `UNCONVERTIBLE`. 143 * 144 * NOTE: 145 * Only works with SINGLE and COMPOUND units. If one of the units is a 146 * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. 147 */ 148 Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source, 149 const MeasureUnitImpl &target, 150 const ConversionRates &conversionRates, 151 UErrorCode &status); 152 153 /** 154 * Converts from a source `MeasureUnit` to a target `MeasureUnit`. 155 * 156 * NOTE: 157 * Only works with SINGLE and COMPOUND units. If one of the units is a 158 * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. 159 */ 160 class U_I18N_API UnitsConverter : public UMemory { 161 public: 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 * NOTE: 168 * This constructor creates an instance of `ConversionRates` internally. 169 * 170 * @param sourceIdentifier represents the source unit identifier. 171 * @param targetIdentifier represents the target unit identifier. 172 * @param status 173 */ 174 UnitsConverter(StringPiece sourceIdentifier, StringPiece targetIdentifier, UErrorCode &status); 175 176 /** 177 * Constructor of `UnitConverter`. 178 * NOTE: 179 * - source and target must be under the same category 180 * - e.g. meter to mile --> both of them are length units. 181 * 182 * @param source represents the source unit. 183 * @param target represents the target unit. 184 * @param ratesInfo Contains all the needed conversion rates. 185 * @param status 186 */ 187 UnitsConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target, 188 const ConversionRates &ratesInfo, UErrorCode &status); 189 190 /** 191 * Compares two single units and returns 1 if the first one is greater, -1 if the second 192 * one is greater and 0 if they are equal. 193 * 194 * NOTE: 195 * Compares only single units that are convertible. 196 */ 197 static int32_t compareTwoUnits(const MeasureUnitImpl &firstUnit, const MeasureUnitImpl &SecondUnit, 198 const ConversionRates &ratesInfo, UErrorCode &status); 199 200 /** 201 * Convert a measurement expressed in the source unit to a measurement 202 * expressed in the target unit. 203 * 204 * @param inputValue the value to be converted. 205 * @return the converted value. 206 */ 207 double convert(double inputValue) const; 208 209 /** 210 * The inverse of convert(): convert a measurement expressed in the target 211 * unit to a measurement expressed in the source unit. 212 * 213 * @param inputValue the value to be converted. 214 * @return the converted value. 215 */ 216 double convertInverse(double inputValue) const; 217 218 ConversionInfo getConversionInfo() const; 219 220 private: 221 ConversionRate conversionRate_; 222 223 /** 224 * Initialises the object. 225 */ 226 void init(const ConversionRates &ratesInfo, UErrorCode &status); 227 }; 228 229 } // namespace units 230 U_NAMESPACE_END 231 232 #endif //__UNITS_CONVERTER_H__ 233 234 #endif /* #if !UCONFIG_NO_FORMATTING */ 235