fixed Decimal (and other such classes ) comparison

This commit is contained in:
sergeych committed 2026-03-29 01:29:48 +03:00
1 parent 05d7432b37
commit aa9565b40b
4 files changed
+630

No files matched your search

@@ -0,0 +1,252 @@
/*
* Copyright 2026 Sergey S. Chernov real.sergeych@gmail.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package net.sergeych.lyng
import com.ionspin.kotlin.bignum.decimal.BigDecimal
import kotlinx.coroutines.test.runTest
import kotlin.test.Test
import kotlin.test.assertEquals
class DecimalModuleTest {
@Test
fun testDecimalModuleFactoriesAndConversions() = runTest {
val scope = Script.newScope()
scope.eval(
"""
import lyng.decimal
assertEquals("12.34", Decimal.fromString("12.34").toStringExpanded())
assertEquals("1", Decimal.fromInt(1).toStringExpanded())
assertEquals("2.5", "2.5".d.toStringExpanded())
assertEquals("1", 1.d.toStringExpanded())
assertEquals("2.2", 2.2.d.toStringExpanded())
assertEquals("3", (1 + 2).d.toStringExpanded())
assertEquals("1.5", (1 + 0.5).d.toStringExpanded())
assertEquals("3", (1 + 2.d).toStringExpanded())
assertEquals("3", (2.d + 1).toStringExpanded())
assertEquals(2.5, "2.5".d.toReal())
assertEquals(2, "2.5".d.toInt())
assertEquals(2.2, 2.2.d.toReal())
assertEquals("0.30000000000000004", (0.1 + 0.2).d.toStringExpanded())
""".trimIndent()
)
}
@Test
fun testDecimalModuleMixedIntOperators() = runTest {
val scope = Script.newScope()
scope.eval(
"""
import lyng.decimal
assertEquals(3.d, 1 + 2.d)
assertEquals(3.d, 2.d + 1)
assertEquals(1.d, 3 - 2.d)
assertEquals(1.d, 3.d - 2)
assertEquals(8.d, 4 * 2.d)
assertEquals(8.d, 4.d * 2)
assertEquals(4.d, 8 / 2.d)
assertEquals(4.d, 8.d / 2)
assertEquals(1.d, 7 % 2.d)
assertEquals(1.d, 7.d % 2)
assert(1 < 2.d)
assert(2 <= 2.d)
assert(3 > 2.d)
assert(3.d > 2)
assert(2 == 2.d)
assert(2.d == 2)
""".trimIndent()
)
}
@Test
fun testDecimalDivisionUsesDefaultContext() = runTest {
val scope = Script.newScope()
scope.eval(
"""
import lyng.decimal
assertEquals("0.125", (1.d / 8.d).toStringExpanded())
assertEquals("0.3333333333333333333333333333333333", (1.d / 3.d).toStringExpanded())
assertEquals("0.6666666666666666666666666666666667", ("2".d / 3.d).toStringExpanded())
""".trimIndent()
)
}
@Test
fun testWithDecimalContextOverridesDivisionContext() = runTest {
val scope = Script.newScope()
scope.eval(
"""
import lyng.decimal
assertEquals("0.3333333333333333333333333333333333", (1.d / 3.d).toStringExpanded())
assertEquals("0.3333333333", withDecimalContext(10) { (1.d / 3.d).toStringExpanded() })
assertEquals("0.666667", withDecimalContext(6) { ("2".d / 3.d).toStringExpanded() })
assertEquals("0.666667", withDecimalContext(DecimalContext(6)) { ("2".d / 3.d).toStringExpanded() })
assertEquals("0.12", withDecimalContext(2) { (1.d / 8.d).toStringExpanded() })
assertEquals("0.13", withDecimalContext(2, DecimalRounding.HalfAwayFromZero) { (1.d / 8.d).toStringExpanded() })
assertEquals("0.13", withDecimalContext(DecimalContext(2, DecimalRounding.HalfAwayFromZero)) { (1.d / 8.d).toStringExpanded() })
assertEquals("0.3333333333333333333333333333333333", (1.d / 3.d).toStringExpanded())
""".trimIndent()
)
}
@Test
fun testDecimalDivisionRoundingMatrix() = runTest {
val scope = Script.newScope()
scope.eval(
"""
import lyng.decimal
assertEquals("0.12", withDecimalContext(2, DecimalRounding.HalfEven) { (1.d / 8.d).toStringExpanded() })
assertEquals("-0.12", withDecimalContext(2, DecimalRounding.HalfEven) { (-1.d / 8.d).toStringExpanded() })
assertEquals("0.13", withDecimalContext(2, DecimalRounding.HalfAwayFromZero) { (1.d / 8.d).toStringExpanded() })
assertEquals("-0.13", withDecimalContext(2, DecimalRounding.HalfAwayFromZero) { (-1.d / 8.d).toStringExpanded() })
assertEquals("0.12", withDecimalContext(2, DecimalRounding.HalfTowardsZero) { (1.d / 8.d).toStringExpanded() })
assertEquals("-0.12", withDecimalContext(2, DecimalRounding.HalfTowardsZero) { (-1.d / 8.d).toStringExpanded() })
assertEquals("0.13", withDecimalContext(2, DecimalRounding.Ceiling) { (1.d / 8.d).toStringExpanded() })
assertEquals("-0.12", withDecimalContext(2, DecimalRounding.Ceiling) { (-1.d / 8.d).toStringExpanded() })
assertEquals("0.12", withDecimalContext(2, DecimalRounding.Floor) { (1.d / 8.d).toStringExpanded() })
assertEquals("-0.13", withDecimalContext(2, DecimalRounding.Floor) { (-1.d / 8.d).toStringExpanded() })
assertEquals("0.13", withDecimalContext(2, DecimalRounding.AwayFromZero) { (1.d / 8.d).toStringExpanded() })
assertEquals("-0.13", withDecimalContext(2, DecimalRounding.AwayFromZero) { (-1.d / 8.d).toStringExpanded() })
assertEquals("0.12", withDecimalContext(2, DecimalRounding.TowardsZero) { (1.d / 8.d).toStringExpanded() })
assertEquals("-0.12", withDecimalContext(2, DecimalRounding.TowardsZero) { (-1.d / 8.d).toStringExpanded() })
""".trimIndent()
)
}
@Test
fun testDefaultToString() = runTest {
eval("""
import lyng.decimal
var s0 = "0.1".d + "0.1".d
assertEquals("0.2", s0.toStringExpanded())
assertEquals("0.2", s0.toString())
""".trimIndent())
}
@Test
fun testDecimalMathHelpersUseExactImplementationsWhenAvailable() = runTest {
val scope = Script.newScope()
scope.eval(
"""
import lyng.decimal
val absValue = abs("-2.5".d) as Decimal
val floorPos = floor("2.9".d) as Decimal
val floorNeg = floor("-2.1".d) as Decimal
val ceilPos = ceil("2.1".d) as Decimal
val ceilNeg = ceil("-2.1".d) as Decimal
val roundPos = round("2.5".d) as Decimal
val roundNeg = round("-2.5".d) as Decimal
val powInt = pow("1.5".d, 2) as Decimal
assertEquals("2.5", absValue.toStringExpanded())
assertEquals("2", floorPos.toStringExpanded())
assertEquals("-3", floorNeg.toStringExpanded())
assertEquals("3", ceilPos.toStringExpanded())
assertEquals("-2", ceilNeg.toStringExpanded())
assertEquals("3", roundPos.toStringExpanded())
assertEquals("-2", roundNeg.toStringExpanded())
assertEquals("2.25", powInt.toStringExpanded())
""".trimIndent()
)
}
@Test
fun testDecimalMathHelpersFallbackThroughRealTemporarily() = runTest {
val scope = Script.newScope()
scope.eval(
"""
import lyng.decimal
val sinDecimal = sin("0.5".d) as Decimal
val expDecimal = exp("1.25".d) as Decimal
val sqrtDecimal = sqrt("2".d) as Decimal
val lnDecimal = ln("2".d) as Decimal
val log10Decimal = log10("2".d) as Decimal
val log2Decimal = log2("2".d) as Decimal
val powDecimal = pow("2".d, "0.5".d) as Decimal
assertEquals((sin(0.5) as Real).d.toStringExpanded(), sinDecimal.toStringExpanded())
assertEquals((exp(1.25) as Real).d.toStringExpanded(), expDecimal.toStringExpanded())
assertEquals((sqrt(2.0) as Real).d.toStringExpanded(), sqrtDecimal.toStringExpanded())
assertEquals((ln(2.0) as Real).d.toStringExpanded(), lnDecimal.toStringExpanded())
assertEquals((log10(2.0) as Real).d.toStringExpanded(), log10Decimal.toStringExpanded())
assertEquals((log2(2.0) as Real).d.toStringExpanded(), log2Decimal.toStringExpanded())
assertEquals((pow(2.0, 0.5) as Real).d.toStringExpanded(), powDecimal.toStringExpanded())
""".trimIndent()
)
}
@Test
fun decimalMustBeObj() = runTest {
eval("""
import lyng.decimal
val decimal = 42.d
val context = DecimalContext(12)
assert(decimal is Decimal)
assertEquals(Decimal, decimal::class)
assert(context is DecimalContext)
assertEquals(DecimalContext, context::class)
""".trimIndent())
}
@Test
fun testFromRealLife1() = runTest {
eval("""
import lyng.decimal
var X = 42.d
X += 11
assertEquals(53.d, X)
""")
}
@Test
fun kotlinHelperCanWrapIonBigDecimal() = runTest {
val scope = Script.newScope()
val decimal = scope.asFacade().newDecimal(BigDecimal.parseStringWithMode("12.34"))
assertEquals("Decimal", decimal.objClass.className)
assertEquals("12.34", decimal.toString(scope).value)
assertEquals("12.34", decimal.invokeInstanceMethod(scope, "toStringExpanded").cast<net.sergeych.lyng.obj.ObjString>(scope).value)
}
@Test
fun testDecimalComparisons() = runTest {
eval("""
import lyng.decimal
val X = 42.d
assert(X < 43.d)
assert(X < 43)
assert(X == 42)
""".trimIndent())
}
}
@@ -168,4 +168,21 @@ class MatrixModuleTest {
assertEquals(Vector, vectorValue::class)
""".trimIndent())
}
@Test
fun testMatrixAndVectorComparisons() = runTest {
eval("""
import lyng.matrix
val v0 = vector([1, 2, 3])
val v1 = vector([1, 2, 4])
assert(v0 < v1)
assert(v0 == vector([1, 2, 3]))
val m0 = matrix([[1, 2], [3, 4]])
val m1 = matrix([[1, 2], [3, 5]])
assert(m0 < m1)
assert(m0 == matrix([[1, 2], [3, 4]]))
""".trimIndent())
}
}