mirror of
https://github.com/Myzel394/NumberHub.git
synced 2025-06-18 16:25:27 +02:00
Refactor Calculator
- no longer abusing mapLatest and StateFlow
This commit is contained in:
parent
eb96868afc
commit
d34c01ed6a
@ -26,6 +26,7 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -43,7 +44,7 @@ class CalculatorHistoryRepositoryImpl @Inject constructor(
|
|||||||
override suspend fun add(
|
override suspend fun add(
|
||||||
expression: String,
|
expression: String,
|
||||||
result: String,
|
result: String,
|
||||||
) {
|
) = withContext(Dispatchers.IO) {
|
||||||
calculatorHistoryDao.insert(
|
calculatorHistoryDao.insert(
|
||||||
CalculatorHistoryEntity(
|
CalculatorHistoryEntity(
|
||||||
timestamp = System.currentTimeMillis(),
|
timestamp = System.currentTimeMillis(),
|
||||||
@ -53,16 +54,16 @@ class CalculatorHistoryRepositoryImpl @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun delete(item: HistoryItem) {
|
override suspend fun delete(item: HistoryItem) = withContext(Dispatchers.IO) {
|
||||||
calculatorHistoryDao.delete(item.id)
|
calculatorHistoryDao.delete(item.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun clear() {
|
override suspend fun clear() = withContext(Dispatchers.IO) {
|
||||||
calculatorHistoryDao.clear()
|
calculatorHistoryDao.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<CalculatorHistoryEntity>.toHistoryItemList(): List<HistoryItem> {
|
private suspend fun List<CalculatorHistoryEntity>.toHistoryItemList(): List<HistoryItem> = withContext(Dispatchers.Default) {
|
||||||
return this.map {
|
this@toHistoryItemList.map {
|
||||||
HistoryItem(
|
HistoryItem(
|
||||||
id = it.entityId,
|
id = it.entityId,
|
||||||
date = Date(it.timestamp),
|
date = Date(it.timestamp),
|
||||||
|
@ -332,7 +332,7 @@ private fun PreviewCalculatorScreen() {
|
|||||||
Ready(
|
Ready(
|
||||||
uiState = CalculatorUIState.Ready(
|
uiState = CalculatorUIState.Ready(
|
||||||
input = TextFieldValue("1.2345"),
|
input = TextFieldValue("1.2345"),
|
||||||
output = CalculationResult.Default("1234"),
|
output = CalculationResult.Success("1234"),
|
||||||
radianMode = false,
|
radianMode = false,
|
||||||
precision = 3,
|
precision = 3,
|
||||||
outputFormat = OutputFormat.PLAIN,
|
outputFormat = OutputFormat.PLAIN,
|
||||||
|
@ -42,9 +42,7 @@ internal sealed class CalculatorUIState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
sealed class CalculationResult {
|
sealed class CalculationResult {
|
||||||
data class Default(val text: String) : CalculationResult()
|
data class Success(val text: String) : CalculationResult()
|
||||||
|
|
||||||
data class Fraction(val text: String) : CalculationResult()
|
|
||||||
|
|
||||||
data object Empty : CalculationResult()
|
data object Empty : CalculationResult()
|
||||||
|
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.feature.calculator
|
package com.sadellie.unitto.feature.calculator
|
||||||
|
|
||||||
import androidx.compose.ui.text.TextRange
|
|
||||||
import androidx.compose.ui.text.input.TextFieldValue
|
import androidx.compose.ui.text.input.TextFieldValue
|
||||||
import androidx.lifecycle.SavedStateHandle
|
import androidx.lifecycle.SavedStateHandle
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
@ -31,6 +30,7 @@ import com.sadellie.unitto.core.ui.common.textfield.getTextField
|
|||||||
import com.sadellie.unitto.core.ui.common.textfield.placeCursorAtTheEnd
|
import com.sadellie.unitto.core.ui.common.textfield.placeCursorAtTheEnd
|
||||||
import com.sadellie.unitto.data.common.format
|
import com.sadellie.unitto.data.common.format
|
||||||
import com.sadellie.unitto.data.common.isExpression
|
import com.sadellie.unitto.data.common.isExpression
|
||||||
|
import com.sadellie.unitto.data.common.isGreaterThan
|
||||||
import com.sadellie.unitto.data.common.stateIn
|
import com.sadellie.unitto.data.common.stateIn
|
||||||
import com.sadellie.unitto.data.model.HistoryItem
|
import com.sadellie.unitto.data.model.HistoryItem
|
||||||
import com.sadellie.unitto.data.model.repository.CalculatorHistoryRepository
|
import com.sadellie.unitto.data.model.repository.CalculatorHistoryRepository
|
||||||
@ -40,10 +40,10 @@ import io.github.sadellie.evaluatto.Expression
|
|||||||
import io.github.sadellie.evaluatto.ExpressionException
|
import io.github.sadellie.evaluatto.ExpressionException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -57,13 +57,13 @@ internal class CalculatorViewModel @Inject constructor(
|
|||||||
private val calculatorHistoryRepository: CalculatorHistoryRepository,
|
private val calculatorHistoryRepository: CalculatorHistoryRepository,
|
||||||
private val savedStateHandle: SavedStateHandle,
|
private val savedStateHandle: SavedStateHandle,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
private var calculationJob: Job? = null
|
||||||
|
|
||||||
private val inputKey = "CALCULATOR_INPUT"
|
private val inputKey = "CALCULATOR_INPUT"
|
||||||
private val input = MutableStateFlow(savedStateHandle.getTextField(inputKey))
|
private val input = MutableStateFlow(savedStateHandle.getTextField(inputKey))
|
||||||
private val result = MutableStateFlow<CalculationResult>(CalculationResult.Empty)
|
private val result = MutableStateFlow<CalculationResult>(CalculationResult.Empty)
|
||||||
private val equalClicked = MutableStateFlow(false)
|
private val equalClicked = MutableStateFlow(false)
|
||||||
private val prefs = userPrefsRepository.calculatorPrefs
|
private val prefs = userPrefsRepository.calculatorPrefs.stateIn(viewModelScope, null)
|
||||||
.stateIn(viewModelScope, null)
|
|
||||||
private var fractionJob: Job? = null
|
|
||||||
|
|
||||||
val uiState: StateFlow<CalculatorUIState> = combine(
|
val uiState: StateFlow<CalculatorUIState> = combine(
|
||||||
input,
|
input,
|
||||||
@ -88,33 +88,6 @@ internal class CalculatorViewModel @Inject constructor(
|
|||||||
partialHistoryView = prefs.partialHistoryView,
|
partialHistoryView = prefs.partialHistoryView,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.mapLatest { ui ->
|
|
||||||
if (ui !is CalculatorUIState.Ready) return@mapLatest ui
|
|
||||||
if (equalClicked.value) return@mapLatest ui
|
|
||||||
|
|
||||||
if (!ui.input.text.isExpression()) {
|
|
||||||
result.update { CalculationResult.Empty }
|
|
||||||
return@mapLatest ui
|
|
||||||
}
|
|
||||||
|
|
||||||
result.update {
|
|
||||||
try {
|
|
||||||
CalculationResult.Default(
|
|
||||||
calculate(
|
|
||||||
input = ui.input.text,
|
|
||||||
radianMode = ui.radianMode,
|
|
||||||
)
|
|
||||||
.format(ui.precision, ui.outputFormat),
|
|
||||||
)
|
|
||||||
} catch (e: ExpressionException.DivideByZero) {
|
|
||||||
CalculationResult.Empty
|
|
||||||
} catch (e: Exception) {
|
|
||||||
CalculationResult.Empty
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ui
|
|
||||||
}
|
|
||||||
.stateIn(viewModelScope, CalculatorUIState.Loading)
|
.stateIn(viewModelScope, CalculatorUIState.Loading)
|
||||||
|
|
||||||
fun addTokens(tokens: String) {
|
fun addTokens(tokens: String) {
|
||||||
@ -150,14 +123,15 @@ internal class CalculatorViewModel @Inject constructor(
|
|||||||
fun clearInput() = updateInput(TextFieldValue())
|
fun clearInput() = updateInput(TextFieldValue())
|
||||||
|
|
||||||
fun updateInput(value: TextFieldValue) {
|
fun updateInput(value: TextFieldValue) {
|
||||||
fractionJob?.cancel()
|
|
||||||
equalClicked.update { false }
|
equalClicked.update { false }
|
||||||
input.update { value }
|
input.update { value }
|
||||||
savedStateHandle[inputKey] = value.text
|
savedStateHandle[inputKey] = value.text
|
||||||
|
calculateInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateRadianMode(newValue: Boolean) = viewModelScope.launch {
|
fun updateRadianMode(newValue: Boolean) = viewModelScope.launch {
|
||||||
userPrefsRepository.updateRadianMode(newValue)
|
userPrefsRepository.updateRadianMode(newValue)
|
||||||
|
calculateInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateAdditionalButtons(newValue: Boolean) = viewModelScope.launch {
|
fun updateAdditionalButtons(newValue: Boolean) = viewModelScope.launch {
|
||||||
@ -168,54 +142,71 @@ internal class CalculatorViewModel @Inject constructor(
|
|||||||
userPrefsRepository.updateInverseMode(newValue)
|
userPrefsRepository.updateInverseMode(newValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun clearHistory() = viewModelScope.launch(Dispatchers.IO) {
|
fun clearHistory() = viewModelScope.launch {
|
||||||
calculatorHistoryRepository.clear()
|
calculatorHistoryRepository.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteHistoryItem(item: HistoryItem) = viewModelScope.launch(Dispatchers.IO) {
|
fun deleteHistoryItem(item: HistoryItem) = viewModelScope.launch {
|
||||||
calculatorHistoryRepository.delete(item)
|
calculatorHistoryRepository.delete(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun equal() = viewModelScope.launch {
|
fun equal() = viewModelScope.launch {
|
||||||
val prefs = prefs.value ?: return@launch
|
val prefs = prefs.value ?: return@launch
|
||||||
|
val inputValue = input.value.text
|
||||||
if (equalClicked.value) return@launch
|
if (equalClicked.value) return@launch
|
||||||
if (!input.value.text.isExpression()) return@launch
|
if (!inputValue.isExpression()) return@launch
|
||||||
|
|
||||||
val result = try {
|
val calculated = try {
|
||||||
calculate(input.value.text, prefs.radianMode, RoundingMode.DOWN)
|
calculate(inputValue, prefs.radianMode, RoundingMode.HALF_EVEN)
|
||||||
|
.format(prefs.precision, prefs.outputFormat)
|
||||||
} catch (e: ExpressionException.DivideByZero) {
|
} catch (e: ExpressionException.DivideByZero) {
|
||||||
equalClicked.update { true }
|
|
||||||
result.update { CalculationResult.DivideByZeroError }
|
result.update { CalculationResult.DivideByZeroError }
|
||||||
return@launch
|
return@launch
|
||||||
} catch (e: ExpressionException.FactorialCalculation) {
|
|
||||||
equalClicked.update { true }
|
|
||||||
result.update { CalculationResult.Error }
|
|
||||||
return@launch
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
equalClicked.update { true }
|
|
||||||
result.update { CalculationResult.Error }
|
result.update { CalculationResult.Error }
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val fractional = try {
|
||||||
|
calculate(inputValue, prefs.radianMode, RoundingMode.DOWN)
|
||||||
|
.toFractionalString()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
result.update { CalculationResult.Error }
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
|
calculatorHistoryRepository.add(
|
||||||
|
expression = inputValue,
|
||||||
|
result = calculated,
|
||||||
|
)
|
||||||
|
|
||||||
equalClicked.update { true }
|
equalClicked.update { true }
|
||||||
|
input.update { TextFieldValue(calculated.replace("-", Token.Operator.minus)) }
|
||||||
|
result.update { CalculationResult.Success(fractional) }
|
||||||
|
}
|
||||||
|
|
||||||
val resultFormatted = result
|
private fun calculateInput() {
|
||||||
.format(prefs.precision, prefs.outputFormat)
|
calculationJob?.cancel()
|
||||||
.replace("-", Token.Operator.minus)
|
calculationJob = viewModelScope.launch {
|
||||||
|
if (!input.value.text.isExpression()) {
|
||||||
|
result.update { CalculationResult.Empty }
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
|
||||||
withContext(Dispatchers.IO) {
|
val prefs = prefs.value ?: return@launch
|
||||||
calculatorHistoryRepository.add(
|
val newResult = try {
|
||||||
expression = input.value.text.replace("-", Token.Operator.minus),
|
val calculated = calculate(
|
||||||
result = resultFormatted,
|
input = input.value.text,
|
||||||
)
|
radianMode = prefs.radianMode,
|
||||||
}
|
roundingMode = RoundingMode.HALF_EVEN,
|
||||||
|
)
|
||||||
fractionJob?.cancel()
|
CalculationResult.Success(
|
||||||
fractionJob = launch(Dispatchers.Default) {
|
calculated.format(prefs.precision, prefs.outputFormat),
|
||||||
val fraction = result.toFractionalString()
|
)
|
||||||
|
} catch (e: Exception) {
|
||||||
input.update { TextFieldValue(resultFormatted, TextRange.Zero) }
|
CalculationResult.Empty
|
||||||
this@CalculatorViewModel.result.update { CalculationResult.Fraction(fraction) }
|
}
|
||||||
|
result.update { newResult }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -227,7 +218,14 @@ internal class CalculatorViewModel @Inject constructor(
|
|||||||
Expression(input, radianMode, roundingMode)
|
Expression(input, radianMode, roundingMode)
|
||||||
.calculate()
|
.calculate()
|
||||||
.also {
|
.also {
|
||||||
if (it > BigDecimal.valueOf(Double.MAX_VALUE)) throw ExpressionException.TooBig()
|
if (it.isGreaterThan(maxCalculationResult)) throw ExpressionException.TooBig()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val maxCalculationResult = BigDecimal.valueOf(Double.MAX_VALUE)
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
viewModelScope.cancel()
|
||||||
|
super.onCleared()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,8 @@ package com.sadellie.unitto.feature.calculator
|
|||||||
|
|
||||||
import com.sadellie.unitto.core.base.Token
|
import com.sadellie.unitto.core.base.Token
|
||||||
import com.sadellie.unitto.data.common.isEqualTo
|
import com.sadellie.unitto.data.common.isEqualTo
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
import java.math.BigInteger
|
import java.math.BigInteger
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
@ -36,13 +38,13 @@ import java.math.RoundingMode
|
|||||||
* @receiver [BigDecimal]. Scale doesn't matter, but should be `MAX_PRECISION`
|
* @receiver [BigDecimal]. Scale doesn't matter, but should be `MAX_PRECISION`
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
fun BigDecimal.toFractionalString(): String {
|
suspend fun BigDecimal.toFractionalString(): String = withContext(Dispatchers.Default) {
|
||||||
// https://www.khanacademy.org/math/cc-eighth-grade-math/cc-8th-numbers-operations/cc-8th-repeating-decimals/v/coverting-repeating-decimals-to-fractions-1
|
// https://www.khanacademy.org/math/cc-eighth-grade-math/cc-8th-numbers-operations/cc-8th-repeating-decimals/v/coverting-repeating-decimals-to-fractions-1
|
||||||
// https://www.khanacademy.org/math/cc-eighth-grade-math/cc-8th-numbers-operations/cc-8th-repeating-decimals/v/coverting-repeating-decimals-to-fractions-2
|
// https://www.khanacademy.org/math/cc-eighth-grade-math/cc-8th-numbers-operations/cc-8th-repeating-decimals/v/coverting-repeating-decimals-to-fractions-2
|
||||||
val (integral, fractional) = this.divideAndRemainder(BigDecimal.ONE)
|
val (integral, fractional) = this@toFractionalString.divideAndRemainder(BigDecimal.ONE)
|
||||||
val integralBI = integral.toBigInteger()
|
val integralBI = integral.toBigInteger()
|
||||||
|
|
||||||
if (fractional.isEqualTo(BigDecimal.ZERO)) return ""
|
if (fractional.isEqualTo(BigDecimal.ZERO)) return@withContext ""
|
||||||
|
|
||||||
val res: String = if (integral.isEqualTo(BigDecimal.ZERO)) "" else "$integralBI "
|
val res: String = if (integral.isEqualTo(BigDecimal.ZERO)) "" else "$integralBI "
|
||||||
|
|
||||||
@ -54,9 +56,9 @@ fun BigDecimal.toFractionalString(): String {
|
|||||||
fractional.repeatingFractional(repeatingDecimals.length)
|
fractional.repeatingFractional(repeatingDecimals.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finalDenominator > maxDenominator) return ""
|
if (finalDenominator > maxDenominator) return@withContext ""
|
||||||
|
|
||||||
return "$res$finalNumerator⁄$finalDenominator"
|
return@withContext "$res$finalNumerator⁄$finalDenominator"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun BigDecimal.notRepeatingFractional(): Pair<BigInteger, BigInteger> {
|
private fun BigDecimal.notRepeatingFractional(): Pair<BigInteger, BigInteger> {
|
||||||
|
@ -93,21 +93,7 @@ fun TextBox(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is CalculationResult.Default -> {
|
is CalculationResult.Success -> {
|
||||||
var outputTF by remember(output) { mutableStateOf(TextFieldValue(output.text)) }
|
|
||||||
|
|
||||||
ExpressionTextField(
|
|
||||||
modifier = calculationResultModifier,
|
|
||||||
value = outputTF,
|
|
||||||
minRatio = 0.8f,
|
|
||||||
onValueChange = { outputTF = it },
|
|
||||||
textColor = MaterialTheme.colorScheme.onSurfaceVariant.copy(0.6f),
|
|
||||||
formatterSymbols = formatterSymbols,
|
|
||||||
readOnly = true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
is CalculationResult.Fraction -> {
|
|
||||||
var outputTF by remember(output) { mutableStateOf(TextFieldValue(output.text)) }
|
var outputTF by remember(output) { mutableStateOf(TextFieldValue(output.text)) }
|
||||||
|
|
||||||
ExpressionTextField(
|
ExpressionTextField(
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
package com.sadellie.unitto.feature.calculator
|
package com.sadellie.unitto.feature.calculator
|
||||||
|
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.math.BigDecimal
|
import java.math.BigDecimal
|
||||||
@ -25,60 +26,52 @@ import java.math.BigDecimal
|
|||||||
class DecimalToFractionTest {
|
class DecimalToFractionTest {
|
||||||
@Test
|
@Test
|
||||||
fun testNoDecimal1() {
|
fun testNoDecimal1() {
|
||||||
val bd = BigDecimal("100")
|
assertFractional("", "100")
|
||||||
assertFractional("", bd.toFractionalString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNoDecimal2() {
|
fun testNoDecimal2() {
|
||||||
val bd = BigDecimal("100.000000000")
|
assertFractional("", "100.000000000")
|
||||||
assertFractional("", bd.toFractionalString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSimpleDecimal1() {
|
fun testSimpleDecimal1() {
|
||||||
val bd = BigDecimal("0.25")
|
assertFractional("1/4", "0.25")
|
||||||
assertFractional("1/4", bd.toFractionalString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSimpleDecimal2() {
|
fun testSimpleDecimal2() {
|
||||||
val bd = BigDecimal("100.25")
|
assertFractional("100 1/4", "100.25")
|
||||||
assertFractional("100 1/4", bd.toFractionalString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRepeating1() {
|
fun testRepeating1() {
|
||||||
val bd = BigDecimal("0.666666666")
|
assertFractional("2/3", "0.666666666")
|
||||||
assertFractional("2/3", bd.toFractionalString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRepeating2() {
|
fun testRepeating2() {
|
||||||
val bd = BigDecimal("4.666666666")
|
assertFractional("4 2/3", "4.666666666")
|
||||||
assertFractional("4 2/3", bd.toFractionalString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRepeating3() {
|
fun testRepeating3() {
|
||||||
val bd = BigDecimal("0.78571428571428571428")
|
assertFractional("11/14", "0.78571428571428571428")
|
||||||
assertFractional("11/14", bd.toFractionalString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRepeating4() {
|
fun testRepeating4() {
|
||||||
val bd = BigDecimal("66.78571428571428571428")
|
assertFractional("66 11/14", "66.78571428571428571428")
|
||||||
assertFractional("66 11/14", bd.toFractionalString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testRepeating5() {
|
fun testRepeating5() {
|
||||||
val bd = BigDecimal("0.666000")
|
assertFractional("333/500", "0.666000")
|
||||||
assertFractional("333/500", bd.toFractionalString())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assertFractional(expected: String, actual: String) = assertEquals(
|
private fun assertFractional(expected: String, actual: String) = assertEquals(
|
||||||
expected,
|
expected,
|
||||||
actual.replace("⁄", "/"),
|
runBlocking { BigDecimal(actual).toFractionalString() }
|
||||||
|
.replace("⁄", "/"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user