Redesign exponential function rendering #274
@ -71,6 +71,15 @@ public object LatexSyntaxRenderer : SyntaxRenderer {
|
|||||||
append('}')
|
append('}')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ExponentSyntax -> if (node.useOperatorForm) {
|
||||||
|
append("\\operatorname{exp}\\,")
|
||||||
|
render(node.operand)
|
||||||
|
} else {
|
||||||
|
append("e^{")
|
||||||
|
render(node.operand)
|
||||||
|
append('}')
|
||||||
|
}
|
||||||
|
|
||||||
is SuperscriptSyntax -> {
|
is SuperscriptSyntax -> {
|
||||||
render(node.left)
|
render(node.left)
|
||||||
append("^{")
|
append("^{")
|
||||||
|
@ -82,6 +82,19 @@ public object MathMLSyntaxRenderer : SyntaxRenderer {
|
|||||||
|
|
||||||
is RadicalSyntax -> tag("msqrt") { render(node.operand) }
|
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") {
|
is SuperscriptSyntax -> tag("msup") {
|
||||||
tag("mrow") { render(node.left) }
|
tag("mrow") { render(node.left) }
|
||||||
tag("mrow") { render(node.right) }
|
tag("mrow") { render(node.right) }
|
||||||
|
@ -83,7 +83,7 @@ public open class FeaturedMathRendererWithPostProcess(
|
|||||||
Fraction.Default,
|
Fraction.Default,
|
||||||
Power.Default,
|
Power.Default,
|
||||||
SquareRoot.Default,
|
SquareRoot.Default,
|
||||||
Exponential.Default,
|
Exponent.Default,
|
||||||
InverseTrigonometricOperations.Default,
|
InverseTrigonometricOperations.Default,
|
||||||
|
|
||||||
// Fallback option for unknown operations - printing them as operator
|
// Fallback option for unknown operations - printing them as operator
|
||||||
@ -100,6 +100,7 @@ public open class FeaturedMathRendererWithPostProcess(
|
|||||||
PrintSymbolic,
|
PrintSymbolic,
|
||||||
),
|
),
|
||||||
listOf(
|
listOf(
|
||||||
|
BetterExponent,
|
||||||
SimplifyParentheses.Default,
|
SimplifyParentheses.Default,
|
||||||
BetterMultiplication,
|
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).
|
* 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 class PrettyPrintFloats(public val types: Set<KClass<out Number>>) : RenderFeature {
|
||||||
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
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
|
||||||
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) {
|
if (toString.contains('E', ignoreCase = true)) {
|
||||||
val (beforeE, afterE) = toString.split('E')
|
val (beforeE, afterE) = toString.split('E', ignoreCase = true)
|
||||||
val significand = beforeE.toDouble().toString().removeSuffix(".0")
|
val significand = beforeE.toDouble().toString().removeSuffix(".0")
|
||||||
val exponent = afterE.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 class PrettyPrintIntegers(public val types: Set<KClass<out Number>>) : RenderFeature {
|
||||||
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
||||||
if (node !is MST.Numeric || node.value::class !in types)
|
if (node !is MST.Numeric || node.value::class !in types) return null
|
||||||
return null
|
|
||||||
|
|
||||||
return printSignedNumberString(node.value.toString())
|
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 class Exponent(operations: Collection<String>?) : Unary(operations) {
|
||||||
public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = SuperscriptSyntax(
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = ExponentSyntax(
|
||||||
operation = node.operation,
|
operation = node.operation,
|
||||||
left = SymbolSyntax(string = "e"),
|
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
|
||||||
right = parent.render(node.value),
|
useOperatorForm = true,
|
||||||
)
|
)
|
||||||
|
|
||||||
public companion object {
|
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,8 +16,7 @@ import space.kscience.kmath.operations.RingOperations
|
|||||||
* @author Iaroslav Postovalov
|
* @author Iaroslav Postovalov
|
||||||
*/
|
*/
|
||||||
public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostProcessStage {
|
public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostProcessStage {
|
||||||
public override fun perform(node: MathSyntax) {
|
public override fun perform(node: MathSyntax): Unit = when (node) {
|
||||||
when (node) {
|
|
||||||
is NumberSyntax -> Unit
|
is NumberSyntax -> Unit
|
||||||
is SymbolSyntax -> Unit
|
is SymbolSyntax -> Unit
|
||||||
is OperatorNameSyntax -> Unit
|
is OperatorNameSyntax -> Unit
|
||||||
@ -32,6 +31,7 @@ public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostPro
|
|||||||
is UnaryPlusSyntax -> perform(node.operand)
|
is UnaryPlusSyntax -> perform(node.operand)
|
||||||
is UnaryMinusSyntax -> perform(node.operand)
|
is UnaryMinusSyntax -> perform(node.operand)
|
||||||
is RadicalSyntax -> perform(node.operand)
|
is RadicalSyntax -> perform(node.operand)
|
||||||
|
is ExponentSyntax -> perform(node.operand)
|
||||||
|
|
||||||
is SuperscriptSyntax -> {
|
is SuperscriptSyntax -> {
|
||||||
perform(node.left)
|
perform(node.left)
|
||||||
@ -80,6 +80,47 @@ public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostPro
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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,8 +131,7 @@ public object BetterMultiplication : FeaturedMathRendererWithPostProcess.PostPro
|
|||||||
*/
|
*/
|
||||||
public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) -> Int) :
|
public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) -> Int) :
|
||||||
FeaturedMathRendererWithPostProcess.PostProcessStage {
|
FeaturedMathRendererWithPostProcess.PostProcessStage {
|
||||||
public override fun perform(node: MathSyntax) {
|
public override fun perform(node: MathSyntax): Unit = when (node) {
|
||||||
when (node) {
|
|
||||||
is NumberSyntax -> Unit
|
is NumberSyntax -> Unit
|
||||||
is SymbolSyntax -> Unit
|
is SymbolSyntax -> Unit
|
||||||
is OperatorNameSyntax -> Unit
|
is OperatorNameSyntax -> Unit
|
||||||
@ -116,8 +156,11 @@ public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) ->
|
|||||||
else -> precedence > precedenceFunction(parent)
|
else -> precedence > precedenceFunction(parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val isInsideExpOperator =
|
||||||
|
node.parent is ExponentSyntax && (node.parent as ExponentSyntax).useOperatorForm
|
||||||
|
|
||||||
node.parentheses = !isRightOfSuperscript
|
node.parentheses = !isRightOfSuperscript
|
||||||
&& (needParenthesesByPrecedence || node.parent is UnaryOperatorSyntax)
|
&& (needParenthesesByPrecedence || node.parent is UnaryOperatorSyntax || isInsideExpOperator)
|
||||||
|
|
||||||
perform(node.operand)
|
perform(node.operand)
|
||||||
}
|
}
|
||||||
@ -128,10 +171,9 @@ public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
is UnaryPlusSyntax -> perform(node.operand)
|
is UnaryPlusSyntax -> perform(node.operand)
|
||||||
is UnaryMinusSyntax -> {
|
is UnaryMinusSyntax -> perform(node.operand)
|
||||||
perform(node.operand)
|
|
||||||
}
|
|
||||||
is RadicalSyntax -> perform(node.operand)
|
is RadicalSyntax -> perform(node.operand)
|
||||||
|
is ExponentSyntax -> perform(node.operand)
|
||||||
|
|
||||||
is SuperscriptSyntax -> {
|
is SuperscriptSyntax -> {
|
||||||
perform(node.left)
|
perform(node.left)
|
||||||
@ -174,7 +216,6 @@ public class SimplifyParentheses(public val precedenceFunction: (MathSyntax) ->
|
|||||||
perform(node.right)
|
perform(node.right)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public companion object {
|
public companion object {
|
||||||
/**
|
/**
|
||||||
|
@ -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.
|
* 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.ast.parseMath
|
||||||
import space.kscience.kmath.expressions.evaluate
|
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.
|
* 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.Complex
|
||||||
import space.kscience.kmath.complex.ComplexField
|
import space.kscience.kmath.complex.ComplexField
|
||||||
import space.kscience.kmath.expressions.evaluate
|
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.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(-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
|
@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", "\\left(x+x\\right)^{x}+x\\,x")
|
||||||
testLatex("x^(x+x)", "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