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 * TODO ICU-22683: COnsider moving the handling of special mappings (e.g. beaufort) to a separate 114 * struct. 115 */ 116 struct U_I18N_API ConversionRate : public UMemory { 117 const MeasureUnitImpl source; 118 const MeasureUnitImpl target; 119 CharString specialSource; 120 CharString specialTarget; 121 double factorNum = 1; 122 double factorDen = 1; 123 double sourceOffset = 0; 124 double targetOffset = 0; 125 bool reciprocal = false; 126 ConversionRateConversionRate127 ConversionRate(MeasureUnitImpl &&source, MeasureUnitImpl &&target) 128 : source(std::move(source)), target(std::move(target)), specialSource(), specialTarget() {} 129 }; 130 131 enum Convertibility { 132 RECIPROCAL, 133 CONVERTIBLE, 134 UNCONVERTIBLE, 135 }; 136 137 MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source, 138 const ConversionRates &conversionRates, 139 UErrorCode &status); 140 141 /** 142 * Check if the convertibility between `source` and `target`. 143 * For example: 144 * `meter` and `foot` are `CONVERTIBLE`. 145 * `meter-per-second` and `second-per-meter` are `RECIPROCAL`. 146 * `meter` and `pound` are `UNCONVERTIBLE`. 147 * 148 * NOTE: 149 * Only works with SINGLE and COMPOUND units. If one of the units is a 150 * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. 151 */ 152 Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source, 153 const MeasureUnitImpl &target, 154 const ConversionRates &conversionRates, 155 UErrorCode &status); 156 157 /** 158 * Converts from a source `MeasureUnit` to a target `MeasureUnit`. 159 * 160 * NOTE: 161 * Only works with SINGLE and COMPOUND units. If one of the units is a 162 * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. 163 */ 164 class U_I18N_API UnitsConverter : public UMemory { 165 public: 166 /** 167 * Constructor of `UnitConverter`. 168 * NOTE: 169 * - source and target must be under the same category 170 * - e.g. meter to mile --> both of them are length units. 171 * NOTE: 172 * This constructor creates an instance of `ConversionRates` internally. 173 * 174 * @param sourceIdentifier represents the source unit identifier. 175 * @param targetIdentifier represents the target unit identifier. 176 * @param status 177 */ 178 UnitsConverter(StringPiece sourceIdentifier, StringPiece targetIdentifier, UErrorCode &status); 179 180 /** 181 * Constructor of `UnitConverter`. 182 * NOTE: 183 * - source and target must be under the same category 184 * - e.g. meter to mile --> both of them are length units. 185 * 186 * @param source represents the source unit. 187 * @param target represents the target unit. 188 * @param ratesInfo Contains all the needed conversion rates. 189 * @param status 190 */ 191 UnitsConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target, 192 const ConversionRates &ratesInfo, UErrorCode &status); 193 194 /** 195 * Compares two single units and returns 1 if the first one is greater, -1 if the second 196 * one is greater and 0 if they are equal. 197 * 198 * NOTE: 199 * Compares only single units that are convertible. 200 */ 201 static int32_t compareTwoUnits(const MeasureUnitImpl &firstUnit, const MeasureUnitImpl &SecondUnit, 202 const ConversionRates &ratesInfo, UErrorCode &status); 203 204 /** 205 * Convert a measurement expressed in the source unit to a measurement 206 * expressed in the target unit. 207 * 208 * @param inputValue the value to be converted. 209 * @return the converted value. 210 */ 211 double convert(double inputValue) const; 212 213 /** 214 * The inverse of convert(): convert a measurement expressed in the target 215 * unit to a measurement expressed in the source unit. 216 * 217 * @param inputValue the value to be converted. 218 * @return the converted value. 219 */ 220 double convertInverse(double inputValue) const; 221 222 ConversionInfo getConversionInfo() const; 223 224 private: 225 ConversionRate conversionRate_; 226 227 /** 228 * Initialises the object. 229 */ 230 void init(const ConversionRates &ratesInfo, UErrorCode &status); 231 232 /** 233 * Convert from what should be discrete scale values for a particular unit like beaufort 234 * to a corresponding value in the base unit (which can have any decimal value, like meters/sec). 235 * This can handle different scales, specified by minBaseForScaleValues[]. 236 */ 237 double scaleToBase(double scaleValue, double minBaseForScaleValues[], int scaleMax) const; 238 239 /** 240 * Convert from a value in the base unit (which can have any decimal value, like meters/sec) to a corresponding 241 * discrete value in a scale (like beaufort), where each scale value represents a range of base values. 242 * This can handle different scales, specified by minBaseForScaleValues[]. 243 */ 244 double baseToScale(double baseValue, double minBaseForScaleValues[], int scaleMax) const; 245 246 }; 247 248 } // namespace units 249 U_NAMESPACE_END 250 251 #endif //__UNITS_CONVERTER_H__ 252 253 #endif /* #if !UCONFIG_NO_FORMATTING */ 254