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