Refactor BigDecimalUtils

This commit is contained in:
Sad Ellie 2023-11-20 16:14:45 +03:00
parent 60e5f1f998
commit 9610b5bc38
9 changed files with 67 additions and 57 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")

View File

@ -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(

View File

@ -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,

View File

@ -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)
}