Some experiments with MST rendering #264
@ -259,8 +259,8 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
api("space.kscience:kmath-core:0.3.0-dev-3")
|
api("space.kscience:kmath-core:0.3.0-dev-4")
|
||||||
// api("kscience.kmath:kmath-core-jvm:0.3.0-dev-3") for jvm-specific version
|
// api("kscience.kmath:kmath-core-jvm:0.3.0-dev-4") for jvm-specific version
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
package space.kscience.kmath.ast
|
||||||
|
|
||||||
|
import space.kscience.kmath.ast.rendering.FeaturedMathRendererWithPostProcess
|
||||||
|
import space.kscience.kmath.ast.rendering.LatexSyntaxRenderer
|
||||||
|
import space.kscience.kmath.ast.rendering.MathMLSyntaxRenderer
|
||||||
|
import space.kscience.kmath.ast.rendering.renderWithStringBuilder
|
||||||
|
|
||||||
|
public fun main() {
|
||||||
|
val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath()
|
||||||
|
val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst)
|
||||||
|
println("MathSyntax:")
|
||||||
|
println(syntax)
|
||||||
|
println()
|
||||||
|
val latex = LatexSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||||
|
println("LaTeX:")
|
||||||
|
println(latex)
|
||||||
|
println()
|
||||||
|
val mathML = MathMLSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||||
|
println("MathML:")
|
||||||
|
println(mathML)
|
||||||
|
}
|
@ -12,7 +12,7 @@ Abstract syntax tree expression representation and related optimizations.
|
|||||||
|
|
||||||
## Artifact:
|
## Artifact:
|
||||||
|
|
||||||
The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0-dev-3`.
|
The Maven coordinates of this project are `space.kscience:kmath-ast:0.3.0-dev-4`.
|
||||||
|
|
||||||
**Gradle:**
|
**Gradle:**
|
||||||
```gradle
|
```gradle
|
||||||
@ -23,7 +23,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'space.kscience:kmath-ast:0.3.0-dev-3'
|
implementation 'space.kscience:kmath-ast:0.3.0-dev-4'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**Gradle Kotlin DSL:**
|
**Gradle Kotlin DSL:**
|
||||||
@ -35,7 +35,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("space.kscience:kmath-ast:0.3.0-dev-3")
|
implementation("space.kscience:kmath-ast:0.3.0-dev-4")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -111,3 +111,39 @@ var executable = function (constants, arguments) {
|
|||||||
#### Known issues
|
#### Known issues
|
||||||
|
|
||||||
- This feature uses `eval` which can be unavailable in several environments.
|
- This feature uses `eval` which can be unavailable in several environments.
|
||||||
|
|
||||||
|
## Rendering expressions
|
||||||
|
|
||||||
|
kmath-ast also includes an extensible engine to display expressions in LaTeX or MathML syntax.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
import space.kscience.kmath.ast.*
|
||||||
|
import space.kscience.kmath.ast.rendering.*
|
||||||
|
|
||||||
|
public fun main() {
|
||||||
|
val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath()
|
||||||
|
val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst)
|
||||||
|
val latex = LatexSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||||
|
println("LaTeX:")
|
||||||
|
println(latex)
|
||||||
|
println()
|
||||||
|
val mathML = MathMLSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||||
|
println("MathML:")
|
||||||
|
println(mathML)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Result LaTeX:
|
||||||
|
|
||||||
|
![](http://chart.googleapis.com/chart?cht=tx&chl=e%5E%7B%5Csqrt%7Bx%7D%7D-%5Cfrac%7B%5Cfrac%7B%5Coperatorname%7Bsin%7D%5E%7B-1%7D%5C,%5Cleft(2%5C,x%5Cright)%7D%7B2%5Ctimes10%5E%7B10%7D%2Bx%5E%7B3%7D%7D%7D%7B-12%7D)
|
||||||
|
|
||||||
|
Result MathML (embedding MathML is not allowed by GitHub Markdown):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<mrow><msup><mrow><mi>e</mi></mrow><mrow><msqrt><mi>x</mi></msqrt></mrow></msup><mo>-</mo><mfrac><mrow><mfrac><mrow><msup><mrow><mo>sin</mo></mrow><mrow><mo>-</mo><mn>1</mn></mrow></msup><mspace width="0.167em"></mspace><mfenced open="(" close=")" separators=""><mn>2</mn><mspace width="0.167em"></mspace><mi>x</mi></mfenced></mrow><mrow><mn>2</mn><mo>×</mo><msup><mrow><mn>10</mn></mrow><mrow><mn>10</mn></mrow></msup><mo>+</mo><msup><mrow><mi>x</mi></mrow><mrow><mn>3</mn></mrow></msup></mrow></mfrac></mrow><mrow><mo>-</mo><mn>12</mn></mrow></mfrac></mrow>
|
||||||
|
```
|
||||||
|
|
||||||
|
It is also possible to create custom algorithms of render, and even add support of other markup languages
|
||||||
|
(see API reference).
|
||||||
|
@ -78,3 +78,39 @@ var executable = function (constants, arguments) {
|
|||||||
#### Known issues
|
#### Known issues
|
||||||
|
|
||||||
- This feature uses `eval` which can be unavailable in several environments.
|
- This feature uses `eval` which can be unavailable in several environments.
|
||||||
|
|
||||||
|
## Rendering expressions
|
||||||
|
|
||||||
|
kmath-ast also includes an extensible engine to display expressions in LaTeX or MathML syntax.
|
||||||
|
|
||||||
|
Example usage:
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
import space.kscience.kmath.ast.*
|
||||||
|
import space.kscience.kmath.ast.rendering.*
|
||||||
|
|
||||||
|
public fun main() {
|
||||||
|
val mst = "exp(sqrt(x))-asin(2*x)/(2e10+x^3)/(-12)".parseMath()
|
||||||
|
val syntax = FeaturedMathRendererWithPostProcess.Default.render(mst)
|
||||||
|
val latex = LatexSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||||
|
println("LaTeX:")
|
||||||
|
println(latex)
|
||||||
|
println()
|
||||||
|
val mathML = MathMLSyntaxRenderer.renderWithStringBuilder(syntax)
|
||||||
|
println("MathML:")
|
||||||
|
println(mathML)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Result LaTeX:
|
||||||
|
|
||||||
|
![](http://chart.googleapis.com/chart?cht=tx&chl=e%5E%7B%5Csqrt%7Bx%7D%7D-%5Cfrac%7B%5Cfrac%7B%5Coperatorname%7Bsin%7D%5E%7B-1%7D%5C,%5Cleft(2%5C,x%5Cright)%7D%7B2%5Ctimes10%5E%7B10%7D%2Bx%5E%7B3%7D%7D%7D%7B-12%7D)
|
||||||
|
|
||||||
|
Result MathML (embedding MathML is not allowed by GitHub Markdown):
|
||||||
|
|
||||||
|
```html
|
||||||
|
<mrow><msup><mrow><mi>e</mi></mrow><mrow><msqrt><mi>x</mi></msqrt></mrow></msup><mo>-</mo><mfrac><mrow><mfrac><mrow><msup><mrow><mo>sin</mo></mrow><mrow><mo>-</mo><mn>1</mn></mrow></msup><mspace width="0.167em"></mspace><mfenced open="(" close=")" separators=""><mn>2</mn><mspace width="0.167em"></mspace><mi>x</mi></mfenced></mrow><mrow><mn>2</mn><mo>×</mo><msup><mrow><mn>10</mn></mrow><mrow><mn>10</mn></mrow></msup><mo>+</mo><msup><mrow><mi>x</mi></mrow><mrow><mn>3</mn></mrow></msup></mrow></mfrac></mrow><mrow><mo>-</mo><mn>12</mn></mrow></mfrac></mrow>
|
||||||
|
```
|
||||||
|
|
||||||
|
It is also possible to create custom algorithms of render, and even add support of other markup languages
|
||||||
|
(see API reference).
|
||||||
|
@ -0,0 +1,128 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [SyntaxRenderer] implementation for LaTeX.
|
||||||
|
*
|
||||||
|
* The generated string is a valid LaTeX fragment to be used in the Math Mode.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* \documentclass{article}
|
||||||
|
* \begin{document}
|
||||||
|
* \begin{equation}
|
||||||
|
* %code generated by the syntax renderer
|
||||||
|
* \end{equation}
|
||||||
|
* \end{document}
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public object LatexSyntaxRenderer : SyntaxRenderer {
|
||||||
|
public override fun render(node: MathSyntax, output: Appendable): Unit = output.run {
|
||||||
|
fun render(syntax: MathSyntax) = render(syntax, output)
|
||||||
|
|
||||||
|
when (node) {
|
||||||
|
is NumberSyntax -> append(node.string)
|
||||||
|
is SymbolSyntax -> append(node.string)
|
||||||
|
|
||||||
|
is OperatorNameSyntax -> {
|
||||||
|
append("\\operatorname{")
|
||||||
|
append(node.name)
|
||||||
|
append('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
is SpecialSymbolSyntax -> when (node.kind) {
|
||||||
|
SpecialSymbolSyntax.Kind.INFINITY -> append("\\infty")
|
||||||
|
}
|
||||||
|
|
||||||
|
is OperandSyntax -> {
|
||||||
|
if (node.parentheses) append("\\left(")
|
||||||
|
render(node.operand)
|
||||||
|
if (node.parentheses) append("\\right)")
|
||||||
|
}
|
||||||
|
|
||||||
|
is UnaryOperatorSyntax -> {
|
||||||
|
render(node.prefix)
|
||||||
|
append("\\,")
|
||||||
|
render(node.operand)
|
||||||
|
}
|
||||||
|
|
||||||
|
is UnaryPlusSyntax -> {
|
||||||
|
append('+')
|
||||||
|
render(node.operand)
|
||||||
|
}
|
||||||
|
|
||||||
|
is UnaryMinusSyntax -> {
|
||||||
|
append('-')
|
||||||
|
render(node.operand)
|
||||||
|
}
|
||||||
|
|
||||||
|
is RadicalSyntax -> {
|
||||||
|
append("\\sqrt")
|
||||||
|
append('{')
|
||||||
|
render(node.operand)
|
||||||
|
append('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
is SuperscriptSyntax -> {
|
||||||
|
render(node.left)
|
||||||
|
append("^{")
|
||||||
|
render(node.right)
|
||||||
|
append('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
is SubscriptSyntax -> {
|
||||||
|
render(node.left)
|
||||||
|
append("_{")
|
||||||
|
render(node.right)
|
||||||
|
append('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
is BinaryOperatorSyntax -> {
|
||||||
|
render(node.prefix)
|
||||||
|
append("\\left(")
|
||||||
|
render(node.left)
|
||||||
|
append(',')
|
||||||
|
render(node.right)
|
||||||
|
append("\\right)")
|
||||||
|
}
|
||||||
|
|
||||||
|
is BinaryPlusSyntax -> {
|
||||||
|
render(node.left)
|
||||||
|
append('+')
|
||||||
|
render(node.right)
|
||||||
|
}
|
||||||
|
|
||||||
|
is BinaryMinusSyntax -> {
|
||||||
|
render(node.left)
|
||||||
|
append('-')
|
||||||
|
render(node.right)
|
||||||
|
}
|
||||||
|
|
||||||
|
is FractionSyntax -> {
|
||||||
|
append("\\frac{")
|
||||||
|
render(node.left)
|
||||||
|
append("}{")
|
||||||
|
render(node.right)
|
||||||
|
append('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
is RadicalWithIndexSyntax -> {
|
||||||
|
append("\\sqrt")
|
||||||
|
append('[')
|
||||||
|
render(node.left)
|
||||||
|
append(']')
|
||||||
|
append('{')
|
||||||
|
render(node.right)
|
||||||
|
append('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
is MultiplicationSyntax -> {
|
||||||
|
render(node.left)
|
||||||
|
append(if (node.times) "\\times" else "\\,")
|
||||||
|
render(node.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,133 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [SyntaxRenderer] implementation for MathML.
|
||||||
|
*
|
||||||
|
* The generated XML string is a valid MathML instance.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public object MathMLSyntaxRenderer : SyntaxRenderer {
|
||||||
|
public override fun render(node: MathSyntax, output: Appendable) {
|
||||||
|
output.append("<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow>")
|
||||||
|
render0(node, output)
|
||||||
|
output.append("</mrow></math>")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun render0(node: MathSyntax, output: Appendable): Unit = output.run {
|
||||||
|
fun tag(tagName: String, vararg attr: Pair<String, String>, block: () -> Unit = {}) {
|
||||||
|
append('<')
|
||||||
|
append(tagName)
|
||||||
|
|
||||||
|
if (attr.isNotEmpty()) {
|
||||||
|
append(' ')
|
||||||
|
var count = 0
|
||||||
|
|
||||||
|
for ((name, value) in attr) {
|
||||||
|
if (++count > 1) append(' ')
|
||||||
|
append(name)
|
||||||
|
append("=\"")
|
||||||
|
append(value)
|
||||||
|
append('"')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
append('>')
|
||||||
|
block()
|
||||||
|
append("</")
|
||||||
|
append(tagName)
|
||||||
|
append('>')
|
||||||
|
}
|
||||||
|
|
||||||
|
fun render(syntax: MathSyntax) = render0(syntax, output)
|
||||||
|
|
||||||
|
when (node) {
|
||||||
|
is NumberSyntax -> tag("mn") { append(node.string) }
|
||||||
|
is SymbolSyntax -> tag("mi") { append(node.string) }
|
||||||
|
is OperatorNameSyntax -> tag("mo") { append(node.name) }
|
||||||
|
|
||||||
|
is SpecialSymbolSyntax -> when (node.kind) {
|
||||||
|
SpecialSymbolSyntax.Kind.INFINITY -> tag("mo") { append("∞") }
|
||||||
|
}
|
||||||
|
|
||||||
|
is OperandSyntax -> if (node.parentheses) {
|
||||||
|
tag("mfenced", "open" to "(", "close" to ")", "separators" to "") {
|
||||||
|
render(node.operand)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
render(node.operand)
|
||||||
|
}
|
||||||
|
|
||||||
|
is UnaryOperatorSyntax -> {
|
||||||
|
render(node.prefix)
|
||||||
|
tag("mspace", "width" to "0.167em")
|
||||||
|
render(node.operand)
|
||||||
|
}
|
||||||
|
|
||||||
|
is UnaryPlusSyntax -> {
|
||||||
|
tag("mo") { append('+') }
|
||||||
|
render(node.operand)
|
||||||
|
}
|
||||||
|
|
||||||
|
is UnaryMinusSyntax -> {
|
||||||
|
tag("mo") { append("-") }
|
||||||
|
render(node.operand)
|
||||||
|
}
|
||||||
|
|
||||||
|
is RadicalSyntax -> tag("msqrt") { render(node.operand) }
|
||||||
|
|
||||||
|
is SuperscriptSyntax -> tag("msup") {
|
||||||
|
tag("mrow") { render(node.left) }
|
||||||
|
tag("mrow") { render(node.right) }
|
||||||
|
}
|
||||||
|
|
||||||
|
is SubscriptSyntax -> tag("msub") {
|
||||||
|
tag("mrow") { render(node.left) }
|
||||||
|
tag("mrow") { render(node.right) }
|
||||||
|
}
|
||||||
|
|
||||||
|
is BinaryOperatorSyntax -> {
|
||||||
|
render(node.prefix)
|
||||||
|
|
||||||
|
tag("mfenced", "open" to "(", "close" to ")", "separators" to "") {
|
||||||
|
render(node.left)
|
||||||
|
tag("mo") { append(',') }
|
||||||
|
render(node.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is BinaryPlusSyntax -> {
|
||||||
|
render(node.left)
|
||||||
|
tag("mo") { append('+') }
|
||||||
|
render(node.right)
|
||||||
|
}
|
||||||
|
|
||||||
|
is BinaryMinusSyntax -> {
|
||||||
|
render(node.left)
|
||||||
|
tag("mo") { append('-') }
|
||||||
|
render(node.right)
|
||||||
|
}
|
||||||
|
|
||||||
|
is FractionSyntax -> tag("mfrac") {
|
||||||
|
tag("mrow") {
|
||||||
|
render(node.left)
|
||||||
|
}
|
||||||
|
|
||||||
|
tag("mrow") {
|
||||||
|
render(node.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
is RadicalWithIndexSyntax -> tag("mroot") {
|
||||||
|
tag("mrow") { render(node.right) }
|
||||||
|
tag("mrow") { render(node.left) }
|
||||||
|
}
|
||||||
|
|
||||||
|
is MultiplicationSyntax -> {
|
||||||
|
render(node.left)
|
||||||
|
if (node.times) tag("mo") { append("×") } else tag("mspace", "width" to "0.167em")
|
||||||
|
render(node.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
import space.kscience.kmath.ast.MST
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders [MST] to [MathSyntax].
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public fun interface MathRenderer {
|
||||||
|
/**
|
||||||
|
* Renders [MST] to [MathSyntax].
|
||||||
|
*/
|
||||||
|
public fun render(mst: MST): MathSyntax
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements [MST] render process with sequence of features.
|
||||||
|
*
|
||||||
|
* @property features The applied features.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public open class FeaturedMathRenderer(public val features: List<RenderFeature>) : MathRenderer {
|
||||||
|
public override fun render(mst: MST): MathSyntax {
|
||||||
|
for (feature in features) feature.render(this, mst)?.let { return it }
|
||||||
|
throw UnsupportedOperationException("Renderer $this has no appropriate feature to render node $mst.")
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logical unit of [MST] rendering.
|
||||||
|
*/
|
||||||
|
public fun interface RenderFeature {
|
||||||
|
/**
|
||||||
|
* Renders [MST] to [MathSyntax] in the context of owning renderer.
|
||||||
|
*/
|
||||||
|
public fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends [FeaturedMathRenderer] by adding post-processing stages.
|
||||||
|
*
|
||||||
|
* @property stages The applied stages.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public open class FeaturedMathRendererWithPostProcess(
|
||||||
|
features: List<RenderFeature>,
|
||||||
|
public val stages: List<PostProcessStage>,
|
||||||
|
) : FeaturedMathRenderer(features) {
|
||||||
|
public override fun render(mst: MST): MathSyntax {
|
||||||
|
val res = super.render(mst)
|
||||||
|
for (stage in stages) stage.perform(res)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logical unit of [MathSyntax] post-processing.
|
||||||
|
*/
|
||||||
|
public fun interface PostProcessStage {
|
||||||
|
/**
|
||||||
|
* Performs the specified action over [MathSyntax].
|
||||||
|
*/
|
||||||
|
public fun perform(node: MathSyntax)
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
/**
|
||||||
|
* The default setup of [FeaturedMathRendererWithPostProcess].
|
||||||
|
*/
|
||||||
|
public val Default: FeaturedMathRendererWithPostProcess = FeaturedMathRendererWithPostProcess(
|
||||||
|
listOf(
|
||||||
|
// Printing known operations
|
||||||
|
BinaryPlus.Default,
|
||||||
|
BinaryMinus.Default,
|
||||||
|
UnaryPlus.Default,
|
||||||
|
UnaryMinus.Default,
|
||||||
|
Multiplication.Default,
|
||||||
|
Fraction.Default,
|
||||||
|
Power.Default,
|
||||||
|
SquareRoot.Default,
|
||||||
|
Exponential.Default,
|
||||||
|
InverseTrigonometricOperations.Default,
|
||||||
|
|
||||||
|
// Fallback option for unknown operations - printing them as operator
|
||||||
|
BinaryOperator.Default,
|
||||||
|
UnaryOperator.Default,
|
||||||
|
|
||||||
|
// Pretty printing for numerics
|
||||||
|
PrettyPrintFloats.Default,
|
||||||
|
PrettyPrintIntegers.Default,
|
||||||
|
|
||||||
|
// Printing terminal nodes as string
|
||||||
|
PrintNumeric,
|
||||||
|
PrintSymbolic,
|
||||||
|
),
|
||||||
|
listOf(
|
||||||
|
SimplifyParentheses.Default,
|
||||||
|
BetterMultiplication,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,326 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mathematical typography syntax node.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public sealed class MathSyntax {
|
||||||
|
/**
|
||||||
|
* The parent node of this syntax node.
|
||||||
|
*/
|
||||||
|
public var parent: MathSyntax? = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Terminal node, which should not have any children nodes.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public sealed class TerminalSyntax : MathSyntax()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node containing a certain operation.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public sealed class OperationSyntax : MathSyntax() {
|
||||||
|
/**
|
||||||
|
* The operation token.
|
||||||
|
*/
|
||||||
|
public abstract val operation: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unary node, which has only one child.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public sealed class UnarySyntax : OperationSyntax() {
|
||||||
|
/**
|
||||||
|
* The operand of this node.
|
||||||
|
*/
|
||||||
|
public abstract val operand: MathSyntax
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binary node, which has only two children.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public sealed class BinarySyntax : OperationSyntax() {
|
||||||
|
/**
|
||||||
|
* The left-hand side operand.
|
||||||
|
*/
|
||||||
|
public abstract val left: MathSyntax
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The right-hand side operand.
|
||||||
|
*/
|
||||||
|
public abstract val right: MathSyntax
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a number.
|
||||||
|
*
|
||||||
|
* @property string The digits of number.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class NumberSyntax(public var string: String) : TerminalSyntax()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a symbol.
|
||||||
|
*
|
||||||
|
* @property string The symbol.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class SymbolSyntax(public var string: String) : TerminalSyntax()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents special typing for operator name.
|
||||||
|
*
|
||||||
|
* @property name The operator name.
|
||||||
|
* @see BinaryOperatorSyntax
|
||||||
|
* @see UnaryOperatorSyntax
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class OperatorNameSyntax(public var name: String) : TerminalSyntax()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a usage of special symbols.
|
||||||
|
*
|
||||||
|
* @property kind The kind of symbol.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class SpecialSymbolSyntax(public var kind: Kind) : TerminalSyntax() {
|
||||||
|
/**
|
||||||
|
* The kind of symbol.
|
||||||
|
*/
|
||||||
|
public enum class Kind {
|
||||||
|
/**
|
||||||
|
* The infinity (∞) symbol.
|
||||||
|
*/
|
||||||
|
INFINITY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents operand of a certain operator wrapped with parentheses or not.
|
||||||
|
*
|
||||||
|
* @property operand The operand.
|
||||||
|
* @property parentheses Whether the operand should be wrapped with parentheses.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class OperandSyntax(
|
||||||
|
public val operand: MathSyntax,
|
||||||
|
public var parentheses: Boolean,
|
||||||
|
) : MathSyntax() {
|
||||||
|
init {
|
||||||
|
operand.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents unary, prefix operator syntax (like f x).
|
||||||
|
*
|
||||||
|
* @property prefix The prefix.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class UnaryOperatorSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public var prefix: MathSyntax,
|
||||||
|
public override val operand: OperandSyntax,
|
||||||
|
) : UnarySyntax() {
|
||||||
|
init {
|
||||||
|
operand.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents prefix, unary plus operator.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class UnaryPlusSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public override val operand: OperandSyntax,
|
||||||
|
) : UnarySyntax() {
|
||||||
|
init {
|
||||||
|
operand.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents prefix, unary minus operator.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class UnaryMinusSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public override val operand: OperandSyntax,
|
||||||
|
) : UnarySyntax() {
|
||||||
|
init {
|
||||||
|
operand.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents radical with a node inside it.
|
||||||
|
*
|
||||||
|
* @property operand The radicand.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class RadicalSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public override val operand: MathSyntax,
|
||||||
|
) : UnarySyntax() {
|
||||||
|
init {
|
||||||
|
operand.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a syntax node with superscript (usually, for exponentiation).
|
||||||
|
*
|
||||||
|
* @property left The node.
|
||||||
|
* @property right The superscript.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class SuperscriptSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public override val left: MathSyntax,
|
||||||
|
public override val right: MathSyntax,
|
||||||
|
) : BinarySyntax() {
|
||||||
|
init {
|
||||||
|
left.parent = this
|
||||||
|
right.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents a syntax node with subscript.
|
||||||
|
*
|
||||||
|
* @property left The node.
|
||||||
|
* @property right The subscript.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class SubscriptSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public override val left: MathSyntax,
|
||||||
|
public override val right: MathSyntax,
|
||||||
|
) : BinarySyntax() {
|
||||||
|
init {
|
||||||
|
left.parent = this
|
||||||
|
right.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents binary, prefix operator syntax (like f(a, b)).
|
||||||
|
*
|
||||||
|
* @property prefix The prefix.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class BinaryOperatorSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public var prefix: MathSyntax,
|
||||||
|
public override val left: MathSyntax,
|
||||||
|
public override val right: MathSyntax,
|
||||||
|
) : BinarySyntax() {
|
||||||
|
init {
|
||||||
|
left.parent = this
|
||||||
|
right.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents binary, infix addition.
|
||||||
|
*
|
||||||
|
* @param left The augend.
|
||||||
|
* @param right The addend.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class BinaryPlusSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public override val left: OperandSyntax,
|
||||||
|
public override val right: OperandSyntax,
|
||||||
|
) : BinarySyntax() {
|
||||||
|
init {
|
||||||
|
left.parent = this
|
||||||
|
right.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents binary, infix subtraction.
|
||||||
|
*
|
||||||
|
* @param left The minuend.
|
||||||
|
* @param right The subtrahend.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class BinaryMinusSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public override val left: OperandSyntax,
|
||||||
|
public override val right: OperandSyntax,
|
||||||
|
) : BinarySyntax() {
|
||||||
|
init {
|
||||||
|
left.parent = this
|
||||||
|
right.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents fraction with numerator and denominator.
|
||||||
|
*
|
||||||
|
* @property left The numerator.
|
||||||
|
* @property right The denominator.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class FractionSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public override val left: MathSyntax,
|
||||||
|
public override val right: MathSyntax,
|
||||||
|
) : BinarySyntax() {
|
||||||
|
init {
|
||||||
|
left.parent = this
|
||||||
|
right.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents radical syntax with index.
|
||||||
|
*
|
||||||
|
* @property left The index.
|
||||||
|
* @property right The radicand.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class RadicalWithIndexSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public override val left: MathSyntax,
|
||||||
|
public override val right: MathSyntax,
|
||||||
|
) : BinarySyntax() {
|
||||||
|
init {
|
||||||
|
left.parent = this
|
||||||
|
right.parent = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents binary, infix multiplication in the form of coefficient (2 x) or with operator (x×2).
|
||||||
|
*
|
||||||
|
* @property left The multiplicand.
|
||||||
|
* @property right The multiplier.
|
||||||
|
* @property times whether the times (×) symbol should be used.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public data class MultiplicationSyntax(
|
||||||
|
public override val operation: String,
|
||||||
|
public override val left: OperandSyntax,
|
||||||
|
public override val right: OperandSyntax,
|
||||||
|
public var times: Boolean,
|
||||||
|
) : BinarySyntax() {
|
||||||
|
init {
|
||||||
|
left.parent = this
|
||||||
|
right.parent = this
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstraction of writing [MathSyntax] as a string of an actual markup language. Typical implementation should
|
||||||
|
* involve traversal of MathSyntax with handling each its subtype.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public fun interface SyntaxRenderer {
|
||||||
|
/**
|
||||||
|
* Renders the [MathSyntax] to [output].
|
||||||
|
*/
|
||||||
|
public fun render(node: MathSyntax, output: Appendable)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls [SyntaxRenderer.render] with given [node] and a new [StringBuilder] instance, and returns its content.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public fun SyntaxRenderer.renderWithStringBuilder(node: MathSyntax): String {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
render(node, sb)
|
||||||
|
return sb.toString()
|
||||||
|
}
|
@ -0,0 +1,312 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
import space.kscience.kmath.ast.MST
|
||||||
|
import space.kscience.kmath.ast.rendering.FeaturedMathRenderer.RenderFeature
|
||||||
|
import space.kscience.kmath.operations.*
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints any [MST.Symbolic] as a [SymbolSyntax] containing the [MST.Symbolic.value] of it.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public object PrintSymbolic : RenderFeature {
|
||||||
|
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
||||||
|
if (node !is MST.Symbolic) return null
|
||||||
|
return SymbolSyntax(string = node.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints any [MST.Numeric] as a [NumberSyntax] containing the [Any.toString] result of it.
|
||||||
|
*
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
public object PrintNumeric : RenderFeature {
|
||||||
|
public override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
||||||
|
if (node !is MST.Numeric) return null
|
||||||
|
return NumberSyntax(string = node.value.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun printSignedNumberString(s: String): MathSyntax {
|
||||||
|
if (s.startsWith('-'))
|
||||||
|
return UnaryMinusSyntax(
|
||||||
|
operation = GroupOperations.MINUS_OPERATION,
|
||||||
|
operand = OperandSyntax(
|
||||||
|
operand = NumberSyntax(string = s.removePrefix("-")),
|
||||||
|
parentheses = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
return NumberSyntax(string = s)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special printing for numeric types which are printed in form of
|
||||||
|
* *('-'? (DIGIT+ ('.' DIGIT+)? ('E' '-'? DIGIT+)? | 'Infinity')) | 'NaN'*.
|
||||||
|
*
|
||||||
|
* @property types The suitable types.
|
||||||
|
*/
|
||||||
|
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")
|
||||||
|
|
||||||
|
if ('E' in toString) {
|
||||||
|
val (beforeE, afterE) = toString.split('E')
|
||||||
|
val significand = beforeE.toDouble().toString().removeSuffix(".0")
|
||||||
|
val exponent = afterE.toDouble().toString().removeSuffix(".0")
|
||||||
|
|
||||||
|
return MultiplicationSyntax(
|
||||||
|
operation = RingOperations.TIMES_OPERATION,
|
||||||
|
left = OperandSyntax(operand = NumberSyntax(significand), parentheses = true),
|
||||||
|
right = OperandSyntax(
|
||||||
|
operand = SuperscriptSyntax(
|
||||||
|
operation = PowerOperations.POW_OPERATION,
|
||||||
|
left = NumberSyntax(string = "10"),
|
||||||
|
right = printSignedNumberString(exponent),
|
||||||
|
),
|
||||||
|
parentheses = true,
|
||||||
|
),
|
||||||
|
times = true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (toString.endsWith("Infinity")) {
|
||||||
|
val infty = SpecialSymbolSyntax(SpecialSymbolSyntax.Kind.INFINITY)
|
||||||
|
|
||||||
|
if (toString.startsWith('-'))
|
||||||
|
return UnaryMinusSyntax(
|
||||||
|
operation = GroupOperations.MINUS_OPERATION,
|
||||||
|
operand = OperandSyntax(operand = infty, parentheses = true),
|
||||||
|
)
|
||||||
|
|
||||||
|
return infty
|
||||||
|
}
|
||||||
|
|
||||||
|
return printSignedNumberString(toString)
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
/**
|
||||||
|
* The default instance containing [Float], and [Double].
|
||||||
|
*/
|
||||||
|
public val Default: PrettyPrintFloats = PrettyPrintFloats(setOf(Float::class, Double::class))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special printing for numeric types which are printed in form of *'-'? DIGIT+*.
|
||||||
|
*
|
||||||
|
* @property types The suitable types.
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
|
||||||
|
return printSignedNumberString(node.value.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
/**
|
||||||
|
* The default instance containing [Byte], [Short], [Int], and [Long].
|
||||||
|
*/
|
||||||
|
public val Default: PrettyPrintIntegers =
|
||||||
|
PrettyPrintIntegers(setOf(Byte::class, Short::class, Int::class, Long::class))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract printing of unary operations which discards [MST] if their operation is not in [operations] or its type is
|
||||||
|
* not [MST.Unary].
|
||||||
|
*
|
||||||
|
* @param operations the allowed operations. If `null`, any operation is accepted.
|
||||||
|
*/
|
||||||
|
public abstract class Unary(public val operations: Collection<String>?) : RenderFeature {
|
||||||
|
/**
|
||||||
|
* The actual render function.
|
||||||
|
*/
|
||||||
|
protected abstract fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax?
|
||||||
|
|
||||||
|
public final override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
||||||
|
if (node !is MST.Unary || operations != null && node.operation !in operations) return null
|
||||||
|
return render0(renderer, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Abstract printing of unary operations which discards [MST] if their operation is not in [operations] or its type is
|
||||||
|
* not [MST.Binary].
|
||||||
|
*
|
||||||
|
* @property operations the allowed operations. If `null`, any operation is accepted.
|
||||||
|
*/
|
||||||
|
public abstract class Binary(public val operations: Collection<String>?) : RenderFeature {
|
||||||
|
/**
|
||||||
|
* The actual render function.
|
||||||
|
*/
|
||||||
|
protected abstract fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax?
|
||||||
|
|
||||||
|
public final override fun render(renderer: FeaturedMathRenderer, node: MST): MathSyntax? {
|
||||||
|
if (node !is MST.Binary || operations != null && node.operation !in operations) return null
|
||||||
|
return render0(renderer, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BinaryPlus(operations: Collection<String>?) : Binary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = BinaryPlusSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
left = OperandSyntax(parent.render(node.left), true),
|
||||||
|
right = OperandSyntax(parent.render(node.right), true),
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: BinaryPlus = BinaryPlus(setOf(GroupOperations.PLUS_OPERATION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BinaryMinus(operations: Collection<String>?) : Binary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = BinaryMinusSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
left = OperandSyntax(operand = parent.render(node.left), parentheses = true),
|
||||||
|
right = OperandSyntax(operand = parent.render(node.right), parentheses = true),
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: BinaryMinus = BinaryMinus(setOf(GroupOperations.MINUS_OPERATION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UnaryPlus(operations: Collection<String>?) : Unary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryPlusSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: UnaryPlus = UnaryPlus(setOf(GroupOperations.PLUS_OPERATION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UnaryMinus(operations: Collection<String>?) : Unary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryMinusSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: UnaryMinus = UnaryMinus(setOf(GroupOperations.MINUS_OPERATION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Fraction(operations: Collection<String>?) : Binary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = FractionSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
left = parent.render(node.left),
|
||||||
|
right = parent.render(node.right),
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: Fraction = Fraction(setOf(FieldOperations.DIV_OPERATION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BinaryOperator(operations: Collection<String>?) : Binary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = BinaryOperatorSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
prefix = OperatorNameSyntax(name = node.operation),
|
||||||
|
left = parent.render(node.left),
|
||||||
|
right = parent.render(node.right),
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: BinaryOperator = BinaryOperator(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UnaryOperator(operations: Collection<String>?) : Unary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryOperatorSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
prefix = OperatorNameSyntax(node.operation),
|
||||||
|
operand = OperandSyntax(parent.render(node.value), true),
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: UnaryOperator = UnaryOperator(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Power(operations: Collection<String>?) : Binary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = SuperscriptSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
left = OperandSyntax(parent.render(node.left), true),
|
||||||
|
right = OperandSyntax(parent.render(node.right), true),
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: Power = Power(setOf(PowerOperations.POW_OPERATION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SquareRoot(operations: Collection<String>?) : Unary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax =
|
||||||
|
RadicalSyntax(operation = node.operation, operand = parent.render(node.value))
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: SquareRoot = SquareRoot(setOf(PowerOperations.SQRT_OPERATION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Exponential(operations: Collection<String>?) : Unary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = SuperscriptSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
left = SymbolSyntax(string = "e"),
|
||||||
|
right = parent.render(node.value),
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: Exponential = Exponential(setOf(ExponentialOperations.EXP_OPERATION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Multiplication(operations: Collection<String>?) : Binary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Binary): MathSyntax = MultiplicationSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
left = OperandSyntax(operand = parent.render(node.left), parentheses = true),
|
||||||
|
right = OperandSyntax(operand = parent.render(node.right), parentheses = true),
|
||||||
|
times = true,
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: Multiplication = Multiplication(setOf(
|
||||||
|
RingOperations.TIMES_OPERATION,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class InverseTrigonometricOperations(operations: Collection<String>?) : Unary(operations) {
|
||||||
|
public override fun render0(parent: FeaturedMathRenderer, node: MST.Unary): MathSyntax = UnaryOperatorSyntax(
|
||||||
|
operation = node.operation,
|
||||||
|
prefix = SuperscriptSyntax(
|
||||||
|
operation = PowerOperations.POW_OPERATION,
|
||||||
|
left = OperatorNameSyntax(name = node.operation.removePrefix("a")),
|
||||||
|
right = UnaryMinusSyntax(
|
||||||
|
operation = GroupOperations.MINUS_OPERATION,
|
||||||
|
operand = OperandSyntax(operand = NumberSyntax(string = "1"), parentheses = true),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
operand = OperandSyntax(operand = parent.render(node.value), parentheses = true),
|
||||||
|
)
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public val Default: InverseTrigonometricOperations = InverseTrigonometricOperations(setOf(
|
||||||
|
TrigonometricOperations.ACOS_OPERATION,
|
||||||
|
TrigonometricOperations.ASIN_OPERATION,
|
||||||
|
TrigonometricOperations.ATAN_OPERATION,
|
||||||
|
ExponentialOperations.ACOSH_OPERATION,
|
||||||
|
ExponentialOperations.ASINH_OPERATION,
|
||||||
|
ExponentialOperations.ATANH_OPERATION,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,197 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
import space.kscience.kmath.operations.FieldOperations
|
||||||
|
import space.kscience.kmath.operations.GroupOperations
|
||||||
|
import space.kscience.kmath.operations.PowerOperations
|
||||||
|
import space.kscience.kmath.operations.RingOperations
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes unnecessary times (×) symbols from [MultiplicationSyntax].
|
||||||
|
*
|
||||||
|
* @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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes unnecessary parentheses from [OperandSyntax].
|
||||||
|
*
|
||||||
|
* @property precedenceFunction Returns the precedence number for syntax node. Higher number is lower priority.
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
|
||||||
|
is OperandSyntax -> {
|
||||||
|
val isRightOfSuperscript =
|
||||||
|
(node.parent is SuperscriptSyntax) && (node.parent as SuperscriptSyntax).right === node
|
||||||
|
|
||||||
|
val precedence = precedenceFunction(node.operand)
|
||||||
|
|
||||||
|
val needParenthesesByPrecedence = when (val parent = node.parent) {
|
||||||
|
null -> false
|
||||||
|
|
||||||
|
is BinarySyntax -> {
|
||||||
|
val parentPrecedence = precedenceFunction(parent)
|
||||||
|
|
||||||
|
parentPrecedence < precedence ||
|
||||||
|
parentPrecedence == precedence && parentPrecedence != 0 && node === parent.right
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> precedence > precedenceFunction(parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
node.parentheses = !isRightOfSuperscript
|
||||||
|
&& (needParenthesesByPrecedence || node.parent is UnaryOperatorSyntax)
|
||||||
|
|
||||||
|
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 MultiplicationSyntax -> {
|
||||||
|
perform(node.left)
|
||||||
|
perform(node.right)
|
||||||
|
}
|
||||||
|
|
||||||
|
is RadicalWithIndexSyntax -> {
|
||||||
|
perform(node.left)
|
||||||
|
perform(node.right)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
/**
|
||||||
|
* The default configuration of [SimplifyParentheses] where power is 1, multiplicative operations are 2,
|
||||||
|
* additive operations are 3.
|
||||||
|
*/
|
||||||
|
public val Default: SimplifyParentheses = SimplifyParentheses {
|
||||||
|
when (it) {
|
||||||
|
is TerminalSyntax -> 0
|
||||||
|
is UnarySyntax -> 2
|
||||||
|
|
||||||
|
is BinarySyntax -> when (it.operation) {
|
||||||
|
PowerOperations.POW_OPERATION -> 1
|
||||||
|
RingOperations.TIMES_OPERATION -> 3
|
||||||
|
FieldOperations.DIV_OPERATION -> 3
|
||||||
|
GroupOperations.MINUS_OPERATION -> 4
|
||||||
|
GroupOperations.PLUS_OPERATION -> 4
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -68,7 +68,7 @@ internal fun <T> MST.compileWith(algebra: Algebra<T>): Expression<T> {
|
|||||||
/**
|
/**
|
||||||
* Compiles an [MST] to ESTree generated expression using given algebra.
|
* Compiles an [MST] to ESTree generated expression using given algebra.
|
||||||
*
|
*
|
||||||
* @author Alexander Nozik.
|
* @author Iaroslav Postovalov
|
||||||
*/
|
*/
|
||||||
public fun <T : Any> Algebra<T>.expression(mst: MST): Expression<T> =
|
public fun <T : Any> Algebra<T>.expression(mst: MST): Expression<T> =
|
||||||
mst.compileWith(this)
|
mst.compileWith(this)
|
||||||
@ -76,7 +76,7 @@ public fun <T : Any> Algebra<T>.expression(mst: MST): Expression<T> =
|
|||||||
/**
|
/**
|
||||||
* Optimizes performance of an [MstExpression] by compiling it into ESTree generated expression.
|
* Optimizes performance of an [MstExpression] by compiling it into ESTree generated expression.
|
||||||
*
|
*
|
||||||
* @author Alexander Nozik.
|
* @author Iaroslav Postovalov
|
||||||
*/
|
*/
|
||||||
public fun <T : Any> MstExpression<T, Algebra<T>>.compile(): Expression<T> =
|
public fun <T : Any> MstExpression<T, Algebra<T>>.compile(): Expression<T> =
|
||||||
mst.compileWith(algebra)
|
mst.compileWith(algebra)
|
||||||
|
@ -73,7 +73,7 @@ internal fun <T : Any> MST.compileWith(type: Class<T>, algebra: Algebra<T>): Exp
|
|||||||
/**
|
/**
|
||||||
* Compiles an [MST] to ASM using given algebra.
|
* Compiles an [MST] to ASM using given algebra.
|
||||||
*
|
*
|
||||||
* @author Alexander Nozik.
|
* @author Alexander Nozik
|
||||||
*/
|
*/
|
||||||
public inline fun <reified T : Any> Algebra<T>.expression(mst: MST): Expression<T> =
|
public inline fun <reified T : Any> Algebra<T>.expression(mst: MST): Expression<T> =
|
||||||
mst.compileWith(T::class.java, this)
|
mst.compileWith(T::class.java, this)
|
||||||
@ -81,7 +81,7 @@ public inline fun <reified T : Any> Algebra<T>.expression(mst: MST): Expression<
|
|||||||
/**
|
/**
|
||||||
* Optimizes performance of an [MstExpression] using ASM codegen.
|
* Optimizes performance of an [MstExpression] using ASM codegen.
|
||||||
*
|
*
|
||||||
* @author Alexander Nozik.
|
* @author Alexander Nozik
|
||||||
*/
|
*/
|
||||||
public inline fun <reified T : Any> MstExpression<T, Algebra<T>>.compile(): Expression<T> =
|
public inline fun <reified T : Any> MstExpression<T, Algebra<T>>.compile(): Expression<T> =
|
||||||
mst.compileWith(T::class.java, algebra)
|
mst.compileWith(T::class.java, algebra)
|
||||||
|
@ -21,7 +21,8 @@ import space.kscience.kmath.operations.RingOperations
|
|||||||
/**
|
/**
|
||||||
* better-parse implementation of grammar defined in the ArithmeticsEvaluator.g4.
|
* better-parse implementation of grammar defined in the ArithmeticsEvaluator.g4.
|
||||||
*
|
*
|
||||||
* @author Alexander Nozik and Iaroslav Postovalov
|
* @author Alexander Nozik
|
||||||
|
* @author Iaroslav Postovalov
|
||||||
*/
|
*/
|
||||||
public object ArithmeticsEvaluator : Grammar<MST>() {
|
public object ArithmeticsEvaluator : Grammar<MST>() {
|
||||||
// TODO replace with "...".toRegex() when better-parse 0.4.1 is released
|
// TODO replace with "...".toRegex() when better-parse 0.4.1 is released
|
||||||
|
@ -0,0 +1,90 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
import space.kscience.kmath.ast.MST.Numeric
|
||||||
|
import space.kscience.kmath.ast.rendering.TestUtils.testLatex
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
internal class TestFeatures {
|
||||||
|
@Test
|
||||||
|
fun printSymbolic() = testLatex("x", "x")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun printNumeric() {
|
||||||
|
val num = object : Number() {
|
||||||
|
override fun toByte(): Byte = throw UnsupportedOperationException()
|
||||||
|
override fun toChar(): Char = throw UnsupportedOperationException()
|
||||||
|
override fun toDouble(): Double = throw UnsupportedOperationException()
|
||||||
|
override fun toFloat(): Float = throw UnsupportedOperationException()
|
||||||
|
override fun toInt(): Int = throw UnsupportedOperationException()
|
||||||
|
override fun toLong(): Long = throw UnsupportedOperationException()
|
||||||
|
override fun toShort(): Short = throw UnsupportedOperationException()
|
||||||
|
override fun toString(): String = "foo"
|
||||||
|
}
|
||||||
|
|
||||||
|
testLatex(Numeric(num), "foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun prettyPrintFloats() {
|
||||||
|
testLatex(Numeric(Double.NaN), "NaN")
|
||||||
|
testLatex(Numeric(Double.POSITIVE_INFINITY), "\\infty")
|
||||||
|
testLatex(Numeric(Double.NEGATIVE_INFINITY), "-\\infty")
|
||||||
|
testLatex(Numeric(1.0), "1")
|
||||||
|
testLatex(Numeric(-1.0), "-1")
|
||||||
|
testLatex(Numeric(1.42), "1.42")
|
||||||
|
testLatex(Numeric(-1.42), "-1.42")
|
||||||
|
testLatex(Numeric(1.1e10), "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}")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun prettyPrintIntegers() {
|
||||||
|
testLatex(Numeric(42), "42")
|
||||||
|
testLatex(Numeric(-42), "-42")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun binaryPlus() = testLatex("2+2", "2+2")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun binaryMinus() = testLatex("2-2", "2-2")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun fraction() = testLatex("2/2", "\\frac{2}{2}")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun binaryOperator() = testLatex("f(x, y)", "\\operatorname{f}\\left(x,y\\right)")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unaryOperator() = testLatex("f(x)", "\\operatorname{f}\\,\\left(x\\right)")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun power() = testLatex("x^y", "x^{y}")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun squareRoot() = testLatex("sqrt(x)", "\\sqrt{x}")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun exponential() = testLatex("exp(x)", "e^{x}")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun multiplication() = testLatex("x*1", "x\\times1")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun inverseTrigonometry() {
|
||||||
|
testLatex("asin(x)", "\\operatorname{sin}^{-1}\\,\\left(x\\right)")
|
||||||
|
testLatex("asinh(x)", "\\operatorname{sinh}^{-1}\\,\\left(x\\right)")
|
||||||
|
testLatex("acos(x)", "\\operatorname{cos}^{-1}\\,\\left(x\\right)")
|
||||||
|
testLatex("acosh(x)", "\\operatorname{cosh}^{-1}\\,\\left(x\\right)")
|
||||||
|
testLatex("atan(x)", "\\operatorname{tan}^{-1}\\,\\left(x\\right)")
|
||||||
|
testLatex("atanh(x)", "\\operatorname{tanh}^{-1}\\,\\left(x\\right)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// @Test
|
||||||
|
// fun unaryPlus() {
|
||||||
|
// testLatex("+1", "+1")
|
||||||
|
// testLatex("+1", "++1")
|
||||||
|
// }
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
import space.kscience.kmath.ast.MST
|
||||||
|
import space.kscience.kmath.ast.rendering.TestUtils.testLatex
|
||||||
|
import space.kscience.kmath.operations.GroupOperations
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
internal class TestLatex {
|
||||||
|
@Test
|
||||||
|
fun number() = testLatex("42", "42")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun symbol() = testLatex("x", "x")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun operatorName() = testLatex("sin(1)", "\\operatorname{sin}\\,\\left(1\\right)")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun specialSymbol() = testLatex(MST.Numeric(Double.POSITIVE_INFINITY), "\\infty")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun operand() {
|
||||||
|
testLatex("sin(1)", "\\operatorname{sin}\\,\\left(1\\right)")
|
||||||
|
testLatex("1+1", "1+1")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unaryOperator() = testLatex("sin(1)", "\\operatorname{sin}\\,\\left(1\\right)")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unaryPlus() = testLatex(MST.Unary(GroupOperations.PLUS_OPERATION, MST.Numeric(1)), "+1")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unaryMinus() = testLatex("-x", "-x")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun radical() = testLatex("sqrt(x)", "\\sqrt{x}")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun superscript() = testLatex("x^y", "x^{y}")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun subscript() = testLatex(SubscriptSyntax("", SymbolSyntax("x"), NumberSyntax("123")), "x_{123}")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun binaryOperator() = testLatex("f(x, y)", "\\operatorname{f}\\left(x,y\\right)")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun binaryPlus() = testLatex("x+x", "x+x")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun binaryMinus() = testLatex("x-x", "x-x")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun fraction() = testLatex("x/x", "\\frac{x}{x}")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun radicalWithIndex() = testLatex(RadicalWithIndexSyntax("", SymbolSyntax("x"), SymbolSyntax("y")), "\\sqrt[x]{y}")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun multiplication() {
|
||||||
|
testLatex("x*1", "x\\times1")
|
||||||
|
testLatex("1*x", "1\\,x")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,84 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
import space.kscience.kmath.ast.MST
|
||||||
|
import space.kscience.kmath.ast.rendering.TestUtils.testMathML
|
||||||
|
import space.kscience.kmath.operations.GroupOperations
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
internal class TestMathML {
|
||||||
|
@Test
|
||||||
|
fun number() = testMathML("42", "<mn>42</mn>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun symbol() = testMathML("x", "<mi>x</mi>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun operatorName() = testMathML(
|
||||||
|
"sin(1)",
|
||||||
|
"<mo>sin</mo><mspace width=\"0.167em\"></mspace><mfenced open=\"(\" close=\")\" separators=\"\"><mn>1</mn></mfenced>",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun specialSymbol() = testMathML(MST.Numeric(Double.POSITIVE_INFINITY), "<mo>∞</mo>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun operand() {
|
||||||
|
testMathML(
|
||||||
|
"sin(1)",
|
||||||
|
"<mo>sin</mo><mspace width=\"0.167em\"></mspace><mfenced open=\"(\" close=\")\" separators=\"\"><mn>1</mn></mfenced>",
|
||||||
|
)
|
||||||
|
|
||||||
|
testMathML("1+1", "<mn>1</mn><mo>+</mo><mn>1</mn>")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unaryOperator() = testMathML(
|
||||||
|
"sin(1)",
|
||||||
|
"<mo>sin</mo><mspace width=\"0.167em\"></mspace><mfenced open=\"(\" close=\")\" separators=\"\"><mn>1</mn></mfenced>",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unaryPlus() =
|
||||||
|
testMathML(MST.Unary(GroupOperations.PLUS_OPERATION, MST.Numeric(1)), "<mo>+</mo><mn>1</mn>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun unaryMinus() = testMathML("-x", "<mo>-</mo><mi>x</mi>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun radical() = testMathML("sqrt(x)", "<msqrt><mi>x</mi></msqrt>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun superscript() = testMathML("x^y", "<msup><mrow><mi>x</mi></mrow><mrow><mi>y</mi></mrow></msup>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun subscript() = testMathML(
|
||||||
|
SubscriptSyntax("", SymbolSyntax("x"), NumberSyntax("123")),
|
||||||
|
"<msub><mrow><mi>x</mi></mrow><mrow><mn>123</mn></mrow></msub>",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun binaryOperator() = testMathML(
|
||||||
|
"f(x, y)",
|
||||||
|
"<mo>f</mo><mfenced open=\"(\" close=\")\" separators=\"\"><mi>x</mi><mo>,</mo><mi>y</mi></mfenced>",
|
||||||
|
)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun binaryPlus() = testMathML("x+x", "<mi>x</mi><mo>+</mo><mi>x</mi>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun binaryMinus() = testMathML("x-x", "<mi>x</mi><mo>-</mo><mi>x</mi>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun fraction() = testMathML("x/x", "<mfrac><mrow><mi>x</mi></mrow><mrow><mi>x</mi></mrow></mfrac>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun radicalWithIndex() =
|
||||||
|
testMathML(RadicalWithIndexSyntax("", SymbolSyntax("x"), SymbolSyntax("y")),
|
||||||
|
"<mroot><mrow><mi>y</mi></mrow><mrow><mi>x</mi></mrow></mroot>")
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun multiplication() {
|
||||||
|
testMathML("x*1", "<mi>x</mi><mo>×</mo><mn>1</mn>")
|
||||||
|
testMathML("1*x", "<mn>1</mn><mspace width=\"0.167em\"></mspace><mi>x</mi>")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
import space.kscience.kmath.ast.rendering.TestUtils.testLatex
|
||||||
|
import kotlin.test.Test
|
||||||
|
|
||||||
|
internal class TestStages {
|
||||||
|
@Test
|
||||||
|
fun betterMultiplication() {
|
||||||
|
testLatex("a*1", "a\\times1")
|
||||||
|
testLatex("1*(2/3)", "1\\times\\left(\\frac{2}{3}\\right)")
|
||||||
|
testLatex("1*1", "1\\times1")
|
||||||
|
testLatex("2e10", "2\\times10^{10}")
|
||||||
|
testLatex("2*x", "2\\,x")
|
||||||
|
testLatex("2*(x+1)", "2\\,\\left(x+1\\right)")
|
||||||
|
testLatex("x*y", "x\\,y")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun parentheses() {
|
||||||
|
testLatex("(x+1)", "x+1")
|
||||||
|
testLatex("x*x*x", "x\\,x\\,x")
|
||||||
|
testLatex("(x+x)*x", "\\left(x+x\\right)\\,x")
|
||||||
|
testLatex("x+x*x", "x+x\\,x")
|
||||||
|
testLatex("x+x^x*x+x", "x+x^{x}\\,x+x")
|
||||||
|
testLatex("(x+x)^x+x*x", "\\left(x+x\\right)^{x}+x\\,x")
|
||||||
|
testLatex("x^(x+x)", "x^{x+x}")
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package space.kscience.kmath.ast.rendering
|
||||||
|
|
||||||
|
import space.kscience.kmath.ast.MST
|
||||||
|
import space.kscience.kmath.ast.parseMath
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
internal object TestUtils {
|
||||||
|
private fun mathSyntax(mst: MST) = FeaturedMathRendererWithPostProcess.Default.render(mst)
|
||||||
|
private fun latex(mst: MST) = LatexSyntaxRenderer.renderWithStringBuilder(mathSyntax(mst))
|
||||||
|
private fun mathML(mst: MST) = MathMLSyntaxRenderer.renderWithStringBuilder(mathSyntax(mst))
|
||||||
|
|
||||||
|
internal fun testLatex(mst: MST, expectedLatex: String) = assertEquals(
|
||||||
|
expected = expectedLatex,
|
||||||
|
actual = latex(mst),
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun testLatex(expression: String, expectedLatex: String) = assertEquals(
|
||||||
|
expected = expectedLatex,
|
||||||
|
actual = latex(expression.parseMath()),
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun testLatex(expression: MathSyntax, expectedLatex: String) = assertEquals(
|
||||||
|
expected = expectedLatex,
|
||||||
|
actual = LatexSyntaxRenderer.renderWithStringBuilder(expression),
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun testMathML(mst: MST, expectedMathML: String) = assertEquals(
|
||||||
|
expected = "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow>$expectedMathML</mrow></math>",
|
||||||
|
actual = mathML(mst),
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun testMathML(expression: String, expectedMathML: String) = assertEquals(
|
||||||
|
expected = "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow>$expectedMathML</mrow></math>",
|
||||||
|
actual = mathML(expression.parseMath()),
|
||||||
|
)
|
||||||
|
|
||||||
|
internal fun testMathML(expression: MathSyntax, expectedMathML: String) = assertEquals(
|
||||||
|
expected = "<math xmlns=\"http://www.w3.org/1998/Math/MathML\"><mrow>$expectedMathML</mrow></math>",
|
||||||
|
actual = MathMLSyntaxRenderer.renderWithStringBuilder(expression),
|
||||||
|
)
|
||||||
|
}
|
@ -8,7 +8,7 @@ Complex and hypercomplex number systems in KMath.
|
|||||||
|
|
||||||
## Artifact:
|
## Artifact:
|
||||||
|
|
||||||
The Maven coordinates of this project are `space.kscience:kmath-complex:0.3.0-dev-3`.
|
The Maven coordinates of this project are `space.kscience:kmath-complex:0.3.0-dev-4`.
|
||||||
|
|
||||||
**Gradle:**
|
**Gradle:**
|
||||||
```gradle
|
```gradle
|
||||||
@ -19,7 +19,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'space.kscience:kmath-complex:0.3.0-dev-3'
|
implementation 'space.kscience:kmath-complex:0.3.0-dev-4'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**Gradle Kotlin DSL:**
|
**Gradle Kotlin DSL:**
|
||||||
@ -31,6 +31,6 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("space.kscience:kmath-complex:0.3.0-dev-3")
|
implementation("space.kscience:kmath-complex:0.3.0-dev-4")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -15,7 +15,7 @@ performance calculations to code generation.
|
|||||||
|
|
||||||
## Artifact:
|
## Artifact:
|
||||||
|
|
||||||
The Maven coordinates of this project are `space.kscience:kmath-core:0.3.0-dev-3`.
|
The Maven coordinates of this project are `space.kscience:kmath-core:0.3.0-dev-4`.
|
||||||
|
|
||||||
**Gradle:**
|
**Gradle:**
|
||||||
```gradle
|
```gradle
|
||||||
@ -26,7 +26,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'space.kscience:kmath-core:0.3.0-dev-3'
|
implementation 'space.kscience:kmath-core:0.3.0-dev-4'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**Gradle Kotlin DSL:**
|
**Gradle Kotlin DSL:**
|
||||||
@ -38,6 +38,6 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("space.kscience:kmath-core:0.3.0-dev-3")
|
implementation("space.kscience:kmath-core:0.3.0-dev-4")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -18,7 +18,8 @@ private typealias TBase = ULong
|
|||||||
/**
|
/**
|
||||||
* Kotlin Multiplatform implementation of Big Integer numbers (KBigInteger).
|
* Kotlin Multiplatform implementation of Big Integer numbers (KBigInteger).
|
||||||
*
|
*
|
||||||
* @author Robert Drynkin (https://github.com/robdrynkin) and Peter Klimai (https://github.com/pklimai)
|
* @author Robert Drynkin
|
||||||
|
* @author Peter Klimai
|
||||||
*/
|
*/
|
||||||
@OptIn(UnstableKMathAPI::class)
|
@OptIn(UnstableKMathAPI::class)
|
||||||
public object BigIntField : Field<BigInt>, NumbersAddOperations<BigInt>, ScaleOperations<BigInt> {
|
public object BigIntField : Field<BigInt>, NumbersAddOperations<BigInt>, ScaleOperations<BigInt> {
|
||||||
|
@ -9,7 +9,7 @@ EJML based linear algebra implementation.
|
|||||||
|
|
||||||
## Artifact:
|
## Artifact:
|
||||||
|
|
||||||
The Maven coordinates of this project are `space.kscience:kmath-ejml:0.3.0-dev-3`.
|
The Maven coordinates of this project are `space.kscience:kmath-ejml:0.3.0-dev-4`.
|
||||||
|
|
||||||
**Gradle:**
|
**Gradle:**
|
||||||
```gradle
|
```gradle
|
||||||
@ -20,7 +20,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'space.kscience:kmath-ejml:0.3.0-dev-3'
|
implementation 'space.kscience:kmath-ejml:0.3.0-dev-4'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**Gradle Kotlin DSL:**
|
**Gradle Kotlin DSL:**
|
||||||
@ -32,6 +32,6 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("space.kscience:kmath-ejml:0.3.0-dev-3")
|
implementation("space.kscience:kmath-ejml:0.3.0-dev-4")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -9,7 +9,7 @@ Specialization of KMath APIs for Double numbers.
|
|||||||
|
|
||||||
## Artifact:
|
## Artifact:
|
||||||
|
|
||||||
The Maven coordinates of this project are `space.kscience:kmath-for-real:0.3.0-dev-3`.
|
The Maven coordinates of this project are `space.kscience:kmath-for-real:0.3.0-dev-4`.
|
||||||
|
|
||||||
**Gradle:**
|
**Gradle:**
|
||||||
```gradle
|
```gradle
|
||||||
@ -20,7 +20,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'space.kscience:kmath-for-real:0.3.0-dev-3'
|
implementation 'space.kscience:kmath-for-real:0.3.0-dev-4'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**Gradle Kotlin DSL:**
|
**Gradle Kotlin DSL:**
|
||||||
@ -32,6 +32,6 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("space.kscience:kmath-for-real:0.3.0-dev-3")
|
implementation("space.kscience:kmath-for-real:0.3.0-dev-4")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -10,7 +10,7 @@ Functions and interpolations.
|
|||||||
|
|
||||||
## Artifact:
|
## Artifact:
|
||||||
|
|
||||||
The Maven coordinates of this project are `space.kscience:kmath-functions:0.3.0-dev-3`.
|
The Maven coordinates of this project are `space.kscience:kmath-functions:0.3.0-dev-4`.
|
||||||
|
|
||||||
**Gradle:**
|
**Gradle:**
|
||||||
```gradle
|
```gradle
|
||||||
@ -21,7 +21,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'space.kscience:kmath-functions:0.3.0-dev-3'
|
implementation 'space.kscience:kmath-functions:0.3.0-dev-4'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**Gradle Kotlin DSL:**
|
**Gradle Kotlin DSL:**
|
||||||
@ -33,6 +33,6 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("space.kscience:kmath-functions:0.3.0-dev-3")
|
implementation("space.kscience:kmath-functions:0.3.0-dev-4")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -9,7 +9,7 @@ ND4J based implementations of KMath abstractions.
|
|||||||
|
|
||||||
## Artifact:
|
## Artifact:
|
||||||
|
|
||||||
The Maven coordinates of this project are `space.kscience:kmath-nd4j:0.3.0-dev-3`.
|
The Maven coordinates of this project are `space.kscience:kmath-nd4j:0.3.0-dev-4`.
|
||||||
|
|
||||||
**Gradle:**
|
**Gradle:**
|
||||||
```gradle
|
```gradle
|
||||||
@ -20,7 +20,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'space.kscience:kmath-nd4j:0.3.0-dev-3'
|
implementation 'space.kscience:kmath-nd4j:0.3.0-dev-4'
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
**Gradle Kotlin DSL:**
|
**Gradle Kotlin DSL:**
|
||||||
@ -32,7 +32,7 @@ repositories {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("space.kscience:kmath-nd4j:0.3.0-dev-3")
|
implementation("space.kscience:kmath-nd4j:0.3.0-dev-4")
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user