summaryrefslogtreecommitdiffstats
path: root/intl/icu/source/i18n/units_converter.h
blob: fd1d6ec4227faa4c3119b7bcf3c7792b144108bb (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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
// © 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,
    CONSTANT_METERS_PER_AU,
    CONSTANT_SEC_PER_JULIAN_YEAR,
    CONSTANT_SPEED_OF_LIGHT_METERS_PER_SECOND,

    // 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
    149597870700,              // CONSTANT_METERS_PER_AU
    31557600,                  // CONSTANT_SEC_PER_JULIAN_YEAR
    299792458,                 // CONSTANT_SPEED_OF_LIGHT_METERS_PER_SECOND
};

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 substitution 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 */