summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/units_complexconverter.h
blob: d56ce8d4ce378f2e06fb75e938dfd9264213baef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
// © 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_COMPLEXCONVERTER_H__
#define __UNITS_COMPLEXCONVERTER_H__

#include "cmemory.h"
#include "measunit_impl.h"
#include "number_roundingutils.h"
#include "unicode/errorcode.h"
#include "unicode/measure.h"
#include "units_converter.h"
#include "units_data.h"

U_NAMESPACE_BEGIN

// Export explicit template instantiations of MaybeStackArray, MemoryPool and
// MaybeStackVector. This is required when building DLLs for Windows. (See
// datefmt.h, collationiterator.h, erarules.h and others for similar examples.)
//
// Note: These need to be outside of the units namespace, or Clang will generate
// a compile error.
#if U_PF_WINDOWS <= U_PLATFORM && U_PLATFORM <= U_PF_CYGWIN
template class U_I18N_API MaybeStackArray<units::UnitsConverter*, 8>;
template class U_I18N_API MemoryPool<units::UnitsConverter, 8>;
template class U_I18N_API MaybeStackVector<units::UnitsConverter, 8>;
template class U_I18N_API MaybeStackArray<MeasureUnitImpl*, 8>;
template class U_I18N_API MemoryPool<MeasureUnitImpl, 8>;
template class U_I18N_API MaybeStackVector<MeasureUnitImpl, 8>;
template class U_I18N_API MaybeStackArray<MeasureUnit*, 8>;
template class U_I18N_API MemoryPool<MeasureUnit, 8>;
template class U_I18N_API MaybeStackVector<MeasureUnit, 8>;
#endif

namespace units {

/**
 *  Converts from single or compound unit to single, compound or mixed units.
 * For example, from `meter` to `foot+inch`.
 *
 *  DESIGN:
 *    This class uses `UnitsConverter` in order to perform the single converter (i.e. converters from a
 *    single unit to another single unit). Therefore, `ComplexUnitsConverter` class contains multiple
 *    instances of the `UnitsConverter` to perform the conversion.
 */
class U_I18N_API ComplexUnitsConverter : public UMemory {
  public:
    /**
     * Constructs `ComplexUnitsConverter` for an `targetUnit` that could be Single, Compound or Mixed.
     * In case of:
     * 1- Single and Compound units,
     *    the conversion will not perform anything, the input will be equal to the output.
     * 2- Mixed Unit
     *    the conversion will consider the input is the biggest unit. And will convert it to be spread
     *    through the target units. For example: if target unit is "inch-and-foot", and the input is 2.5.
     *    The converter will consider the input value in "foot", because foot is the biggest unit.
     *    Then, it will convert 2.5 feet to "inch-and-foot".
     *
     * @param targetUnit could be any units type (single, compound or mixed).
     * @param ratesInfo
     * @param status
     */
    ComplexUnitsConverter(const MeasureUnitImpl &targetUnit, const ConversionRates &ratesInfo,
                          UErrorCode &status);
    /**
     * Constructor of `ComplexUnitsConverter`.
     * NOTE:
     *   - inputUnit and outputUnits must be under the same category
     *      - e.g. meter to feet and inches --> all of them are length units.
     *
     * @param inputUnit represents the source unit. (should be single or compound unit).
     * @param outputUnits represents the output unit. could be any type. (single, compound or mixed).
     * @param status
     */
    ComplexUnitsConverter(StringPiece inputUnitIdentifier, StringPiece outputUnitsIdentifier,
                          UErrorCode &status);

    /**
     * Constructor of `ComplexUnitsConverter`.
     * NOTE:
     *   - inputUnit and outputUnits must be under the same category
     *      - e.g. meter to feet and inches --> all of them are length units.
     *
     * @param inputUnit represents the source unit. (should be single or compound unit).
     * @param outputUnits represents the output unit. could be any type. (single, compound or mixed).
     * @param ratesInfo a ConversionRates instance containing the unit conversion rates.
     * @param status
     */
    ComplexUnitsConverter(const MeasureUnitImpl &inputUnit, const MeasureUnitImpl &outputUnits,
                          const ConversionRates &ratesInfo, UErrorCode &status);

    // Returns true if the specified `quantity` of the `inputUnit`, expressed in terms of the biggest
    // unit in the MeasureUnit `outputUnit`, is greater than or equal to `limit`.
    //    For example, if the input unit is `meter` and the target unit is `foot+inch`. Therefore, this
    //    function will convert the `quantity` from `meter` to `foot`, then, it will compare the value in
    //    `foot` with the `limit`.
    UBool greaterThanOrEqual(double quantity, double limit) const;

    // Returns outputMeasures which is an array with the corresponding values.
    //    - E.g. converting meters to feet and inches.
    //                  1 meter --> 3 feet, 3.3701 inches
    //         NOTE:
    //           the smallest element is the only element that could have fractional values. And all
    //           other elements are floored to the nearest integer
    MaybeStackVector<Measure>
    convert(double quantity, icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const;

    // TODO(ICU-21937): Make it private after submitting the public units conversion API.
    MaybeStackVector<UnitsConverter> unitsConverters_;

    // TODO(ICU-21937): Make it private after submitting the public units conversion API.
    // Individual units of mixed units, sorted big to small, with indices
    // indicating the requested output mixed unit order.
    MaybeStackVector<MeasureUnitImplWithIndex> units_;

  private:
    // Sorts units_, which must be populated before calling this, and populates
    // unitsConverters_.
    void init(const MeasureUnitImpl &inputUnit, const ConversionRates &ratesInfo, UErrorCode &status);

    // Applies the rounder to the quantity (last element) and bubble up any carried value to all the
    // intValues.
    // TODO(ICU-21288): get smarter about precision for mixed units.
    void applyRounder(MaybeStackArray<int64_t, 5> &intValues, double &quantity,
                      icu::number::impl::RoundingImpl *rounder, UErrorCode &status) const;
};

} // namespace units
U_NAMESPACE_END

#endif //__UNITS_COMPLEXCONVERTER_H__

#endif /* #if !UCONFIG_NO_FORMATTING */