ICU-20568 UnitsRouter, ComplexUnitConverter, numberformatter.h

add usage to number formatter settings header
PR: https://github.com/sffc/icu/pull/23
Commit: 6d78a95d6dbe0ef946624e7253f57d414168c77a

Implementation of UnitsRouter and ComplexUnitConverter.
PR: https://github.com/sffc/icu/pull/30
Commit: 1ae7190d1950377d5fdab822bb65eb67a8891104
diff --git a/icu4c/source/i18n/complexunitsconverter.cpp b/icu4c/source/i18n/complexunitsconverter.cpp
new file mode 100644
index 0000000..4a77b79
--- /dev/null
+++ b/icu4c/source/i18n/complexunitsconverter.cpp
@@ -0,0 +1,128 @@
+// © 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
+
+#include <math.h>
+#include <utility>
+
+#include "cmemory.h"
+#include "complexunitsconverter.h"
+#include "uassert.h"
+#include "unicode/fmtable.h"
+#include "unitconverter.h"
+
+U_NAMESPACE_BEGIN
+
+ComplexUnitsConverter::ComplexUnitsConverter(const MeasureUnit &inputUnit,
+                                             const MeasureUnit &outputUnits,
+                                             const ConversionRates &ratesInfo, UErrorCode &status) {
+
+    if (outputUnits.getComplexity(status) != UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
+        unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, outputUnits, ratesInfo, status);
+        if (U_FAILURE(status)) {
+            return;
+        }
+
+        units_.emplaceBackAndCheckErrorCode(status, outputUnits);
+        return;
+    }
+
+    // In case the `outputUnits` are `UMEASURE_UNIT_MIXED` such as `foot+inch`. In this case we need more
+    // converters to convert from the `inputUnit` to the first unit in the `outputUnits`. Then, a
+    // converter from the first unit in the `outputUnits` to the second unit and so on.
+    //      For Example:
+    //          - inputUnit is `meter`
+    //          - outputUnits is `foot+inch`
+    //              - Therefore, we need to have two converters:
+    //                      1. a converter from `meter` to `foot`
+    //                      2. a converter from `foot` to `inch`
+    //          - Therefore, if the input is `2 meter`:
+    //              1. convert `meter` to `foot` --> 2 meter to 6.56168 feet
+    //              2. convert the residual of 6.56168 feet (0.56168) to inches, which will be (6.74016
+    //              inches)
+    //              3. then, the final result will be (6 feet and 6.74016 inches)
+    int32_t length;
+    auto singleUnits = outputUnits.splitToSingleUnits(length, status);
+    MaybeStackVector<MeasureUnit> singleUnitsInOrder;
+    for (int i = 0; i < length; ++i) {
+        /**
+         *  TODO(younies): ensure units being in order by the biggest unit at first.
+         * 
+         * HINT:
+         *  MaybeStackVector<SingleUnitImpl> singleUnitsInOrder =  MeasureUnitImpl::forMeasureUnitMaybeCopy(outputUnits, status).units;
+         *      uprv_sortArray(
+         *      singleUnitsInOrder.getAlias(),
+         *      singleUnitsInOrder.length(),
+         *      sizeof(singleUnitsInOrder[0]),
+         *      compareSingleUnits,
+         *      nullptr,
+         *      false,
+         *      &status);
+         */ 
+        singleUnitsInOrder.emplaceBackAndCheckErrorCode(status, singleUnits[i]);
+    }
+
+    if (singleUnitsInOrder.length() == 0) {
+        status = U_ILLEGAL_ARGUMENT_ERROR;
+        return;
+    }
+
+    for (int i = 0, n = singleUnitsInOrder.length(); i < n; i++) {
+        if (i == 0) { // first element
+            unitConverters_.emplaceBackAndCheckErrorCode(status, inputUnit, *singleUnitsInOrder[i],
+                                                         ratesInfo, status);
+        } else {
+            unitConverters_.emplaceBackAndCheckErrorCode(status, *singleUnitsInOrder[i - 1],
+                                                         *singleUnitsInOrder[i], ratesInfo, status);
+        }
+
+        if (U_FAILURE(status)) {
+            return;
+        }
+    }
+
+    units_.appendAll(singleUnitsInOrder, status);
+}
+
+UBool ComplexUnitsConverter::greaterThanOrEqual(double quantity, double limit) const {
+    U_ASSERT(unitConverters_.length() > 0);
+
+    // First converter converts to the biggest quantity.
+    double newQuantity = unitConverters_[0]->convert(quantity);
+    return newQuantity >= limit;
+}
+
+MaybeStackVector<Measure> ComplexUnitsConverter::convert(double quantity, UErrorCode &status) const {
+    MaybeStackVector<Measure> result;
+
+    for (int i = 0, n = unitConverters_.length(); i < n; ++i) {
+        quantity = (*unitConverters_[i]).convert(quantity);
+        if (i < n - 1) {
+            int64_t newQuantity = floor(quantity);
+            Formattable formattableNewQuantity(newQuantity);
+
+            // NOTE: Measure would own its MeasureUnit.
+            result.emplaceBackAndCheckErrorCode(status, formattableNewQuantity,
+                                                new MeasureUnit(*units_[i]), status);
+
+            // Keep the residual of the quantity.
+            //   For example: `3.6 feet`, keep only `0.6 feet`
+            quantity -= newQuantity;
+        } else { // LAST ELEMENT
+            Formattable formattableQuantity(quantity);
+
+            // NOTE: Measure would own its MeasureUnit.
+            result.emplaceBackAndCheckErrorCode(status, formattableQuantity, new MeasureUnit(*units_[i]),
+                                                status);
+        }
+    }
+
+    return result;
+}
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/complexunitsconverter.h b/icu4c/source/i18n/complexunitsconverter.h
new file mode 100644
index 0000000..0fe76ba
--- /dev/null
+++ b/icu4c/source/i18n/complexunitsconverter.h
@@ -0,0 +1,67 @@
+// © 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 __COMPLEXUNITSCONVERTER_H__
+#define __COMPLEXUNITSCONVERTER_H__
+
+#include "cmemory.h"
+#include "unicode/errorcode.h"
+#include "unicode/measunit.h"
+#include "unicode/measure.h"
+#include "unitconverter.h"
+#include "unitsdata.h"
+
+U_NAMESPACE_BEGIN
+
+/**
+ *  Converts from single or compound unit to single, compound or mixed units.
+ * For example, from `meter` to `foot+inch`.
+ *
+ *  DESIGN:
+ *    This class uses `UnitConverter` 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 `UnitConverter` to perform the conversion.
+ */
+class U_I18N_API ComplexUnitsConverter {
+  public:
+    /**
+     * 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(const MeasureUnit &inputUnit, const MeasureUnit &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, UErrorCode &status) const;
+
+  private:
+    MaybeStackVector<UnitConverter> unitConverters_;
+    MaybeStackVector<MeasureUnit> units_;
+};
+
+U_NAMESPACE_END
+
+#endif //__COMPLEXUNITSCONVERTER_H__
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/i18n.vcxproj b/icu4c/source/i18n/i18n.vcxproj
index f39f503..ea139c0 100644
--- a/icu4c/source/i18n/i18n.vcxproj
+++ b/icu4c/source/i18n/i18n.vcxproj
@@ -251,6 +251,8 @@
     <ClCompile Include="ulocdata.cpp" />
     <ClCompile Include="umsg.cpp" />
     <ClCompile Include="unitconverter.cpp" />
+    <ClCompile Include="complexunitsconverter.cpp" />
+    <ClCompile Include="unitsrouter.cpp" />
     <ClCompile Include="unitsdata.cpp" />
     <ClCompile Include="unum.cpp" />
     <ClCompile Include="unumsys.cpp" />
@@ -487,6 +489,8 @@
     <ClInclude Include="numrange_impl.h" />
     <ClInclude Include="formattedval_impl.h" />
     <ClInclude Include="unitconverter.h" />
+    <ClInclude Include="complexunitsconverter.h" />
+    <ClInclude Include="unitsrouter.h" />
     <ClInclude Include="unitsdata.h" />
   </ItemGroup>
   <ItemGroup>
diff --git a/icu4c/source/i18n/i18n.vcxproj.filters b/icu4c/source/i18n/i18n.vcxproj.filters
index 1df9dd6..1dc9b5c 100644
--- a/icu4c/source/i18n/i18n.vcxproj.filters
+++ b/icu4c/source/i18n/i18n.vcxproj.filters
@@ -654,6 +654,12 @@
     <ClCompile Include="unitconverter.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
+    <ClCompile Include="complexunitsconverter.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
+    <ClCompile Include="unitsrouter.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
     <ClCompile Include="unitsdata.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
@@ -1004,6 +1010,12 @@
     <ClInclude Include="unitconveter.h">
       <Filter>formatting</Filter>
     </ClInclude>
+    <ClInclude Include="complexunitsconverter.h">
+      <Filter>formatting</Filter>
+    </ClInclude>
+    <ClInclude Include="unitsrouter.h">
+      <Filter>formatting</Filter>
+    </ClInclude>
     <ClInclude Include="unitsdata.h">
       <Filter>formatting</Filter>
     </ClInclude>
@@ -1247,4 +1259,4 @@
       <Filter>misc</Filter>
     </ResourceCompile>
   </ItemGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/icu4c/source/i18n/i18n_uwp.vcxproj b/icu4c/source/i18n/i18n_uwp.vcxproj
index aabf100..30c98a0 100644
--- a/icu4c/source/i18n/i18n_uwp.vcxproj
+++ b/icu4c/source/i18n/i18n_uwp.vcxproj
@@ -482,6 +482,8 @@
     <ClCompile Include="ulocdata.cpp" />
     <ClCompile Include="umsg.cpp" />
     <ClCompile Include="unitconverter.cpp" />
+    <ClCompile Include="complexunitsconverter.cpp" />
+    <ClCompile Include="unitsrouter.cpp" />
     <ClCompile Include="unitsdata.cpp" />
     <ClCompile Include="unum.cpp" />
     <ClCompile Include="unumsys.cpp" />
@@ -718,6 +720,8 @@
     <ClInclude Include="numrange_impl.h" />
     <ClInclude Include="formattedval_impl.h" />
     <ClInclude Include="unitconverter.h" />
+    <ClInclude Include="complexunitsconverter.h" />
+    <ClInclude Include="unitsrouter.h" />
     <ClInclude Include="unitsdata.h" />
   </ItemGroup>
   <ItemGroup>
@@ -729,4 +733,4 @@
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" Condition="'$(SkipUWP)'!='true'" />
   <ImportGroup Label="ExtensionTargets">
   </ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
diff --git a/icu4c/source/i18n/sources.txt b/icu4c/source/i18n/sources.txt
index 6c9e134..ce2486b 100644
--- a/icu4c/source/i18n/sources.txt
+++ b/icu4c/source/i18n/sources.txt
@@ -32,6 +32,7 @@
 collationtailoring.cpp
 collationweights.cpp
 compactdecimalformat.cpp
+complexunitsconverter.cpp
 coptccal.cpp
 cpdtrans.cpp
 csdetect.cpp
@@ -210,6 +211,7 @@
 uni2name.cpp
 unitconverter.cpp
 unitsdata.cpp
+unitsrouter.cpp
 unum.cpp
 unumsys.cpp
 upluralrules.cpp
diff --git a/icu4c/source/i18n/unicode/measunit.h b/icu4c/source/i18n/unicode/measunit.h
index 7c59735..faa7aef 100644
--- a/icu4c/source/i18n/unicode/measunit.h
+++ b/icu4c/source/i18n/unicode/measunit.h
@@ -434,7 +434,7 @@
      * For example, if the receiver is "kilowatt" and the argument is "hour-per-day", then the
      * unit "kilowatt-hour-per-day" is returned.
      *
-     * NOTE: Only works on SINGLE and COMPOUND units. If either unit (receivee and argument) is a
+     * NOTE: Only works on SINGLE and COMPOUND units. If either unit (receiver and argument) is a
      * MIXED unit, an error will occur. For more information, see UMeasureUnitComplexity.
      *
      * @param other The MeasureUnit to multiply with the target.
diff --git a/icu4c/source/i18n/unicode/numberformatter.h b/icu4c/source/i18n/unicode/numberformatter.h
index 50da816..0fb16c2 100644
--- a/icu4c/source/i18n/unicode/numberformatter.h
+++ b/icu4c/source/i18n/unicode/numberformatter.h
@@ -1508,10 +1508,15 @@
      * All units will be properly localized with locale data, and all units are compatible with notation styles,
      * rounding precisions, and other number formatter settings.
      *
+     * \note If the usage() is set, the output unit **will be changed** to
+     *       produce localised units, according to usage, locale and unit. See
+     *       FormattedNumber::getOutputUnit().
+     *
      * Pass this method any instance of {@link MeasureUnit}. For units of measure:
      *
      * <pre>
      * NumberFormatter::with().unit(MeasureUnit::getMeter())
+     * NumberFormatter::with().unit(MeasureUnit::forIdentifier("foot-per-second", status))
      * </pre>
      *
      * Currency:
@@ -2039,6 +2044,54 @@
      */
     Derived scale(const Scale &scale) &&;
 
+#ifndef U_HIDE_DRAFT_API
+    /**
+     * Specifies the usage for which numbers will be formatted ("person",
+     * "road", "person", etc.)
+     *
+     * When a `usage` is specified, the output unit will change depending on the
+     * `Locale` and the unit quantity. For example, formatting length
+     * measurements specified in meters:
+     *
+     * `NumberFormatter::with().usage("person").unit(MeasureUnit::getMeter()).locale("en-US")`
+     *   * When formatting 0.25, the output will be "10 inches".
+     *   * When formatting 1.50, the output will be "4 feet and 11 inches".
+     *
+     * The input unit specified via unit() determines the type of measurement
+     * being formatted (e.g. "length" when the unit is "foot"). The usage
+     * requested will be looked for only within this category of measurement
+     * units.
+     *
+     * The output unit can be found via FormattedNumber::getOutputUnit().
+     *
+     * If the usage has multiple parts (e.g. "land-agriculture-grain") and does
+     * not match a known usage preference, the last part will be dropped
+     * repeatedly until a match is found (e.g. trying "land-agriculture", then
+     * "land"). If a match is still not found, usage will fall back to
+     * "default".
+     *
+     * Setting usage to an empty string clears the usage (disables usage-based
+     * localized formatting).
+     *
+     * @param usage A `usage` parameter from the units resource. See the
+     * unitPreferenceData in *source/data/misc/units.txt*, generated from
+     * `unitPreferenceData` in [CLDR's
+     * supplemental/units.xml](https://github.com/unicode-org/cldr/blob/master/common/supplemental/units.xml).
+     * @return The fluent chain.
+     * @draft ICU 68
+     */
+    Derived usage(StringPiece usage) const &;
+
+    /**
+     * Overload of usage() for use on an rvalue reference.
+     *
+     * @param usage The unit `usage`.
+     * @return The fluent chain.
+     * @draft ICU 68
+     */
+    Derived usage(StringPiece usage) &&;
+#endif // U_HIDE_DRAFT_API
+
 #ifndef U_HIDE_INTERNAL_API
 
     /**
@@ -2515,6 +2568,21 @@
     inline StringClass toDecimalNumber(UErrorCode& status) const;
 #endif // U_HIDE_DRAFT_API
 
+#ifndef U_HIDE_DRAFT_API
+	/**
+     * Gets the resolved output unit.
+     *
+     * The output unit is dependent upon the localized preferences for the usage
+     * specified via NumberFormatterSettings::usage(), and may be a unit with
+     * UMEASURE_UNIT_MIXED unit complexity (MeasureUnit::getComplexity()), such
+     * as "foot+inch" or "hour+minute+second".
+     *
+     * @return `MeasureUnit`.
+     * @draft ICU 68
+     */
+    MeasureUnit getOutputUnit(UErrorCode& status) const;
+#endif // U_HIDE_DRAFT_API
+
 #ifndef U_HIDE_INTERNAL_API
 
     /**
diff --git a/icu4c/source/i18n/unitconverter.cpp b/icu4c/source/i18n/unitconverter.cpp
index 516f223..70adbd7 100644
--- a/icu4c/source/i18n/unitconverter.cpp
+++ b/icu4c/source/i18n/unitconverter.cpp
@@ -126,14 +126,19 @@
         constantsValues[CONSTANT_GAL_IMP2M3] = 0.00454609;
 
         for (int i = 0; i < CONSTANTS_COUNT; i++) {
-            if (this->constants[i] == 0) { continue;}
+            if (this->constants[i] == 0) {
+                continue;
+            }
 
             auto absPower = std::abs(this->constants[i]);
             SigNum powerSig = this->constants[i] < 0 ? SigNum::NEGATIVE : SigNum::POSITIVE;
             double absConstantValue = std::pow(constantsValues[i], absPower);
 
-            if (powerSig ==  SigNum::NEGATIVE) { this->factorDen *= absConstantValue;} 
-            else { this->factorNum *= absConstantValue;}
+            if (powerSig == SigNum::NEGATIVE) {
+                this->factorDen *= absConstantValue;
+            } else {
+                this->factorNum *= absConstantValue;
+            }
 
             this->constants[i] = 0;
         }
@@ -152,7 +157,9 @@
     StringToDoubleConverter converter(0, 0, 0, "", "");
     int32_t count;
     double result = converter.StringToDouble(strNum.data(), strNum.length(), &count);
-    if (count != strNum.length()) { status = U_INVALID_FORMAT_ERROR; }
+    if (count != strNum.length()) {
+        status = U_INVALID_FORMAT_ERROR;
+    }
 
     return result;
 }
@@ -176,48 +183,6 @@
     return strToDouble(strWithDivide, status);
 }
 
-/**
- * Extracts the compound base unit of a compound unit (`source`). For example, if the source unit is
- * `square-mile-per-hour`, the compound base unit will be `square-meter-per-second`
- */
-MeasureUnit extractCompoundBaseUnit(const MeasureUnit &source, const ConversionRates &conversionRates,
-                                    UErrorCode &status) {
-    MeasureUnit result;
-    int32_t count;
-    const auto singleUnits = source.splitToSingleUnits(count, status);
-    if (U_FAILURE(status)) return result;
-
-    for (int i = 0; i < count; ++i) {
-        const auto &singleUnit = singleUnits[i];
-        // Extract `ConversionRateInfo` using the absolute unit. For example: in case of `square-meter`,
-        // we will use `meter`
-        const auto singleUnitImpl = SingleUnitImpl::forMeasureUnit(singleUnit, status);
-        const auto rateInfo = conversionRates.extractConversionInfo(singleUnitImpl.getSimpleUnitID(), status);
-        if (U_FAILURE(status)) { return result; }
-        if (rateInfo == nullptr) {
-            status = U_INTERNAL_PROGRAM_ERROR;
-            return result;
-        }
-
-        // Multiply the power of the singleUnit by the power of the baseUnit. For example, square-hectare
-        // must be p4-meter. (NOTE: hectare --> square-meter)
-        auto compoundBaseUnit = MeasureUnit::forIdentifier(rateInfo->baseUnit.toStringPiece(), status);
-
-        int32_t baseUnitsCount;
-        auto baseUnits = compoundBaseUnit.splitToSingleUnits(baseUnitsCount, status);
-        for (int j = 0; j < baseUnitsCount; j++) {
-            int8_t newDimensionality =
-                baseUnits[j].getDimensionality(status) * singleUnit.getDimensionality(status);
-            result = result.product(baseUnits[j].withDimensionality(newDimensionality, status), status);
-
-            if (U_FAILURE(status)) { return result; }
-        }
-    }
-
-    return result;
-}
-
-// TODO: Load those constant from units data.
 /*
  * Adds a single factor element to the `Factor`. e.g "ft3m", "2.333" or "cup2m3". But not "cup2m3^3".
  */
@@ -365,7 +330,9 @@
     const auto &compoundSourceUnit = MeasureUnitImpl::forMeasureUnit(unit, memory, status);
     if (U_FAILURE(status)) return false;
 
-    if (compoundSourceUnit.complexity != UMEASURE_UNIT_SINGLE) { return false; }
+    if (compoundSourceUnit.complexity != UMEASURE_UNIT_SINGLE) {
+        return false;
+    }
 
     U_ASSERT(compoundSourceUnit.units.length() == 1);
     auto singleUnit = *(compoundSourceUnit.units[0]);
@@ -419,10 +386,63 @@
 
 } // namespace
 
