mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-18 16:25:27 +02:00
Refactor BigDecimalUtils
This commit is contained in:
parent
60e5f1f998
commit
9610b5bc38
@ -24,7 +24,6 @@ import java.math.RoundingMode
|
||||
import kotlin.math.floor
|
||||
import kotlin.math.log10
|
||||
|
||||
// TODO Use everywhere
|
||||
fun BigDecimal.format(
|
||||
scale: Int,
|
||||
outputFormat: Int
|
||||
@ -35,25 +34,43 @@ fun BigDecimal.format(
|
||||
.toStringWith(outputFormat)
|
||||
}
|
||||
|
||||
// TODO Move tests and mark as internal
|
||||
/**
|
||||
* Shorthand function to use correct `toString` method according to [outputFormat].
|
||||
* Removes all trailing zeroes.
|
||||
*/
|
||||
fun BigDecimal.toStringWith(outputFormat: Int): String {
|
||||
// Setting result value using a specified OutputFormat
|
||||
return when (outputFormat) {
|
||||
OutputFormat.ALLOW_ENGINEERING -> this.toString()
|
||||
OutputFormat.FORCE_ENGINEERING -> this.toEngineeringString()
|
||||
else -> this.toPlainString()
|
||||
}
|
||||
fun BigDecimal.trimZeros(): BigDecimal {
|
||||
return if (this.isEqualTo(BigDecimal.ZERO)) BigDecimal.ZERO else this.stripTrailingZeros()
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses [compareTo], ignores scale differences.
|
||||
*
|
||||
* @param bd BigDecimal to which this BigDecimal is to be compared.
|
||||
* @return `true` if [compareTo] returned 0
|
||||
*/
|
||||
fun BigDecimal.isEqualTo(bd: BigDecimal): Boolean = compareTo(bd) == 0
|
||||
|
||||
/**
|
||||
* Uses [compareTo], ignores scale differences.
|
||||
*
|
||||
* @param bd BigDecimal to which this BigDecimal is to be compared.
|
||||
* @return `true` if [compareTo] returned 1
|
||||
*/
|
||||
fun BigDecimal.isGreaterThan(bd: BigDecimal): Boolean = compareTo(bd) == 1
|
||||
|
||||
/**
|
||||
* Uses [compareTo], ignores scale differences.
|
||||
*
|
||||
* @param bd BigDecimal to which this BigDecimal is to be compared.
|
||||
* @return `true` if [compareTo] returned -1
|
||||
*/
|
||||
fun BigDecimal.isLessThan(bd: BigDecimal): Boolean = compareTo(bd) == -1
|
||||
|
||||
/**
|
||||
* Sets the minimum scale that is required to get first non zero value in fractional part
|
||||
*
|
||||
* @param[prefScale] Is the preferred scale, the one which will be compared against
|
||||
*/
|
||||
fun BigDecimal.setMinimumRequiredScale(prefScale: Int): BigDecimal {
|
||||
internal fun BigDecimal.setMinimumRequiredScale(prefScale: Int): BigDecimal {
|
||||
/**
|
||||
* Here we are getting the amount of zeros in fractional part before non zero value
|
||||
* For example, for 0.00000123456 we need the length of 00000
|
||||
@ -75,10 +92,13 @@ fun BigDecimal.setMinimumRequiredScale(prefScale: Int): BigDecimal {
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all trailing zeroes.
|
||||
* Shorthand function to use correct `toString` method according to [outputFormat].
|
||||
*/
|
||||
fun BigDecimal.trimZeros(): BigDecimal {
|
||||
return if (this.isEqualTo(BigDecimal.ZERO)) BigDecimal.ZERO else this.stripTrailingZeros()
|
||||
private fun BigDecimal.toStringWith(outputFormat: Int): String {
|
||||
// Setting result value using a specified OutputFormat
|
||||
return when (outputFormat) {
|
||||
OutputFormat.ALLOW_ENGINEERING -> this.toString()
|
||||
OutputFormat.FORCE_ENGINEERING -> this.toEngineeringString()
|
||||
else -> this.toPlainString()
|
||||
}
|
||||
}
|
||||
|
||||
fun BigDecimal.isEqualTo(bd: BigDecimal): Boolean = compareTo(bd) == 0
|
||||
|
@ -16,9 +16,8 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.sadellie.unitto.data.units
|
||||
package com.sadellie.unitto.data.common
|
||||
|
||||
import com.sadellie.unitto.data.common.setMinimumRequiredScale
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import java.math.BigDecimal
|
@ -19,6 +19,7 @@
|
||||
package com.sadellie.unitto.data.model.unit
|
||||
|
||||
import com.sadellie.unitto.core.base.MAX_PRECISION
|
||||
import com.sadellie.unitto.data.common.isEqualTo
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
@ -35,7 +36,7 @@ data class FuelBackward(
|
||||
) : DefaultUnit {
|
||||
override fun convert(unitTo: DefaultUnit, value: BigDecimal): BigDecimal {
|
||||
// Avoid division by zero
|
||||
if (unitTo.basicUnit.compareTo(BigDecimal.ZERO) == 0) return BigDecimal.ZERO
|
||||
if (unitTo.basicUnit.isEqualTo(BigDecimal.ZERO)) return BigDecimal.ZERO
|
||||
|
||||
return when (unitTo) {
|
||||
is FuelForward -> this
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.sadellie.unitto.data.model.unit
|
||||
|
||||
import com.sadellie.unitto.core.base.MAX_PRECISION
|
||||
import com.sadellie.unitto.data.common.isEqualTo
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
@ -35,7 +36,7 @@ data class FuelForward(
|
||||
) : DefaultUnit {
|
||||
override fun convert(unitTo: DefaultUnit, value: BigDecimal): BigDecimal {
|
||||
// Avoid division by zero
|
||||
if (unitTo.basicUnit.compareTo(BigDecimal.ZERO) == 0) return BigDecimal.ZERO
|
||||
if (unitTo.basicUnit.isEqualTo(BigDecimal.ZERO)) return BigDecimal.ZERO
|
||||
|
||||
return when (unitTo) {
|
||||
is FuelForward -> this
|
||||
|
@ -19,6 +19,7 @@
|
||||
package com.sadellie.unitto.data.model.unit
|
||||
|
||||
import com.sadellie.unitto.core.base.MAX_PRECISION
|
||||
import com.sadellie.unitto.data.common.isEqualTo
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
@ -35,7 +36,7 @@ data class NormalUnit(
|
||||
) : DefaultUnit {
|
||||
override fun convert(unitTo: DefaultUnit, value: BigDecimal): BigDecimal {
|
||||
// Avoid division by zero
|
||||
if (unitTo.basicUnit.compareTo(BigDecimal.ZERO) == 0) return BigDecimal.ZERO
|
||||
if (unitTo.basicUnit.isEqualTo(BigDecimal.ZERO)) return BigDecimal.ZERO
|
||||
|
||||
return this
|
||||
.basicUnit
|
||||
|
@ -20,8 +20,8 @@ package com.sadellie.unitto.data.units
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Room
|
||||
import com.sadellie.unitto.data.common.setMinimumRequiredScale
|
||||
import com.sadellie.unitto.data.common.trimZeros
|
||||
import com.sadellie.unitto.core.base.OutputFormat
|
||||
import com.sadellie.unitto.data.common.format
|
||||
import com.sadellie.unitto.data.database.UnittoDatabase
|
||||
import com.sadellie.unitto.data.model.UnitGroup
|
||||
import com.sadellie.unitto.data.model.unit.DefaultUnit
|
||||
@ -42,7 +42,7 @@ class AllUnitsTest {
|
||||
private var history: MutableMap<UnitGroup, Set<String>> = mutableMapOf()
|
||||
private val mContext: Context = RuntimeEnvironment.getApplication().applicationContext
|
||||
private val database = Room.inMemoryDatabaseBuilder(mContext, UnittoDatabase::class.java).build()
|
||||
private val allUnitsRepository = UnitsRepository(
|
||||
private val allUnitsRepository = UnitsRepositoryImpl(
|
||||
unitsDao = database.unitsDao(),
|
||||
currencyRatesDao = database.currencyRatesDao(),
|
||||
mContext = mContext
|
||||
@ -550,14 +550,10 @@ class AllUnitsTest {
|
||||
UnitGroup.NUMBER_BASE -> (unitFrom as NumberBaseUnit).convert((unitTo as NumberBaseUnit), value)
|
||||
UnitGroup.FLOW_RATE -> (unitFrom as ReverseUnit)
|
||||
.convert((unitTo as DefaultUnit), BigDecimal(value))
|
||||
.setMinimumRequiredScale(5)
|
||||
.trimZeros()
|
||||
.toPlainString()
|
||||
.format(5, OutputFormat.PLAIN)
|
||||
else -> (unitFrom as DefaultUnit)
|
||||
.convert((unitTo as DefaultUnit), BigDecimal(value))
|
||||
.setMinimumRequiredScale(5)
|
||||
.trimZeros()
|
||||
.toPlainString()
|
||||
.format(5, OutputFormat.PLAIN)
|
||||
}
|
||||
assertEquals("Failed at $this to $checkingId", expected, actual)
|
||||
println("PASSED: $this -> $expected == $actual")
|
||||
|
@ -29,11 +29,9 @@ import com.sadellie.unitto.core.ui.common.textfield.addBracket
|
||||
import com.sadellie.unitto.core.ui.common.textfield.addTokens
|
||||
import com.sadellie.unitto.core.ui.common.textfield.deleteTokens
|
||||
import com.sadellie.unitto.core.ui.common.textfield.getTextField
|
||||
import com.sadellie.unitto.data.common.format
|
||||
import com.sadellie.unitto.data.common.isExpression
|
||||
import com.sadellie.unitto.data.common.setMinimumRequiredScale
|
||||
import com.sadellie.unitto.data.common.stateIn
|
||||
import com.sadellie.unitto.data.common.toStringWith
|
||||
import com.sadellie.unitto.data.common.trimZeros
|
||||
import com.sadellie.unitto.data.model.repository.CalculatorHistoryRepository
|
||||
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
@ -104,9 +102,7 @@ internal class CalculatorViewModel @Inject constructor(
|
||||
input = ui.input.text,
|
||||
radianMode = ui.radianMode,
|
||||
)
|
||||
.setMinimumRequiredScale(ui.precision)
|
||||
.trimZeros()
|
||||
.toStringWith(ui.outputFormat)
|
||||
.format(ui.precision, ui.outputFormat)
|
||||
)
|
||||
} catch (e: ExpressionException.DivideByZero) {
|
||||
CalculationResult.Empty
|
||||
@ -198,10 +194,7 @@ internal class CalculatorViewModel @Inject constructor(
|
||||
|
||||
_equalClicked.update { true }
|
||||
|
||||
val resultFormatted = result
|
||||
.setMinimumRequiredScale(prefs.precision)
|
||||
.trimZeros()
|
||||
.toStringWith(prefs.outputFormat)
|
||||
val resultFormatted = result.format(prefs.precision, prefs.outputFormat)
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
calculatorHistoryRepository.add(
|
||||
|
@ -24,6 +24,9 @@ import com.sadellie.unitto.core.base.R
|
||||
import com.sadellie.unitto.core.base.Token
|
||||
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
||||
import com.sadellie.unitto.core.ui.common.textfield.formatExpression
|
||||
import com.sadellie.unitto.data.common.isEqualTo
|
||||
import com.sadellie.unitto.data.common.isGreaterThan
|
||||
import com.sadellie.unitto.data.common.isLessThan
|
||||
import com.sadellie.unitto.data.common.trimZeros
|
||||
import com.sadellie.unitto.data.model.unit.DefaultUnit
|
||||
import com.sadellie.unitto.data.model.unit.NumberBaseUnit
|
||||
@ -62,7 +65,7 @@ internal sealed class ConverterResult {
|
||||
data class Default(val value: BigDecimal) : ConverterResult() {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is Default) return false
|
||||
return this.value.compareTo(other.value) == 0
|
||||
return this.value.isEqualTo(other.value)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int = value.hashCode()
|
||||
@ -90,35 +93,35 @@ internal sealed class ConverterResult {
|
||||
internal fun ConverterResult.Time.format(mContext: Context, formatterSymbols: FormatterSymbols): String {
|
||||
val result = mutableListOf<String>()
|
||||
|
||||
if (day.compareTo(BigDecimal.ZERO) == 1) {
|
||||
if (day.isGreaterThan(BigDecimal.ZERO)) {
|
||||
result += "${day.toPlainString().formatExpression(formatterSymbols)}${mContext.getString(R.string.unit_day_short)}"
|
||||
}
|
||||
|
||||
if (hour.compareTo(BigDecimal.ZERO) == 1) {
|
||||
if (hour.isGreaterThan(BigDecimal.ZERO)) {
|
||||
result += "${hour.toPlainString().formatExpression(formatterSymbols)}${mContext.getString(R.string.unit_hour_short)}"
|
||||
}
|
||||
|
||||
if (minute.compareTo(BigDecimal.ZERO) == 1) {
|
||||
if (minute.isGreaterThan(BigDecimal.ZERO)) {
|
||||
result += "${minute.toPlainString().formatExpression(formatterSymbols)}${mContext.getString(R.string.unit_minute_short)}"
|
||||
}
|
||||
|
||||
if (second.compareTo(BigDecimal.ZERO) == 1) {
|
||||
if (second.isGreaterThan(BigDecimal.ZERO)) {
|
||||
result += "${second.toPlainString().formatExpression(formatterSymbols)}${mContext.getString(R.string.unit_second_short)}"
|
||||
}
|
||||
|
||||
if (millisecond.compareTo(BigDecimal.ZERO) == 1) {
|
||||
if (millisecond.isGreaterThan(BigDecimal.ZERO)) {
|
||||
result += "${millisecond.toPlainString().formatExpression(formatterSymbols)}${mContext.getString(R.string.unit_millisecond_short)}"
|
||||
}
|
||||
|
||||
if (microsecond.compareTo(BigDecimal.ZERO) == 1) {
|
||||
if (microsecond.isGreaterThan(BigDecimal.ZERO)) {
|
||||
result += "${microsecond.toPlainString().formatExpression(formatterSymbols)}${mContext.getString(R.string.unit_microsecond_short)}"
|
||||
}
|
||||
|
||||
if (nanosecond.compareTo(BigDecimal.ZERO) == 1) {
|
||||
if (nanosecond.isGreaterThan(BigDecimal.ZERO)) {
|
||||
result += "${nanosecond.toPlainString().formatExpression(formatterSymbols)}${mContext.getString(R.string.unit_nanosecond_short)}"
|
||||
}
|
||||
|
||||
if (attosecond.compareTo(BigDecimal.ZERO) == 1) {
|
||||
if (attosecond.isGreaterThan(BigDecimal.ZERO)) {
|
||||
result += "${attosecond.toPlainString().formatExpression(formatterSymbols)}${mContext.getString(R.string.unit_attosecond_short)}"
|
||||
}
|
||||
|
||||
@ -131,7 +134,7 @@ internal fun formatTime(
|
||||
val negative = input < BigDecimal.ZERO
|
||||
val inputAbs = input.abs()
|
||||
|
||||
if (inputAbs.compareTo(attosecondBasicUnit) == -1) return ConverterResult.Time(
|
||||
if (inputAbs.isLessThan(attosecondBasicUnit)) return ConverterResult.Time(
|
||||
negative = negative,
|
||||
day = BigDecimal.ZERO,
|
||||
hour = BigDecimal.ZERO,
|
||||
@ -143,7 +146,7 @@ internal fun formatTime(
|
||||
attosecond = inputAbs
|
||||
)
|
||||
|
||||
if (inputAbs.compareTo(nanosecondBasicUnit) == -1) return ConverterResult.Time(
|
||||
if (inputAbs.isLessThan(nanosecondBasicUnit)) return ConverterResult.Time(
|
||||
negative = negative,
|
||||
day = BigDecimal.ZERO,
|
||||
hour = BigDecimal.ZERO,
|
||||
|
@ -24,10 +24,8 @@ import com.sadellie.unitto.core.base.MAX_PRECISION
|
||||
import com.sadellie.unitto.core.ui.common.textfield.AllFormatterSymbols
|
||||
import com.sadellie.unitto.core.ui.common.textfield.FormatterSymbols
|
||||
import com.sadellie.unitto.core.ui.common.textfield.formatExpression
|
||||
import com.sadellie.unitto.data.common.setMinimumRequiredScale
|
||||
import com.sadellie.unitto.data.common.format
|
||||
import com.sadellie.unitto.data.common.stateIn
|
||||
import com.sadellie.unitto.data.common.toStringWith
|
||||
import com.sadellie.unitto.data.common.trimZeros
|
||||
import com.sadellie.unitto.data.model.repository.UserPreferencesRepository
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@ -78,9 +76,7 @@ class FormattingViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
return BigDecimal(bigD)
|
||||
.setMinimumRequiredScale(precision)
|
||||
.trimZeros()
|
||||
.toStringWith(outputFormat)
|
||||
.format(precision, outputFormat)
|
||||
.formatExpression(formatterSymbols)
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user