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_COMPLEXCONVERTER_H__
8 #define __UNITS_COMPLEXCONVERTER_H__
9 
10 #include "cmemory.h"
11 #include "measunit_impl.h"
12 #include "number_roundingutils.h"
13 #include "unicode/errorcode.h"
14 #include "unicode/measure.h"
15 #include "units_converter.h"
16 #include "units_data.h"
17 
18 U_NAMESPACE_BEGIN
19 
20 // Export explicit template instantiations of MaybeStackArray, MemoryPool and
21 // MaybeStackVector. This is required when building DLLs for Windows. (See
22 // datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
23 //
24 // Note: These need to be outside of the units namespace, or Clang will generate
25 // a compile error.
26 #if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
27 template class U_I18N_API MaybeStackArray<units::UnitsConverter*, 8>;
28 template class U_I18N_API MemoryPool<units::UnitsConverter, 8>;
29 template class U_I18N_API MaybeStackVector<units::UnitsConverter, 8>;
30 template class U_I18N_API MaybeStackArray<MeasureUnitImpl*, 8>;
31 template class U_I18N_API MemoryPool<MeasureUnitImpl, 8>;
32 template class U_I18N_API MaybeStackVector<MeasureUnitImpl, 8>;
33 template class U_I18N_API MaybeStackArray<MeasureUnit*, 8>;
34 template class U_I18N_API MemoryPool<MeasureUnit, 8>;
35 template class U_I18N_API MaybeStackVector<MeasureUnit, 8>;
36 #endif
37 
38 namespace units {
39 
40 /**
41  *  Converts from single or compound unit to single, compound or mixed units.
42  * For example, from `meter` to `foot+inch`.
43  *
44  *  DESIGN:
45  *    This class uses `UnitsConverter` in order to perform the single converter (i.e. converters from a
46  *    single unit to another single unit). Therefore, `ComplexUnitsConverter` class contains multiple
47  *    instances of the `UnitsConverter` to perform the conversion.
48  */
49 class U_I18N_API ComplexUnitsConverter : public UMemory {
50   public:
51     /**
52      * Constructs `ComplexUnitsConverter` for an `targetUnit` that could be Single, Compound or Mixed.
53      * In case of:
54      * 1- Single and Compound units,
55      *    the conversion will not perform anything, the input will be equal to the output.
56      * 2- Mixed Unit
57      *    the conversion will consider the input is the biggest unit. And will convert it to be spread
58      *    through the target units. For example: if target unit is "inch-and-foot", and the input is 2.5.
59      *    The converter will consider the input value in "foot", because foot is the biggest unit.
60      *    Then, it will convert 2.5 feet to "inch-and-foot".
61      *
62      * @param targetUnit could be any units type (single, compound or mixed).
63      * @param ratesInfo
64      * @param status
65      */
66     ComplexUnitsConverter(const MeasureUnitImpl &targetUnit, const ConversionRates &ratesInfo,
67                           UErrorCode &status);
68     /**
69      * Constructor of `ComplexUnitsConverter`.
70      * NOTE:
71      *   - inputUnit and outputUnits must be under the same category
72      *      - e.g. meter to feet and inches --> all of them are length units.
73      *
74      * @param inputUnit represents the source unit. (should be single or compound unit).
75      * @param outputUnits represents the output unit. could be any type. (single, compound or mixed).
76      * @param status
77      */
78     ComplexUnitsConverter(StringPiece inputUnitIdentifier, StringPiece outputUnitsIdentifier,
79                           UErrorCode &status);
80 
81     /**
82      * Constructor of `ComplexUnitsConverter`.
83      * NOTE:
84      *   - inputUnit and outputUnits must be under the same category
85      *      - e.g. meter to feet and inches --> all of them are length units.
86      *
87      * @param inputUnit represents the source unit. (should be single or compound unit).
88      * @param outputUnits represents the output unit. could be any type. (single, compound or mixed).
89      * @param ratesInfo a ConversionRates instance containing the unit conversion rates.
90      * @param status
91      */
92     ComplexUnitsConverter(const MeasureUnitImpl &inputUnit, const MeasureUnitImpl &outputUnits,
93                           const ConversionRates &ratesInfo, UErrorCode &status);
94 
95     // Returns true if the specified `quantity` of the `inputUnit`, expressed in terms of the biggest
96     // unit in the MeasureUnit `outputUnit`, is greater than or equal to `limit`.
97     //    For example, if the input unit is `meter` and the target unit is `foot+inch`. Therefore, this
98     //    function will convert the `quantity` from `meter` to `foot`, then, it will compare the value in
99     //    `foot` with the `limit`.
100     UBool greaterThanOrEqual(double quantity, double limit) const;
101 
102     // Returns outputMeasures which is an array with the corresponding values.
103     //    - E.g. converting meters to feet and inches.
104     //                  1 meter --> 3 feet, 3.3701 inches
105     //         NOTE:
106     //           the smallest element is the only element that could have fractional values. And all
107     //           other elements are floored to the nearest integer
108     MaybeStackVector<Measure>
109     convert(double quantity, icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const;
110 
111   private:
112     MaybeStackVector<UnitsConverter> unitsConverters_;
113 
114     // Individual units of mixed units, sorted big to small, with indices
115     // indicating the requested output mixed unit order.
116     MaybeStackVector<MeasureUnitImplWithIndex> units_;
117 
118     // Sorts units_, which must be populated before calling this, and populates
119     // unitsConverters_.
120     void init(const MeasureUnitImpl &inputUnit, const ConversionRates &ratesInfo, UErrorCode &status);
121 
122     // Applies the rounder to the quantity (last element) and bubble up any carried value to all the
123     // intValues.
124     // TODO(ICU-21288): get smarter about precision for mixed units.
125     void applyRounder(MaybeStackArray<int64_t, 5> &intValues, double &quantity,
126                       icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const;
127 };
128 
129 } // namespace units
130 U_NAMESPACE_END
131 
132 #endif //__UNITS_COMPLEXCONVERTER_H__
133 
134 #endif /* #if !UCONFIG_NO_FORMATTING */
135