+/**
+ * Extracts the compound base unit of a compound unit (`source`). For example, if the source unit is
+ * `square-mile-per-hour`, the compound base unit will be `square-meter-per-second`
+ */
+MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &source,
+                                               const ConversionRates &conversionRates,
+                                               UErrorCode &status) {
+    MeasureUnit result;
+    int32_t count;
+    const auto singleUnits = source.splitToSingleUnits(count, status);
+    if (U_FAILURE(status)) return result;
+
+    for (int i = 0; i < count; ++i) {
+        const auto &singleUnit = singleUnits[i];
+        // Extract `ConversionRateInfo` using the absolute unit. For example: in case of `square-meter`,
+        // we will use `meter`
+        const auto singleUnitImpl = SingleUnitImpl::forMeasureUnit(singleUnit, status);
+        const auto rateInfo =
+            conversionRates.extractConversionInfo(singleUnitImpl.getSimpleUnitID(), status);
+        if (U_FAILURE(status)) {
+            return result;
+        }
+        if (rateInfo == nullptr) {
+            status = U_INTERNAL_PROGRAM_ERROR;
+            return result;
+        }
+
+        // Multiply the power of the singleUnit by the power of the baseUnit. For example, square-hectare
+        // must be p4-meter. (NOTE: hectare --> square-meter)
+        auto compoundBaseUnit = MeasureUnit::forIdentifier(rateInfo->baseUnit.toStringPiece(), status);
+
+        int32_t baseUnitsCount;
+        auto baseUnits = compoundBaseUnit.splitToSingleUnits(baseUnitsCount, status);
+        for (int j = 0; j < baseUnitsCount; j++) {
+            int8_t newDimensionality =
+                baseUnits[j].getDimensionality(status) * singleUnit.getDimensionality(status);
+            result = result.product(baseUnits[j].withDimensionality(newDimensionality, status), status);
+
+            if (U_FAILURE(status)) {
+                return result;
+            }
+        }
+    }
+
+    return result;
+}
+
 UnitsConvertibilityState U_I18N_API checkConvertibility(const MeasureUnit &source,
                                                         const MeasureUnit &target,
                                                         const ConversionRates &conversionRates,
                                                         UErrorCode &status) {
+    if (source.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
+        target.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
+        status = U_INTERNAL_PROGRAM_ERROR;
+        return UNCONVERTIBLE;
+    }
+
     auto sourceBaseUnit = extractCompoundBaseUnit(source, conversionRates, status);
     auto targetBaseUnit = extractCompoundBaseUnit(target, conversionRates, status);
 
@@ -431,11 +451,24 @@
     if (sourceBaseUnit == targetBaseUnit) return CONVERTIBLE;
     if (sourceBaseUnit == targetBaseUnit.reciprocal(status)) return RECIPROCAL;
 
+    auto sourceSimplified = sourceBaseUnit.simplify(status);
+    auto targetSimplified = targetBaseUnit.simplify(status);
+
+    if (sourceSimplified == targetSimplified) return CONVERTIBLE;
+    if (sourceSimplified == targetSimplified.reciprocal(status)) return RECIPROCAL;
+
     return UNCONVERTIBLE;
 }
 
 UnitConverter::UnitConverter(MeasureUnit source, MeasureUnit target, const ConversionRates &ratesInfo,
                              UErrorCode &status) {
+
+    if (source.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED ||
+        target.getComplexity(status) == UMeasureUnitComplexity::UMEASURE_UNIT_MIXED) {
+        status = U_INTERNAL_PROGRAM_ERROR;
+        return;
+    }
+
     UnitsConvertibilityState unitsState = checkConvertibility(source, target, ratesInfo, status);
     if (U_FAILURE(status)) return;
     if (unitsState == UnitsConvertibilityState::UNCONVERTIBLE) {
@@ -459,8 +492,15 @@
 
     if (result == 0)
         return 0.0; // If the result is zero, it does not matter if the conversion are reciprocal or not.
-    if (conversionRate_.reciprocal) { result = 1.0 / result; }
-    return result;
+    if (conversionRate_.reciprocal) {
+        result = 1.0 / result;
+    }
+
+    // TODO: remove the multiplication by 1.000,000,000,001 after using `decNumber`
+
+    // Multiply the result by 1.000,000,000,001 to fix the deterioration from using `double` (the
+    // deterioration is around 15 to 17 decimal digit).
+    return result * 1.000000000001;
 }
 
 U_NAMESPACE_END
diff --git a/icu4c/source/i18n/unitconverter.h b/icu4c/source/i18n/unitconverter.h
index 39a8e81..3b29a66 100644
--- a/icu4c/source/i18n/unitconverter.h
+++ b/icu4c/source/i18n/unitconverter.h
@@ -34,6 +34,21 @@
     UNCONVERTIBLE,
 };
 
