diff --git a/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestFeatures.kt b/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestFeatures.kt
new file mode 100644
index 000000000..1584293ce
--- /dev/null
+++ b/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestFeatures.kt
@@ -0,0 +1,100 @@
+/*
+ * 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
+
+import space.kscience.kmath.ast.rendering.TestUtils.testLatex
+import space.kscience.kmath.expressions.MST.Numeric
+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 prettyPrintPi() {
+ testLatex("pi", "\\pi")
+ }
+
+ @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")
+// }
+}
diff --git a/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestLatex.kt b/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestLatex.kt
new file mode 100644
index 000000000..6322df25d
--- /dev/null
+++ b/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestLatex.kt
@@ -0,0 +1,73 @@
+/*
+ * 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
+
+import space.kscience.kmath.ast.rendering.TestUtils.testLatex
+import space.kscience.kmath.expressions.MST
+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")
+ testLatex("pi", "\\pi")
+ }
+
+ @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")
+ }
+}
diff --git a/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestMathML.kt b/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestMathML.kt
new file mode 100644
index 000000000..2d7bfad19
--- /dev/null
+++ b/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestMathML.kt
@@ -0,0 +1,92 @@
+/*
+ * 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
+
+import space.kscience.kmath.ast.rendering.TestUtils.testMathML
+import space.kscience.kmath.expressions.MST
+import space.kscience.kmath.operations.GroupOperations
+import kotlin.test.Test
+
+internal class TestMathML {
+ @Test
+ fun number() = testMathML("42", "42")
+
+ @Test
+ fun symbol() = testMathML("x", "x")
+
+ @Test
+ fun operatorName() = testMathML(
+ "sin(1)",
+ "sin1",
+ )
+
+ @Test
+ fun specialSymbol() {
+ testMathML(MST.Numeric(Double.POSITIVE_INFINITY), "∞")
+ testMathML("pi", "π")
+ }
+
+ @Test
+ fun operand() {
+ testMathML(
+ "sin(1)",
+ "sin1",
+ )
+
+ testMathML("1+1", "1+1")
+ }
+
+ @Test
+ fun unaryOperator() = testMathML(
+ "sin(1)",
+ "sin1",
+ )
+
+ @Test
+ fun unaryPlus() =
+ testMathML(MST.Unary(GroupOperations.PLUS_OPERATION, MST.Numeric(1)), "+1")
+
+ @Test
+ fun unaryMinus() = testMathML("-x", "-x")
+
+ @Test
+ fun radical() = testMathML("sqrt(x)", "x")
+
+ @Test
+ fun superscript() = testMathML("x^y", "xy")
+
+ @Test
+ fun subscript() = testMathML(
+ SubscriptSyntax("", SymbolSyntax("x"), NumberSyntax("123")),
+ "x123",
+ )
+
+ @Test
+ fun binaryOperator() = testMathML(
+ "f(x, y)",
+ "fx,y",
+ )
+
+ @Test
+ fun binaryPlus() = testMathML("x+x", "x+x")
+
+ @Test
+ fun binaryMinus() = testMathML("x-x", "x-x")
+
+ @Test
+ fun fraction() = testMathML("x/x", "xx")
+
+ @Test
+ fun radicalWithIndex() =
+ testMathML(RadicalWithIndexSyntax("", SymbolSyntax("x"), SymbolSyntax("y")),
+ "yx")
+
+ @Test
+ fun multiplication() {
+ testMathML("x*1", "x×1")
+ testMathML("1*x", "1x")
+ }
+}
diff --git a/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestStages.kt b/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestStages.kt
new file mode 100644
index 000000000..a4017fdb4
--- /dev/null
+++ b/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestStages.kt
@@ -0,0 +1,33 @@
+/*
+ * 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
+
+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}")
+ }
+}
diff --git a/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestUtils.kt b/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestUtils.kt
new file mode 100644
index 000000000..7c9400532
--- /dev/null
+++ b/kmath-ast/src/commonTest/kotlin/space/kscisnce/kmath/ast/rendering/TestUtils.kt
@@ -0,0 +1,46 @@
+/*
+ * 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
+
+import space.kscience.kmath.ast.parseMath
+import space.kscience.kmath.expressions.MST
+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 = "",
+ actual = mathML(mst),
+ )
+
+ internal fun testMathML(expression: String, expectedMathML: String) = assertEquals(
+ expected = "",
+ actual = mathML(expression.parseMath()),
+ )
+
+ internal fun testMathML(expression: MathSyntax, expectedMathML: String) = assertEquals(
+ expected = "",
+ actual = MathMLSyntaxRenderer.renderWithStringBuilder(expression),
+ )
+}