1 /**
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  *      http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package com.android.healthconnect.controller.dataentries.units
15 
16 import kotlin.math.roundToInt
17 
18 object WeightConverter {
19     private const val POUNDS_PER_STONE = 14
20 
21     private const val KG_PER_POUND = 0.453592
22     private const val KG_PER_STONE = 6.35029
23     private const val POUND_PER_KG = 2.20462
24     private const val STONE_PER_KG = 0.157473
25 
26     private const val MAX_STONE_TO_KG_INPUT = Double.MAX_VALUE / KG_PER_STONE
27     private const val MAX_KG_TO_POUND_INPUT = Double.MAX_VALUE / POUND_PER_KG
28 
29     /**
30      * Converts from kilograms to the provided units
31      *
32      * @param toUnit the units type to convert the passed in sourceKilograms (kg) to
33      * @param sourceKilograms the kg weight to convert to toUnit weight
34      * @return the sourceKilograms in toUnit weight units
35      */
convertFromKilogramsnull36     fun convertFromKilograms(toUnit: WeightUnit, sourceKilograms: Double): Double {
37         return when (toUnit) {
38             WeightUnit.KILOGRAM -> keepOneDecimal(sourceKilograms)
39             WeightUnit.POUND -> {
40                 require(
41                     !(sourceKilograms > MAX_KG_TO_POUND_INPUT ||
42                         sourceKilograms < -MAX_KG_TO_POUND_INPUT)) {
43                         "Kilogram input out of range: $sourceKilograms"
44                     }
45                 keepOneDecimal(sourceKilograms * POUND_PER_KG)
46             }
47             WeightUnit.STONE -> keepOneDecimal(sourceKilograms * STONE_PER_KG)
48         }
49     }
50 
51     /**
52      * Converts from grams to the provided units
53      *
54      * @param toUnit the units type to convert the passed in sourceGrams (g) to
55      * @param sourceGrams the gram weight to convert to toUnit weight
56      * @return the sourceGrams in toUnit weight units
57      */
convertFromGramsnull58     fun convertFromGrams(toUnit: WeightUnit, sourceGrams: Double): Double {
59         return convertFromKilograms(toUnit, sourceGrams / 1000)
60     }
61 
62     /**
63      * Converts from pounds to stones and pounds.
64      *
65      * @param weight the weight in pounds to convert
66      * @param significantDigits the number of significant digits to round the pounds to
67      */
stonePoundsFromPoundsnull68     fun stonePoundsFromPounds(weight: Double, significantDigits: Int): StonePounds {
69         var stone = (weight / POUNDS_PER_STONE).toInt()
70         val pow = Math.pow(10.0, significantDigits.toDouble())
71         var pound = keepOneDecimal((weight % POUNDS_PER_STONE * pow).roundToInt() / pow)
72         if (pound == POUNDS_PER_STONE.toDouble()) {
73             pound = 0.0
74             stone++
75         }
76         return StonePounds(stone, pound)
77     }
78 
keepOneDecimalnull79     private fun keepOneDecimal(weight: Double): Double {
80         return (weight * 10).roundToInt() / 10.0
81     }
82 }
83 
84 /**
85  * Encapsulates the result of a conversion to UK imperial units; whole stones and fractional pounds.
86  *
87  * @param stone Stone value (14 pounds).
88  * @param pounds Pounds (between zero and 14).
89  */
90 data class StonePounds(val stone: Int, val pounds: Double)
91