+MeasureUnit U_I18N_API extractCompoundBaseUnit(const MeasureUnit &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.
+ */
 UnitsConvertibilityState U_I18N_API checkConvertibility(const MeasureUnit &source,
                                                         const MeasureUnit &target,
                                                         const ConversionRates &conversionRates,
@@ -41,8 +56,12 @@
 
 /**
  * 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 UnitConverter {
+class U_I18N_API UnitConverter : public UMemory {
   public:
     /**
      * Constructor of `UnitConverter`.
@@ -55,8 +74,8 @@
      * @param ratesInfo Contains all the needed conversion rates.
      * @param status
      */
-    UnitConverter(MeasureUnit source, MeasureUnit target,
-                  const ConversionRates &ratesInfo, UErrorCode &status);
+    UnitConverter(MeasureUnit source, MeasureUnit target, const ConversionRates &ratesInfo,
+                  UErrorCode &status);
 
     /**
      * Convert a value in the source unit to another value in the target unit.
diff --git a/icu4c/source/i18n/unitsdata.cpp b/icu4c/source/i18n/unitsdata.cpp
index 05d759b..1ac285f 100644
--- a/icu4c/source/i18n/unitsdata.cpp
+++ b/icu4c/source/i18n/unitsdata.cpp
@@ -5,6 +5,7 @@
 
 #if !UCONFIG_NO_FORMATTING
 
+#include "number_decimalquantity.h"
 #include "cstring.h"
 #include "number_decimalquantity.h"
 #include "resource.h"
@@ -16,7 +17,7 @@
 
 namespace {
 
-using number::impl::DecimalQuantity;
+using icu::number::impl::DecimalQuantity;
 
 void trimSpaces(CharString& factor, UErrorCode& status){
    CharString trimmed;
@@ -399,7 +400,7 @@
 
 // TODO: make outPreferences const?
 //
-// TODO: consider replacing `UnitPreference **&outPrefrences` with slice class
+// TODO: consider replacing `UnitPreference **&outPreferences` with slice class
 // of some kind.
 void U_I18N_API UnitPreferences::getPreferencesFor(StringPiece category, StringPiece usage,
                                                    StringPiece region,
diff --git a/icu4c/source/i18n/unitsdata.h b/icu4c/source/i18n/unitsdata.h
index 0acea7a..1ea8dee 100644
--- a/icu4c/source/i18n/unitsdata.h
+++ b/icu4c/source/i18n/unitsdata.h
@@ -171,7 +171,7 @@
      * result set.
      * @param status Receives status.
      *
-     * TODO(hugovdm): maybe replace `UnitPreference **&outPrefrences` with a slice class?
+     * TODO(hugovdm): maybe replace `UnitPreference **&outPreferences` with a slice class?
      */
     void getPreferencesFor(StringPiece category, StringPiece usage, StringPiece region,
                            const UnitPreference *const *&outPreferences, int32_t &preferenceCount,
diff --git a/icu4c/source/i18n/unitsrouter.cpp b/icu4c/source/i18n/unitsrouter.cpp
new file mode 100644
index 0000000..9f787ce
--- /dev/null
+++ b/icu4c/source/i18n/unitsrouter.cpp
@@ -0,0 +1,68 @@
+// © 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
+
+#include <stdio.h>
+#include <utility>
+
+#include "cmemory.h"
+#include "cstring.h"
+#include "number_decimalquantity.h"
+#include "resource.h"
+#include "unitconverter.h" // for extractCompoundBaseUnit
+#include "unitsdata.h"     // for getUnitCategory
+#include "unitsrouter.h"
+#include "uresimp.h"
+
+U_NAMESPACE_BEGIN
+
+UnitsRouter::UnitsRouter(MeasureUnit inputUnit, StringPiece region, StringPiece usage,
+                         UErrorCode &status) {
+    // TODO: do we want to pass in ConversionRates and UnitPreferences instead
+    // of loading in each UnitsRouter instance? (Or make global?)
+    ConversionRates conversionRates(status);
+    UnitPreferences prefs(status);
+
+    MeasureUnit baseUnit = extractCompoundBaseUnit(inputUnit, conversionRates, status);
+    CharString category = getUnitCategory(baseUnit.getIdentifier(), status);
+
+    const UnitPreference *const *unitPreferences;
+    int32_t preferencesCount;
+    prefs.getPreferencesFor(category.data(), usage, region, unitPreferences, preferencesCount, status);
+
+    for (int i = 0; i < preferencesCount; ++i) {
+        const auto &preference = *unitPreferences[i];
+
+        MeasureUnit complexTargetUnit = MeasureUnit::forIdentifier(preference.unit.data(), status);
+        if (U_FAILURE(status)) {
+            return;
+        }
+
+        converterPreferences_.emplaceBackAndCheckErrorCode(status, inputUnit, complexTargetUnit,
+                                                           preference.geq, conversionRates, status);
+        if (U_FAILURE(status)) {
+            return;
+        }
+    }
+}
+
+MaybeStackVector<Measure> UnitsRouter::route(double quantity, UErrorCode &status) {
+    for (int i = 0, n = converterPreferences_.length(); i < n; i++) {
+        const auto &converterPreference = *converterPreferences_[i];
+
+        if (converterPreference.converter.greaterThanOrEqual(quantity, converterPreference.limit)) {
+            return converterPreference.converter.convert(quantity, status);
+        }
+    }
+
+    // In case of the `quantity` does not fit in any converter limit, use the last converter.
+    const auto &lastConverter = (*converterPreferences_[converterPreferences_.length() - 1]).converter;
+    return lastConverter.convert(quantity, status);
+}
+
+U_NAMESPACE_END
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/i18n/unitsrouter.h b/icu4c/source/i18n/unitsrouter.h
new file mode 100644
index 0000000..1b29d9d
--- /dev/null
+++ b/icu4c/source/i18n/unitsrouter.h
@@ -0,0 +1,88 @@
+// © 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 __UNITSROUTER_H__
+#define __UNITSROUTER_H__
+
+#include <limits>
+
+#include "charstr.h" // CharString
+#include "cmemory.h"
+#include "complexunitsconverter.h"
+#include "unicode/errorcode.h"
+#include "unicode/measunit.h"
+#include "unicode/measure.h"
+#include "unicode/stringpiece.h"
+#include "unitsdata.h"
+
+U_NAMESPACE_BEGIN
+
+/**
+ * Contains the complex unit converter and the limit which representing the smallest value that the
+ * converter should accept. For example, if the converter is converting to `foot+inch` and the limit
+ * equals 3.0, thus means the converter should not convert to a value less than `3.0 feet`.
+ *
+ * NOTE:
+ *    if the limit doest not has a value `i.e. (std::numeric_limits<double>::lowest())`, this mean there
+ *    is no limit for the converter.
+ */
+struct ConverterPreference : UMemory {
+    ComplexUnitsConverter converter;
+    double limit;
+
+    ConverterPreference(MeasureUnit source, MeasureUnit complexTarget, double limit,
+                        const ConversionRates &ratesInfo, UErrorCode &status)
+        : converter(source, complexTarget, ratesInfo, status), limit(limit) {}
+
+    ConverterPreference(MeasureUnit source, MeasureUnit complexTarget, const ConversionRates &ratesInfo,
+                        UErrorCode &status)
+        : ConverterPreference(source, complexTarget, std::numeric_limits<double>::lowest(), ratesInfo,
+                              status) {}
+};
+
+/**
+ * `UnitsRouter` responsible for converting from a single unit (such as `meter` or `meter-per-second`) to
+ * one of the complex units based on the limits.
+ * For example:
+ *    if the input is `meter` and the output as following
+ *    {`foot+inch`, limit: 3.0}
+ *    {`inch`     , limit: no value (-inf)}
+ *    Thus means if the input in `meter` is greater than or equal to `3.0 feet`, the output will be in
+ *    `foot+inch`, otherwise, the output will be in `inch`.
+ *
+ * NOTE:
+ *    the output units and the their limits MUST BE in order, for example, if the output units, from the
+ *    previous example, are the following:
+ *        {`inch`     , limit: no value (-inf)}
+ *        {`foot+inch`, limit: 3.0}
+ *     IN THIS CASE THE OUTPUT WILL BE ALWAYS IN `inch`.
+ *
+ * NOTE:
+ *    the output units  and their limits will be extracted from the units preferences database by knowing
+ *    the followings:
+ *        - input unit
+ *        - locale
+ *        - usage
+ *
+ * DESIGN:
+ *    `UnitRouter` uses internally `ComplexUnitConverter` in order to convert the input units to the
+ *    desired complex units and to check the limit too.
+ */
+class U_I18N_API UnitsRouter {
+  public:
+    UnitsRouter(MeasureUnit inputUnit, StringPiece locale, StringPiece usage, UErrorCode &status);
+
+    MaybeStackVector<Measure> route(double quantity, UErrorCode &status);
+
+  private:
+    MaybeStackVector<ConverterPreference> converterPreferences_;
+};
+
+U_NAMESPACE_END
+
+#endif //__UNITSROUTER_H__
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/depstest/dependencies.txt b/icu4c/source/test/depstest/dependencies.txt
index e7db664..480f07f 100644
--- a/icu4c/source/test/depstest/dependencies.txt
+++ b/icu4c/source/test/depstest/dependencies.txt
@@ -1074,9 +1074,9 @@
     stringenumeration errorcode
 
 group: unitsformatter
-    unitsdata.o unitconverter.o
+    unitsdata.o unitconverter.o complexunitsconverter.o unitsrouter.o
   deps
-    resourcebundle units_extra double_conversion number_representation
+    resourcebundle units_extra double_conversion number_representation formattable
 
 group: decnumber
     decContext.o decNumber.o
diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in
index 74a6f90..cb356c4 100644
--- a/icu4c/source/test/intltest/Makefile.in
+++ b/icu4c/source/test/intltest/Makefile.in
@@ -69,7 +69,7 @@
 numbertest_parse.o numbertest_doubleconversion.o numbertest_skeletons.o \
 static_unisets_test.o numfmtdatadriventest.o numbertest_range.o erarulestest.o \
 formattedvaluetest.o formatted_string_builder_test.o numbertest_permutation.o \
-unitsdatatest.o unitstest.o
+unitsdatatest.o unitstest.o unitsroutertest.o
 
 DEPS = $(OBJECTS:.o=.d)
 
diff --git a/icu4c/source/test/intltest/intltest.vcxproj b/icu4c/source/test/intltest/intltest.vcxproj
index 4fdaa23..e6cee37 100644
--- a/icu4c/source/test/intltest/intltest.vcxproj
+++ b/icu4c/source/test/intltest/intltest.vcxproj
@@ -285,7 +285,9 @@
     <ClCompile Include="formattedvaluetest.cpp" />
     <ClCompile Include="localebuildertest.cpp" />
     <ClCompile Include="localematchertest.cpp" />
+    <ClCompile Include="unitsdatatest.cpp" />
     <ClCompile Include="unitstest.cpp" />
+    <ClCompile Include="unitsroutertest.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="colldata.h" />
diff --git a/icu4c/source/test/intltest/intltest.vcxproj.filters b/icu4c/source/test/intltest/intltest.vcxproj.filters
index f068ff8..bba4e92 100644
--- a/icu4c/source/test/intltest/intltest.vcxproj.filters
+++ b/icu4c/source/test/intltest/intltest.vcxproj.filters
@@ -550,9 +550,15 @@
     <ClCompile Include="localematchertest.cpp">
       <Filter>locales &amp; resources</Filter>
     </ClCompile>
+    <ClCompile Include="unitsdatatest.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
     <ClCompile Include="unitstest.cpp">
       <Filter>formatting</Filter>
     </ClCompile>
+    <ClCompile Include="unitsroutertest.cpp">
+      <Filter>formatting</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="itrbbi.h">
diff --git a/icu4c/source/test/intltest/itformat.cpp b/icu4c/source/test/intltest/itformat.cpp
index bd80488..a2b5ffe 100644
--- a/icu4c/source/test/intltest/itformat.cpp
+++ b/icu4c/source/test/intltest/itformat.cpp
@@ -76,6 +76,7 @@
 extern IntlTest *createStringSegmentTest();
 extern IntlTest *createUnitsDataTest();
 extern IntlTest *createUnitsTest();
+extern IntlTest *createUnitsRouterTest();
 
 
 #define TESTCLASS(id, TestClass)          \
@@ -267,6 +268,15 @@
             callTest(*test, par);
           }
           break;
+        case 58:
+          name = "UnitsRouterTest";
+          if (exec) {
+            logln("UnitsRouterTest test---");
+            logln((UnicodeString)"");
+            LocalPointer<IntlTest> test(createUnitsRouterTest());
+            callTest(*test, par);
+          }
+          break;
         default: name = ""; break; //needed to end loop
     }
     if (exec) {
diff --git a/icu4c/source/test/intltest/unitsdatatest.cpp b/icu4c/source/test/intltest/unitsdatatest.cpp
index 012784c..d79ca78 100644
--- a/icu4c/source/test/intltest/unitsdatatest.cpp
+++ b/icu4c/source/test/intltest/unitsdatatest.cpp
@@ -114,7 +114,7 @@
         {"GB area", "area", "default", "GB", "square-mile", "square-inch"},
         {"001 area geograph", "area", "geograph", "001", "square-kilometer", "square-kilometer"},
         {"GB area geograph", "area", "geograph", "GB", "square-mile", "square-mile"},
-        {"CA person-height", "length", "person-height", "CA", "foot-and-inch", "foot-and-inch"},
+        {"CA person-height", "length", "person-height", "CA", "foot-and-inch", "inch"},
         {"AT person-height", "length", "person-height", "AT", "meter-and-centimeter",
          "meter-and-centimeter"},
     };
diff --git a/icu4c/source/test/intltest/unitsroutertest.cpp b/icu4c/source/test/intltest/unitsroutertest.cpp
new file mode 100644
index 0000000..8d2489e
--- /dev/null
+++ b/icu4c/source/test/intltest/unitsroutertest.cpp
@@ -0,0 +1,33 @@
+// © 2020 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+
+#include "unicode/utypes.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "intltest.h"
+#include "unicode/unistr.h"
+#include "unitsrouter.h"
+
+
+class UnitsRouterTest : public IntlTest {
+  public:
+    UnitsRouterTest() {}
+
+    void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par = NULL);
+
+    void testBasic();
+};
+
+extern IntlTest *createUnitsRouterTest() { return new UnitsRouterTest(); }
+
+void UnitsRouterTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) {
+    if (exec) { logln("TestSuite UnitsRouterTest: "); }
+    TESTCASE_AUTO_BEGIN;
+    TESTCASE_AUTO(testBasic);
+    TESTCASE_AUTO_END;
+}
+
+void UnitsRouterTest::testBasic() { IcuTestErrorCode status(*this, "UnitsRouter testBasic"); }
+
+#endif /* #if !UCONFIG_NO_FORMATTING */
diff --git a/icu4c/source/test/intltest/unitstest.cpp b/icu4c/source/test/intltest/unitstest.cpp
index 4931bcd..cc31410 100644
--- a/icu4c/source/test/intltest/unitstest.cpp
+++ b/icu4c/source/test/intltest/unitstest.cpp
@@ -5,6 +5,9 @@
 
 #if !UCONFIG_NO_FORMATTING
 
