forked from kscience/kmath
Redesign exponential function rendering
This commit is contained in:
parent
23d2f8d758
commit
488f5f0008
@ -71,6 +71,15 @@ public object LatexSyntaxRenderer : SyntaxRenderer {
|
||||
append('}')
|
||||
}
|
||||
|
||||
is ExponentSyntax -> if (node.useOperatorForm) {
|
||||
append("\\operatorname{exp}\\,")
|
||||
render(node.operand)
|
||||
} else {
|
||||
append("e^{")
|
||||
render(node.operand)
|
||||
append('}')
|
||||
}
|
||||
|
||||
is SuperscriptSyntax -> {
|
||||
render(node.left)
|
||||
append("^{")
|
||||
|
@ -82,6 +82,19 @@ public object MathMLSyntaxRenderer : SyntaxRenderer {
|
||||
|
||||
is RadicalSyntax -> tag("msqrt") { render(node.operand) }
|
||||
|
||||
is ExponentSyntax -> if (node.useOperatorForm) {
|
||||
tag("mo") { append("exp") }
|
||||
tag("mspace", "width" to "0.167em")
|
||||
render(node.operand)
|
||||
} else {
|
||||
tag("msup") {
|
||||
tag("mrow") {
|
||||
tag("mi") { append("e") }
|
||||
}
|
||||
tag("mrow") { render(node.operand) }
|
||||
}
|
||||
}
|
||||
|
||||
is SuperscriptSyntax -> tag("msup") {
|
||||
tag("mrow") { render(node.left) }
|
||||
tag("mrow") { render(node.right) }
|
||||
|
@ -83,7 +83,7 @@ public open class FeaturedMathRendererWithPostProcess(
|
||||
Fraction.Default,
|
||||
Power.Default,
|
||||
SquareRoot.Default,
|
||||
Exponential.Default,
|
||||
Exponent.Default,
|
||||
InverseTrigonometricOperations.Default,
|
||||
|
||||
// Fallback option for unknown operations - printing them as operator
|
||||
@ -100,6 +100,7 @@ public open class FeaturedMathRendererWithPostProcess(
|
||||
PrintSymbolic,
|
||||
),
|
||||
listOf(
|
||||
BetterExponent,
|
||||
SimplifyParentheses.Default,
|
||||
BetterMultiplication,
|
||||
),
|
||||
|
@ -189,6 +189,24 @@ public data class RadicalSyntax(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents exponential function.
|
||||
*
|
||||
* @property operand The argument of function.
|
||||
* @property useOperatorForm `true` if operator form is used (*exp (x)*), `false` if exponentiation form is used
|
||||
* (*e<sup>x</sup>*).
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
public data class ExponentSyntax(
|
||||
public override val operation: String,
|
||||
public override val operand: OperandSyntax,
|
||||
public var useOperatorForm: Boolean,
|
||||
) : UnarySyntax() {
|
||||
init {
|
||||
operand.parent = this
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a syntax node with superscript (usually, for exponentiation).
|
||||
*
|
||||
|
@ -56,10 +56,14 @@ private fun printSignedNumberString(s: String): MathSyntax {
|
||||
public class PrettyPrintFloats(public val types: Set<KClass<out Number>>) : RenderFeature {
|
||||
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
||||
if (node !is MST.Numeric || node.value::class !in types) return null
|
||||
val toString = node.value.toString().removeSuffix(".0")
|
||||
val toString = when (val v = node.value) {
|
||||
is Float -> v.multiplatformToString()
|
||||
is Double -> v.multiplatformToString()
|
||||
else -> v.toString()
|
||||
}.removeSuffix(".0")
|
||||
|
||||
if ('E' in toString) {
|
||||
val (beforeE, afterE) = toString.split('E')
|
||||
if (toString.contains('E', ignoreCase = true)) {
|
||||
val (beforeE, afterE) = toString.split('E', ignoreCase = true)
|
||||
val significand = beforeE.toDouble().toString().removeSuffix(".0")
|
||||
val exponent = afterE.toDouble().toString().removeSuffix(".0")
|
||||
|
||||
@ -108,9 +112,7 @@ public class PrettyPrintFloats(public val types: Set<KClass<out Number>>) : Rend
|
||||
*/
|
||||
public class PrettyPrintIntegers(public val types: Set<KClass<out Number>>) : RenderFeature {
|
||||
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
||||
if (node !is MST.Numeric || node.value::class !in types)
|
||||
return null
|
||||
|
||||
if (node !is MST.Numeric || node.value::class !in types) return null
|
||||
return printSignedNumberString(node.value.toString())
|
||||
}
|
||||
|
||||
@ -282,15 +284,15 @@ public class SquareRoot(operations: Collection<String>?) : Unary(operations) {
|
||||
}
|
||||
}
|
||||
|
||||
public class Exponential(operations: Collection<String>?) : Unary(operations) {
|
||||
public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = SuperscriptSyntax(
|
||||
public class Exponent(operations: Collection<String>?) : Unary(operations) {
|
||||
public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = ExponentSyntax(
|
||||
operation = node.operation,
|
||||
left = SymbolSyntax(string = "e"),
|
||||
right = parent.render(node.value),
|
||||
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
|
||||
useOperatorForm = true,
|
||||
)
|
||||
|
||||
public companion object {
|
||||
public val Default: Exponential = Exponential(setOf(ExponentialOperations.EXP_OPERATION))
|
||||
public val Default: Exponent = Exponent(setOf(ExponentialOperations.EXP_OPERATION))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
internal expect fun Double.multiplatformToString(): String
|
||||
internal expect fun Float.multiplatformToString(): String
|
@ -16,69 +16,110 @@ import space.kscience.kmath.operations.RingOperations
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostProcessStage {
|
||||
public override fun perform(node: MathSyntax) {
|
||||
when (node) {
|
||||
is NumberSyntax -> Unit
|
||||
is SymbolSyntax -> Unit
|
||||
is OperatorNameSyntax -> Unit
|
||||
is SpecialSymbolSyntax -> Unit
|
||||
is OperandSyntax -> perform(node.operand)
|
||||
public override fun perform(node: MathSyntax): Unit = when (node) {
|
||||
is NumberSyntax -> Unit
|
||||
is SymbolSyntax -> Unit
|
||||
is OperatorNameSyntax -> Unit
|
||||
is SpecialSymbolSyntax -> Unit
|
||||
is OperandSyntax -> perform(node.operand)
|
||||
|
||||
is UnaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.operand)
|
||||
}
|
||||
|
||||
is UnaryPlusSyntax -> perform(node.operand)
|
||||
is UnaryMinusSyntax -> perform(node.operand)
|
||||
is RadicalSyntax -> perform(node.operand)
|
||||
|
||||
is SuperscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is SubscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryPlusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryMinusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is FractionSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is RadicalWithIndexSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is MultiplicationSyntax -> {
|
||||
node.times = node.right.operand is NumberSyntax && !node.right.parentheses
|
||||
|| node.left.operand is NumberSyntax && node.right.operand is FractionSyntax
|
||||
|| node.left.operand is NumberSyntax && node.right.operand is NumberSyntax
|
||||
|| node.left.operand is NumberSyntax && node.right.operand is SuperscriptSyntax && node.right.operand.left is NumberSyntax
|
||||
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
is UnaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.operand)
|
||||
}
|
||||
|
||||
is UnaryPlusSyntax -> perform(node.operand)
|
||||
is UnaryMinusSyntax -> perform(node.operand)
|
||||
is RadicalSyntax -> perform(node.operand)
|
||||
is ExponentSyntax -> perform(node.operand)
|
||||
|
||||
is SuperscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is SubscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryPlusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryMinusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is FractionSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is RadicalWithIndexSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is MultiplicationSyntax -> {
|
||||
node.times = node.right.operand is NumberSyntax && !node.right.parentheses
|
||||
|| node.left.operand is NumberSyntax && node.right.operand is FractionSyntax
|
||||
|| node.left.operand is NumberSyntax && node.right.operand is NumberSyntax
|
||||
|| node.left.operand is NumberSyntax && node.right.operand is SuperscriptSyntax && node.right.operand.left is NumberSyntax
|
||||
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Applies [ExponentSyntax.useOperatorForm] to [ExponentSyntax] when the operand contains a fraction, a
|
||||
* superscript or a subscript to improve readability.
|
||||
*
|
||||
* @author Iaroslav Postovalov
|
||||
*/
|
||||
public object BetterExponent : FeaturedMathRendererWithPostProcess.PostProcessStage {
|
||||
private fun perform0(node: MathSyntax): Boolean {
|
||||
return when (node) {
|
||||
is NumberSyntax -> false
|
||||
is SymbolSyntax -> false
|
||||
is OperatorNameSyntax -> false
|
||||
is SpecialSymbolSyntax -> false
|
||||
is OperandSyntax -> perform0(node.operand)
|
||||
is UnaryOperatorSyntax -> perform0(node.prefix) || perform0(node.operand)
|
||||
is UnaryPlusSyntax -> perform0(node.operand)
|
||||
is UnaryMinusSyntax -> perform0(node.operand)
|
||||
is RadicalSyntax -> perform0(node.operand)
|
||||
|
||||
is ExponentSyntax -> {
|
||||
val r = perform0(node.operand)
|
||||
node.useOperatorForm = r
|
||||
r
|
||||
}
|
||||
|
||||
is SuperscriptSyntax -> true
|
||||
is SubscriptSyntax -> true
|
||||
is BinaryOperatorSyntax -> perform0(node.prefix) || perform0(node.left) || perform0(node.right)
|
||||
is BinaryPlusSyntax -> perform0(node.left) || perform0(node.right)
|
||||
is BinaryMinusSyntax -> perform0(node.left) || perform0(node.right)
|
||||
is FractionSyntax -> true
|
||||
is RadicalWithIndexSyntax -> perform0(node.left) || perform0(node.right)
|
||||
is MultiplicationSyntax -> perform0(node.left) || perform0(node.right)
|
||||
}
|
||||
}
|
||||
|
||||
public override fun perform(node: MathSyntax) {
|
||||
perform0(node)
|
||||
}
|
||||
}
|
||||
|
||||
@ -90,89 +131,89 @@ public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostPro
|
||||
*/
|
||||
public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) -> Int) :
|
||||
FeaturedMathRendererWithPostProcess.PostProcessStage {
|
||||
public override fun perform(node: MathSyntax) {
|
||||
when (node) {
|
||||
is NumberSyntax -> Unit
|
||||
is SymbolSyntax -> Unit
|
||||
is OperatorNameSyntax -> Unit
|
||||
is SpecialSymbolSyntax -> Unit
|
||||
public override fun perform(node: MathSyntax): Unit = when (node) {
|
||||
is NumberSyntax -> Unit
|
||||
is SymbolSyntax -> Unit
|
||||
is OperatorNameSyntax -> Unit
|
||||
is SpecialSymbolSyntax -> Unit
|
||||
|
||||
is OperandSyntax -> {
|
||||
val isRightOfSuperscript =
|
||||
(node.parent is SuperscriptSyntax) && (node.parent as SuperscriptSyntax).right === node
|
||||
is OperandSyntax -> {
|
||||
val isRightOfSuperscript =
|
||||
(node.parent is SuperscriptSyntax) && (node.parent as SuperscriptSyntax).right === node
|
||||
|
||||
val precedence = precedenceFunction(node.operand)
|
||||
val precedence = precedenceFunction(node.operand)
|
||||
|
||||
val needParenthesesByPrecedence = when (val parent = node.parent) {
|
||||
null -> false
|
||||
val needParenthesesByPrecedence = when (val parent = node.parent) {
|
||||
null -> false
|
||||
|
||||
is BinarySyntax -> {
|
||||
val parentPrecedence = precedenceFunction(parent)
|
||||
is BinarySyntax -> {
|
||||
val parentPrecedence = precedenceFunction(parent)
|
||||
|
||||
parentPrecedence < precedence ||
|
||||
parentPrecedence == precedence && parentPrecedence != 0 && node === parent.right
|
||||
}
|
||||
|
||||
else -> precedence > precedenceFunction(parent)
|
||||
parentPrecedence < precedence ||
|
||||
parentPrecedence == precedence && parentPrecedence != 0 && node === parent.right
|
||||
}
|
||||
|
||||
node.parentheses = !isRightOfSuperscript
|
||||
&& (needParenthesesByPrecedence || node.parent is UnaryOperatorSyntax)
|
||||
|
||||
perform(node.operand)
|
||||
else -> precedence > precedenceFunction(parent)
|
||||
}
|
||||
|
||||
is UnaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.operand)
|
||||
}
|
||||
val isInsideExpOperator =
|
||||
node.parent is ExponentSyntax && (node.parent as ExponentSyntax).useOperatorForm
|
||||
|
||||
is UnaryPlusSyntax -> perform(node.operand)
|
||||
is UnaryMinusSyntax -> {
|
||||
perform(node.operand)
|
||||
}
|
||||
is RadicalSyntax -> perform(node.operand)
|
||||
node.parentheses = !isRightOfSuperscript
|
||||
&& (needParenthesesByPrecedence || node.parent is UnaryOperatorSyntax || isInsideExpOperator)
|
||||
|
||||
is SuperscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
perform(node.operand)
|
||||
}
|
||||
|
||||
is SubscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
is UnaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.operand)
|
||||
}
|
||||
|
||||
is BinaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
is UnaryPlusSyntax -> perform(node.operand)
|
||||
is UnaryMinusSyntax -> perform(node.operand)
|
||||
is RadicalSyntax -> perform(node.operand)
|
||||
is ExponentSyntax -> perform(node.operand)
|
||||
|
||||
is BinaryPlusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
is SuperscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is BinaryMinusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
is SubscriptSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is FractionSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
is BinaryOperatorSyntax -> {
|
||||
perform(node.prefix)
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is MultiplicationSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
is BinaryPlusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is RadicalWithIndexSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
is BinaryMinusSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is FractionSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is MultiplicationSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
|
||||
is RadicalWithIndexSyntax -> {
|
||||
perform(node.left)
|
||||
perform(node.right)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscisnce.kmath.ast
|
||||
package space.kscience.kmath.ast
|
||||
|
||||
import space.kscience.kmath.ast.parseMath
|
||||
import space.kscience.kmath.expressions.evaluate
|
@ -3,9 +3,8 @@
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscisnce.kmath.ast
|
||||
package space.kscience.kmath.ast
|
||||
|
||||
import space.kscience.kmath.ast.parseMath
|
||||
import space.kscience.kmath.complex.Complex
|
||||
import space.kscience.kmath.complex.ComplexField
|
||||
import space.kscience.kmath.expressions.evaluate
|
@ -42,6 +42,22 @@ internal class TestFeatures {
|
||||
testLatex(Numeric(1.1e-10), "1.1\\times10^{-10}")
|
||||
testLatex(Numeric(-1.1e-10), "-1.1\\times10^{-10}")
|
||||
testLatex(Numeric(-1.1e10), "-1.1\\times10^{10}")
|
||||
testLatex(Numeric(0.001), "0.001")
|
||||
testLatex(Numeric(0.0000001), "1\\times10^{-7}")
|
||||
|
||||
testLatex(Numeric(Float.NaN), "NaN")
|
||||
testLatex(Numeric(Float.POSITIVE_INFINITY), "\\infty")
|
||||
testLatex(Numeric(Float.NEGATIVE_INFINITY), "-\\infty")
|
||||
testLatex(Numeric(1.0f), "1")
|
||||
testLatex(Numeric(-1.0f), "-1")
|
||||
testLatex(Numeric(1.42f), "1.42")
|
||||
testLatex(Numeric(-1.42f), "-1.42")
|
||||
testLatex(Numeric(1e10f), "1\\times10^{10}")
|
||||
testLatex(Numeric(1e-10f), "1\\times10^{-10}")
|
||||
testLatex(Numeric(-1e-10f), "-1\\times10^{-10}")
|
||||
testLatex(Numeric(-1e10f), "-1\\times10^{10}")
|
||||
testLatex(Numeric(0.001f), "0.001")
|
||||
testLatex(Numeric(0.0000001f), "1\\times10^{-7}")
|
||||
}
|
||||
|
||||
@Test
|
@ -30,4 +30,11 @@ internal class TestStages {
|
||||
testLatex("(x+x)^x+x*x", "\\left(x+x\\right)^{x}+x\\,x")
|
||||
testLatex("x^(x+x)", "x^{x+x}")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun exponent() {
|
||||
testLatex("exp(x)", "e^{x}")
|
||||
testLatex("exp(x/2)", "\\operatorname{exp}\\,\\left(\\frac{x}{2}\\right)")
|
||||
testLatex("exp(x^2)", "\\operatorname{exp}\\,\\left(x^{2}\\right)")
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
internal actual fun Double.multiplatformToString(): String {
|
||||
val d = this
|
||||
if (d >= 1e7 || d <= -1e7) return js("d.toExponential()") as String
|
||||
return toString()
|
||||
}
|
||||
|
||||
internal actual fun Float.multiplatformToString(): String {
|
||||
val d = this
|
||||
if (d >= 1e7f || d <= -1e7f) return js("d.toExponential()") as String
|
||||
return toString()
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* Copyright 2018-2021 KMath contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package space.kscience.kmath.ast.rendering
|
||||
|
||||
internal actual fun Double.multiplatformToString(): String = toString()
|
||||
internal actual fun Float.multiplatformToString(): String = toString()
|
Loading…
Reference in New Issue
Block a user