// © 2020 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html #include "unicode/utypes.h" #if !UCONFIG_NO_FORMATTING #ifndef __UNITS_CONVERTER_H__ #define __UNITS_CONVERTER_H__ #include "cmemory.h" #include "measunit_impl.h" #include "unicode/errorcode.h" #include "unicode/stringpiece.h" #include "unicode/uobject.h" #include "units_converter.h" #include "units_data.h" U_NAMESPACE_BEGIN namespace units { /* Internal Structure */ // Constants corresponding to unitConstants in CLDR's units.xml. enum Constants { CONSTANT_FT2M, // ft_to_m CONSTANT_PI, // PI CONSTANT_GRAVITY, // Gravity of earth (9.80665 m/s^2), "g". CONSTANT_G, // Newtonian constant of gravitation, "G". CONSTANT_GAL_IMP2M3, // Gallon imp to m3 CONSTANT_LB2KG, // Pound to Kilogram CONSTANT_GLUCOSE_MOLAR_MASS, CONSTANT_ITEM_PER_MOLE, // Must be the last element. CONSTANTS_COUNT }; // These values are a hard-coded subset of unitConstants in the units // resources file. A unit test checks that all constants in the resource // file are at least recognised by the code. Derived constants' values or // hard-coded derivations are not checked. // In ICU4J, these constants live in UnitConverter.Factor.getConversionRate(). static const double constantsValues[CONSTANTS_COUNT] = { 0.3048, // CONSTANT_FT2M 411557987.0 / 131002976.0, // CONSTANT_PI 9.80665, // CONSTANT_GRAVITY 6.67408E-11, // CONSTANT_G 0.00454609, // CONSTANT_GAL_IMP2M3 0.45359237, // CONSTANT_LB2KG 180.1557, // CONSTANT_GLUCOSE_MOLAR_MASS 6.02214076E+23, // CONSTANT_ITEM_PER_MOLE }; typedef enum Signum { NEGATIVE = -1, POSITIVE = 1, } Signum; /* Represents a conversion factor */ struct U_I18N_API Factor { double factorNum = 1; double factorDen = 1; double offset = 0; bool reciprocal = false; // Exponents for the symbolic constants int32_t constantExponents[CONSTANTS_COUNT] = {}; void multiplyBy(const Factor &rhs); void divideBy(const Factor &rhs); // Apply the power to the factor. void power(int32_t power); // Apply SI or binary prefix to the Factor. void applyPrefix(UMeasurePrefix unitPrefix); // Does an in-place substition of the "symbolic constants" based on // constantExponents (resetting the exponents). // // In ICU4J, see UnitConverter.Factor.getConversionRate(). void substituteConstants(); }; struct U_I18N_API ConversionInfo { double conversionRate; double offset; bool reciprocal; }; /* * Adds a single factor element to the `Factor`. e.g "ft3m", "2.333" or "cup2m3". But not "cup2m3^3". */ void U_I18N_API addSingleFactorConstant(StringPiece baseStr, int32_t power, Signum sigNum, Factor &factor, UErrorCode &status); /** * Represents the conversion rate between `source` and `target`. */ struct U_I18N_API ConversionRate : public UMemory { const MeasureUnitImpl source; const MeasureUnitImpl target; double factorNum = 1; double factorDen = 1; double sourceOffset = 0; double targetOffset = 0; bool reciprocal = false; ConversionRate(MeasureUnitImpl &&source, MeasureUnitImpl &&target) : source(std::move(source)), target(std::move(target)) {} }; enum Convertibility { RECIPROCAL, CONVERTIBLE, UNCONVERTIBLE, }; MeasureUnitImpl U_I18N_API extractCompoundBaseUnit(const MeasureUnitImpl &source, const ConversionRates &conversionRates, UErrorCode &status); /** * Check if the convertibility between `source` and `target`. * For example: * `meter` and `foot` are `CONVERTIBLE`. * `meter-per-second` and `second-per-meter` are `RECIPROCAL`. * `meter` and `pound` are `UNCONVERTIBLE`. * * NOTE: * Only works with SINGLE and COMPOUND units. If one of the units is a * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. */ Convertibility U_I18N_API extractConvertibility(const MeasureUnitImpl &source, const MeasureUnitImpl &target, const ConversionRates &conversionRates, UErrorCode &status); /** * Converts from a source `MeasureUnit` to a target `MeasureUnit`. * * NOTE: * Only works with SINGLE and COMPOUND units. If one of the units is a * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity. */ class U_I18N_API UnitsConverter : public UMemory { public: /** * Constructor of `UnitConverter`. * NOTE: * - source and target must be under the same category * - e.g. meter to mile --> both of them are length units. * NOTE: * This constructor creates an instance of `ConversionRates` internally. * * @param sourceIdentifier represents the source unit identifier. * @param targetIdentifier represents the target unit identifier. * @param status */ UnitsConverter(StringPiece sourceIdentifier, StringPiece targetIdentifier, UErrorCode &status); /** * Constructor of `UnitConverter`. * NOTE: * - source and target must be under the same category * - e.g. meter to mile --> both of them are length units. * * @param source represents the source unit. * @param target represents the target unit. * @param ratesInfo Contains all the needed conversion rates. * @param status */ UnitsConverter(const MeasureUnitImpl &source, const MeasureUnitImpl &target, const ConversionRates &ratesInfo, UErrorCode &status); /** * Compares two single units and returns 1 if the first one is greater, -1 if the second * one is greater and 0 if they are equal. * * NOTE: * Compares only single units that are convertible. */ static int32_t compareTwoUnits(const MeasureUnitImpl &firstUnit, const MeasureUnitImpl &SecondUnit, const ConversionRates &ratesInfo, UErrorCode &status); /** * Convert a measurement expressed in the source unit to a measurement * expressed in the target unit. * * @param inputValue the value to be converted. * @return the converted value. */ double convert(double inputValue) const; /** * The inverse of convert(): convert a measurement expressed in the target * unit to a measurement expressed in the source unit. * * @param inputValue the value to be converted. * @return the converted value. */ double convertInverse(double inputValue) const; ConversionInfo getConversionInfo() const; private: ConversionRate conversionRate_; /** * Initialises the object. */ void init(const ConversionRates &ratesInfo, UErrorCode &status); }; } // namespace units U_NAMESPACE_END #endif //__UNITS_CONVERTER_H__ #endif /* #if !UCONFIG_NO_FORMATTING */