+#include <cmath>
+#include <iostream>
+
 #include "charstr.h"
 #include "cmemory.h"
 #include "filestrm.h"
@@ -16,8 +19,16 @@
 #include "unicode/unum.h"
 #include "unitconverter.h"
 #include "unitsdata.h"
+#include "unitsrouter.h"
 #include "uparse.h"
 
+struct UnitConversionTestCase {
+    const StringPiece source;
+    const StringPiece target;
+    const double inputValue;
+    const double expectedValue;
+};
+
 using icu::number::impl::DecimalQuantity;
 
 class UnitsTest : public IntlTest {
@@ -29,7 +40,6 @@
     void testConversionCapability();
     void testConversions();
     void testPreferences();
-    void testBasic();
     void testSiPrefixes();
     void testMass();
     void testTemperature();
@@ -39,12 +49,13 @@
 extern IntlTest *createUnitsTest() { return new UnitsTest(); }
 
 void UnitsTest::runIndexedTest(int32_t index, UBool exec, const char *&name, char * /*par*/) {
-    if (exec) { logln("TestSuite UnitsTest: "); }
+    if (exec) {
+        logln("TestSuite UnitsTest: ");
+    }
     TESTCASE_AUTO_BEGIN;
     TESTCASE_AUTO(testConversionCapability);
     TESTCASE_AUTO(testConversions);
     TESTCASE_AUTO(testPreferences);
-    TESTCASE_AUTO(testBasic);
     TESTCASE_AUTO(testSiPrefixes);
     TESTCASE_AUTO(testMass);
     TESTCASE_AUTO(testTemperature);
@@ -81,38 +92,6 @@
     }
 }
 
