package com.lemenzo.calculator.engine import java.math.BigDecimal import java.math.MathContext import java.math.RoundingMode object DisplayFormatter { enum class Notation { AUTO, SCIENTIFIC, ENGINEERING } private val mc = MathContext(34, RoundingMode.HALF_EVEN) fun format( value: BigDecimal, notation: Notation = Notation.AUTO, decimalSeparator: Char = '.', groupingSeparator: Char = ',', grouping: Boolean = true ): String { if (value.compareTo(BigDecimal.ZERO) == 0) return "0" val rounded = value.round(mc).stripTrailingZeros() val raw = when (notation) { Notation.SCIENTIFIC -> scientific(rounded) Notation.ENGINEERING -> rounded.toEngineeringString() Notation.AUTO -> { val a = rounded.abs() if (a >= BigDecimal("1E15") || a < BigDecimal("1E-10")) rounded.toEngineeringString() else rounded.toPlainString() } } return localize(raw.replace("E+", "E"), decimalSeparator, groupingSeparator, grouping && !raw.contains('E')) } private fun scientific(v: BigDecimal): String { val x = v.stripTrailingZeros() val exponent = x.precision() - 1 - x.scale() val mantissa = x.movePointLeft(exponent).stripTrailingZeros().toPlainString() return "$mantissa" + "E" + exponent } private fun localize(raw: String, decimal: Char, grouping: Char, group: Boolean): String { val e = raw.indexOf('E') val number = if (e >= 0) raw.substring(0, e) else raw val exponent = if (e >= 0) raw.substring(e) else "" val sign = if (number.startsWith("-")) "-" else "" val unsigned = number.removePrefix("-") val dot = unsigned.indexOf('.') val integer = if (dot >= 0) unsigned.substring(0, dot) else unsigned val fraction = if (dot >= 0) unsigned.substring(dot + 1) else "" val grouped = if (group) groupDigits(integer, grouping) else integer val localized = if (fraction.isEmpty()) "$sign$grouped" else "$sign$grouped$decimal$fraction" return localized + exponent.replace('.', decimal) } private fun groupDigits(value: String, separator: Char): String { if (value.length <= 3) return value val first = value.length % 3 val out = StringBuilder() var index = 0 if (first != 0) { out.append(value.substring(0, first)) index = first } while (index < value.length) { if (out.isNotEmpty()) out.append(separator) out.append(value.substring(index, index + 3)) index += 3 } return out.toString() } }