-void UnitsTest::testBasic() {
-    IcuTestErrorCode status(*this, "Units testBasic");
-
-    // Test Cases
-    struct TestCase {
-        StringPiece source;
-        StringPiece target;
-        const double inputValue;
-        const double expectedValue;
-    } testCases[]{
-        {"meter", "foot", 1.0, 3.28084},     //
-        {"kilometer", "foot", 1.0, 3280.84}, //
-    };
-
-    for (const auto &testCase : testCases) {
-        UErrorCode status = U_ZERO_ERROR;
-
-        MeasureUnit source = MeasureUnit::forIdentifier(testCase.source, status);
-        MeasureUnit target = MeasureUnit::forIdentifier(testCase.target, status);
-
-        MaybeStackVector<MeasureUnit> units;
-        units.emplaceBack(source);
-        units.emplaceBack(target);
-
-        ConversionRates conversionRates(status);
-        UnitConverter converter(source, target, conversionRates, status);
-
-        assertEqualsNear("test conversion", testCase.expectedValue,
-                         converter.convert(testCase.inputValue), 0.001);
-    }
-}
-
 void UnitsTest::testSiPrefixes() {
     IcuTestErrorCode status(*this, "Units testSiPrefixes");
     // Test Cases
@@ -236,9 +215,17 @@
         const double inputValue;
         const double expectedValue;
     } testCases[]{
-        {"square-meter", "square-yard", 10.0, 11.9599}, //
-        {"hectare", "square-yard", 1.0, 11959.9},       //
-        {"square-mile", "square-foot", 0.0001, 2787.84} //
+        {"square-meter", "square-yard", 10.0, 11.9599},     //
+        {"hectare", "square-yard", 1.0, 11959.9},           //
+        {"square-mile", "square-foot", 0.0001, 2787.84},    //
+        {"hectare", "square-yard", 1.0, 11959.9},           //
+        {"hectare", "square-meter", 1.0, 10000},            //
+        {"hectare", "square-meter", 0.0, 0.0},              //
+        {"square-mile", "square-foot", 0.0001, 2787.84},    //
+        {"square-yard", "square-foot", 10, 90},             //
+        {"square-yard", "square-foot", 0, 0},               //
+        {"square-yard", "square-foot", 0.000001, 0.000009}, //
+        {"square-mile", "square-foot", 0.0, 0.0},           //
     };
 
     for (const auto &testCase : testCases) {
@@ -300,9 +287,11 @@
  * @param pErrorCode Receives status.
  */
 void unitsTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount, UErrorCode *pErrorCode) {
-    if (U_FAILURE(*pErrorCode)) { return; }
+    if (U_FAILURE(*pErrorCode)) {
+        return;
+    }
     UnitsTestContext *ctx = (UnitsTestContext *)context;
-    UnitsTest* unitsTest = ctx->unitsTest;
+    UnitsTest *unitsTest = ctx->unitsTest;
     (void)fieldCount; // unused UParseLineFn variable
     IcuTestErrorCode status(*unitsTest, "unitsTestDatalineFn");
 
@@ -312,16 +301,26 @@
     StringPiece commentConversionFormula = trimField(fields[3]);
     StringPiece utf8Expected = trimField(fields[4]);
 
-    UNumberFormat *nf = unum_open(UNUM_DEFAULT, NULL, -1, "en_US", NULL, pErrorCode);
+    UNumberFormat *nf = unum_open(UNUM_DEFAULT, NULL, -1, "en_US", NULL, status);
+    if (status.errIfFailureAndReset("unum_open failed")) {
+        return;
+    }
     UnicodeString uExpected = UnicodeString::fromUTF8(utf8Expected);
-    double expected = unum_parseDouble(nf, uExpected.getBuffer(), uExpected.length(), 0, pErrorCode);
+    double expected = unum_parseDouble(nf, uExpected.getBuffer(), uExpected.length(), 0, status);
     unum_close(nf);
+    if (status.errIfFailureAndReset("unum_parseDouble(\"%s\") failed", utf8Expected)) {
+        return;
+    }
 
     MeasureUnit sourceUnit = MeasureUnit::forIdentifier(x, status);
-    if (status.errIfFailureAndReset("forIdentifier(\"%.*s\")", x.length(), x.data())) { return; }
+    if (status.errIfFailureAndReset("forIdentifier(\"%.*s\")", x.length(), x.data())) {
+        return;
+    }
 
     MeasureUnit targetUnit = MeasureUnit::forIdentifier(y, status);
-    if (status.errIfFailureAndReset("forIdentifier(\"%.*s\")", y.length(), y.data())) { return; }
+    if (status.errIfFailureAndReset("forIdentifier(\"%.*s\")", y.length(), y.data())) {
+        return;
+    }
 
     unitsTest->logln("Quantity (Category): \"%.*s\", "
                      "Expected value of \"1000 %.*s in %.*s\": %f, "
@@ -340,7 +339,9 @@
         .append(sourceUnit.getIdentifier(), status)
         .append(" -> ", status)
         .append(targetUnit.getIdentifier(), status);
-    if (status.errIfFailureAndReset("msg construction")) { return; }
+    if (status.errIfFailureAndReset("msg construction")) {
+        return;
+    }
     unitsTest->assertNotEquals(msg.data(), UNCONVERTIBLE, convertibility);
 
     // Conversion:
@@ -393,7 +394,7 @@
  * find a decimal fraction for each output unit.
  */
 class ExpectedOutput {
-  private:
+  public:
     // Counts number of units in the output. When this is more than one, we have
     // "mixed units" in the expected output.
     int _compoundCount = 0;
@@ -408,7 +409,6 @@
     // The amounts of each of the output units.
     double _amounts[3];
 
-  public:
     /**
      * Parse an expected output field from the test data file.
      *
@@ -445,7 +445,7 @@
         _skippedFields++;
         if (_skippedFields < 2) {
             // We are happy skipping one field per output unit: we want to skip
-            // rational fraction fiels like "11 / 10".
+            // rational fraction fields like "11 / 10".
             errorCode = U_ZERO_ERROR;
             return;
         } else {
@@ -469,9 +469,55 @@
     }
 };
 
+// TODO(Hugo): Add a comment and Use AssertEqualsNear.
+void checkOutput(UnitsTest *unitsTest, const char *msg, ExpectedOutput expected,
+                 const MaybeStackVector<Measure> &actual, double precision) {
+    IcuTestErrorCode status(*unitsTest, "checkOutput");
+    bool success = true;
+    if (expected._compoundCount != actual.length()) {
+        success = false;
+    }
+    for (int i = 0; i < actual.length(); i++) {
+        if (i >= expected._compoundCount) {
+            break;
+        }
+
+        // assertEqualsNear("test conversion", expected._amounts[i],
+        //                 actual[i]->getNumber().getDouble(status), 0.0001);
+
+        double diff = std::abs(expected._amounts[i] - actual[i]->getNumber().getDouble(status));
+        double diffPercent = expected._amounts[i] != 0 ? diff / expected._amounts[i] : diff;
+        if (diffPercent > precision) {
+            success = false;
+            break;
+        }
+
+        if (expected._measureUnits[i] != actual[i]->getUnit()) {
+            success = false;
+            break;
+        }
+    }
+
+    CharString testMessage("test case: ", status);
+    testMessage.append(msg, status);
+    testMessage.append(", expected output: ", status);
+    testMessage.append(expected.toDebugString().c_str(), status);
+    testMessage.append(", obtained output:", status);
+    for (int i = 0; i < actual.length(); i++) {
+        testMessage.append(" ", status);
+        testMessage.append(std::to_string(actual[i]->getNumber().getDouble(status)), status);
+        testMessage.append(" ", status);
+        testMessage.appendInvariantChars(actual[i]->getUnit().getIdentifier(), status);
+    }
+
+    unitsTest->assertTrue(testMessage.data(), success);
+}
+
 /**
- * WIP(hugovdm): deals with a single data-driven unit test for unit preferences.
- * This is a UParseLineFn as required by u_parseDelimitedFile.
+ * Runs a single data-driven unit test for unit preferences.
+ *
+ * This is a UParseLineFn as required by u_parseDelimitedFile, intended for
+ * parsing unitPreferencesTest.txt.
  */
 void unitPreferencesTestDataLineFn(void *context, char *fields[][2], int32_t fieldCount,
                                    UErrorCode *pErrorCode) {
@@ -491,9 +537,9 @@
     // Unused // StringPiece inputR = trimField(fields[3]);
     StringPiece inputD = trimField(fields[4]);
     StringPiece inputUnit = trimField(fields[5]);
-    ExpectedOutput output;
+    ExpectedOutput expected;
     for (int i = 6; i < fieldCount; i++) {
-        output.parseOutputField(trimField(fields[i]), status);
+        expected.parseOutputField(trimField(fields[i]), status);
     }
     if (status.errIfFailureAndReset("parsing unitPreferencesTestData.txt test case: %s", fields[0][0])) {
         return;
@@ -512,25 +558,46 @@
         return;
     }
 
-    // WIP(hugovdm): hook this up to actual tests.
-    //
-    // Possible after merging in younies/tryingdouble:
-    // UnitConverter converter(sourceUnit, targetUnit, *pErrorCode);
-    // double got = converter.convert(1000, *pErrorCode);
-    // ((UnitsTest*)context)->assertEqualsNear(quantity.data(), expected, got, 0.0001);
-
     unitsTest->logln("Quantity (Category): \"%.*s\", Usage: \"%.*s\", Region: \"%.*s\", "
                      "Input: \"%f %s\", Expected Output: %s",
                      quantity.length(), quantity.data(), usage.length(), usage.data(), region.length(),
                      region.data(), inputAmount, inputMeasureUnit.getIdentifier(),
-                     output.toDebugString().c_str());
+                     expected.toDebugString().c_str());
+
+    if (U_FAILURE(status)) {
+        return;
+    }
+    UnitsRouter router(inputMeasureUnit, region, usage, status);
+    if (status.errIfFailureAndReset("UnitsRouter(<%s>, \"%.*s\", \"%.*s\", status)",
+                                    inputMeasureUnit.getIdentifier(), region.length(), region.data(),
+                                    usage.length(), usage.data())) {
+        return;
+    }
+
+    CharString msg(quantity, status);
+    msg.append(" ", status);
+    msg.append(usage, status);
+    msg.append(" ", status);
+    msg.append(region, status);
+    msg.append(" ", status);
+    msg.append(inputD, status);
+    msg.append(" ", status);
+    msg.append(inputMeasureUnit.getIdentifier(), status);
+    if (status.errIfFailureAndReset("Failure before router.route")) {
+        return;
+    }
+    MaybeStackVector<Measure> result = router.route(inputAmount, status);
+    if (status.errIfFailureAndReset("router.route(inputAmount, ...)")) {
+        return;
+    }
+    checkOutput(unitsTest, msg.data(), expected, result, 0.0001);
 }
 
 /**
  * Parses the format used by unitPreferencesTest.txt, calling lineFn for each
  * line.
  *
- * This is a modified version of u_parseDelimitedFile, customised for
+ * This is a modified version of u_parseDelimitedFile, customized for
  * unitPreferencesTest.txt, due to it having a variable number of fields per
  * line.
  */
@@ -542,7 +609,9 @@
     char *start, *limit;
     int32_t i;
 
-    if (U_FAILURE(*pErrorCode)) { return; }
+    if (U_FAILURE(*pErrorCode)) {
+        return;
+    }
 
     if (fields == NULL || lineFn == NULL || maxFieldCount <= 0) {
         *pErrorCode = U_ILLEGAL_ARGUMENT_ERROR;
@@ -568,7 +637,9 @@
         *pErrorCode = U_ZERO_ERROR;
 
         /* skip this line if it is empty or a comment */
-        if (*start == 0 || *start == '#') { continue; }
+        if (*start == 0 || *start == '#') {
+            continue;
+        }
 
         /* remove in-line comments */
         limit = uprv_strchr(start, '#');
@@ -583,7 +654,9 @@
         }
 
         /* skip lines with only whitespace */
-        if (u_skipWhitespace(start)[0] == 0) { continue; }
+        if (u_skipWhitespace(start)[0] == 0) {
+            continue;
+        }
 
         /* for each field, call the corresponding field function */
         for (i = 0; i < maxFieldCount; ++i) {
@@ -605,15 +678,21 @@
                 break;
             }
         }
-        if (i == maxFieldCount) { *pErrorCode = U_PARSE_ERROR; }
+        if (i == maxFieldCount) {
+            *pErrorCode = U_PARSE_ERROR;
+        }
         int fieldCount = i + 1;
 
         /* call the field function */
         lineFn(context, fields, fieldCount, pErrorCode);
-        if (U_FAILURE(*pErrorCode)) { break; }
+        if (U_FAILURE(*pErrorCode)) {
+            break;
+        }
     }
 
-    if (filename != NULL) { T_FileStream_close(file); }
+    if (filename != NULL) {
+        T_FileStream_close(file);
+    }
 }
 
 /**