Files
centvrion/tests/09_test_fraction.py
2026-04-24 19:13:48 +02:00

264 lines
9.1 KiB
Python

from tests._helpers import (
unittest, parameterized, Fraction, time,
run_test, run_compiler_error_test,
ArrayIndex, ArraySlice, Bool, BinOp, BuiltIn, DataArray, DataDict, DataRangeArray,
Defini, Continva, Designa, DesignaDestructure, DesignaIndex, DumStatement,
Erumpe, ExpressionStatement, Fvnctio, ID, InterpolatedString, Invoca,
ModuleCall, Nullus, Numeral, PerStatement, Program, Redi, SiStatement,
String, TemptaStatement, UnaryMinus, UnaryNot, Fractio, frac_to_fraction,
fraction_to_frac, num_to_int, int_to_num, make_string,
ValInt, ValStr, ValBool, ValList, ValDict, ValNul, ValFunc, ValFrac,
CentvrionError, _RUNTIME_C, _cent_rng,
Lexer, Parser, compile_program,
os, subprocess, tempfile, StringIO, patch,
)
# --- FRACTIO module ---
fractio_tests = [
# Basic fraction literals
("CVM FRACTIO\nIIIS",
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio("IIIS"))]),
ValFrac(Fraction(7, 2))),
("CVM FRACTIO\nS",
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio("S"))]),
ValFrac(Fraction(1, 2))),
("CVM FRACTIO\nS:.",
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio("S:."))]),
ValFrac(Fraction(3, 4))),
("CVM FRACTIO\n.",
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio("."))]),
ValFrac(Fraction(1, 12))),
("CVM FRACTIO\n:.",
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio(":."))]),
ValFrac(Fraction(1, 4))),
# Integer part with fraction
("CVM FRACTIO\nVIIS:|::",
Program([ModuleCall("FRACTIO")], [ExpressionStatement(Fractio("VIIS:|::"))]),
ValFrac(Fraction(7) + Fraction(100, 144))),
# Arithmetic
("CVM FRACTIO\nIIIS + S",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("S"), "SYMBOL_PLUS"))
]),
ValFrac(Fraction(4))
),
("CVM FRACTIO\nIIIS - S",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("S"), "SYMBOL_MINUS"))
]),
ValFrac(Fraction(3))
),
("CVM FRACTIO\nS * IV",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("S"), Numeral("IV"), "SYMBOL_TIMES"))
]),
ValFrac(Fraction(2))
),
# Division returns fraction
("CVM FRACTIO\nI / IV",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Numeral("I"), Numeral("IV"), "SYMBOL_DIVIDE"))
]),
ValFrac(Fraction(1, 4))
),
("CVM FRACTIO\nI / III",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Numeral("I"), Numeral("III"), "SYMBOL_DIVIDE"))
]),
ValFrac(Fraction(1, 3))
),
# Integer division still works without fractions in operands... but with FRACTIO returns ValFrac
("CVM FRACTIO\nX / II",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Numeral("X"), Numeral("II"), "SYMBOL_DIVIDE"))
]),
ValFrac(Fraction(5))
),
# Modulo on fractions: 7/2 RELIQVVM 3/2 = 1/2 (7/2 / 3/2 = 7/3, floor=2, 7/2 - 3 = 1/2)
("CVM FRACTIO\nIIIS RELIQVVM IS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IS"), "KEYWORD_RELIQVVM"))
]),
ValFrac(Fraction(1, 2))
),
# Modulo with mixed operand types: 5/2 RELIQVVM 1 = 1/2
("CVM FRACTIO\nIIS RELIQVVM I",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIS"), Numeral("I"), "KEYWORD_RELIQVVM"))
]),
ValFrac(Fraction(1, 2))
),
# Int operands under FRACTIO still return a fraction: 10 RELIQVVM 3 = 1 (as Fraction)
("CVM FRACTIO\nX RELIQVVM III",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Numeral("X"), Numeral("III"), "KEYWORD_RELIQVVM"))
]),
ValFrac(Fraction(1))
),
# Exact multiple under FRACTIO: 3 RELIQVVM 3/2 = 0
("CVM FRACTIO\nIII RELIQVVM IS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Numeral("III"), Fractio("IS"), "KEYWORD_RELIQVVM"))
]),
ValFrac(Fraction(0))
),
# String concatenation with fraction
("CVM FRACTIO\nDIC(IIIS & \"!\")",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BuiltIn("DIC", [BinOp(Fractio("IIIS"), String("!"), "SYMBOL_AMPERSAND")]))
]),
ValStr("IIIS!"), "IIIS!\n"
),
# Negative fractions
("CVM FRACTIO\nCVM SVBNVLLA\n-IIS",
Program([ModuleCall("FRACTIO"),ModuleCall("SVBNVLLA")],[
ExpressionStatement(UnaryMinus(Fractio("IIS")))
]),
ValFrac(Fraction(-5,2))
)
]
class TestFractio(unittest.TestCase):
@parameterized.expand(fractio_tests)
def test_fractio(self, source, nodes, value, output=""):
run_test(self, source, nodes, value, output)
fractio_comparison_tests = [
# fraction vs fraction
("CVM FRACTIO\nIIIS PLVS III",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("III"), "KEYWORD_PLVS"))
]),
ValBool(True)
),
("CVM FRACTIO\nIII MINVS IIIS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Numeral("III"), Fractio("IIIS"), "KEYWORD_MINVS"))
]),
ValBool(True)
),
("CVM FRACTIO\nIIIS MINVS IV",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("IV"), "KEYWORD_MINVS"))
]),
ValBool(True)
),
("CVM FRACTIO\nIV PLVS IIIS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Numeral("IV"), Fractio("IIIS"), "KEYWORD_PLVS"))
]),
ValBool(True)
),
("CVM FRACTIO\nIIIS PLVS IIIS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_PLVS"))
]),
ValBool(False)
),
("CVM FRACTIO\nIIIS MINVS IIIS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_MINVS"))
]),
ValBool(False)
),
# HAVD_PLVS / HAVD_MINVS on fractions — equality boundary distinguishes from MINVS / PLVS
("CVM FRACTIO\nIIIS HAVD_PLVS III",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("III"), "KEYWORD_HAVD_PLVS"))
]),
ValBool(False) # 3.5 <= 3 is false
),
("CVM FRACTIO\nIIIS HAVD_MINVS IIIS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_HAVD_MINVS"))
]),
ValBool(True) # 3.5 >= 3.5 is true (equality boundary)
),
("CVM FRACTIO\nIIIS HAVD_PLVS IIIS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_HAVD_PLVS"))
]),
ValBool(True) # 3.5 <= 3.5 is true (equality boundary)
),
# equality: fraction == fraction
("CVM FRACTIO\nIIIS EST IIIS",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Fractio("IIIS"), "KEYWORD_EST"))
]),
ValBool(True)
),
("CVM FRACTIO\nIIIS EST IV",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(Fractio("IIIS"), Numeral("IV"), "KEYWORD_EST"))
]),
ValBool(False)
),
# equality: fraction == whole number (ValFrac(4) vs ValInt(4))
("CVM FRACTIO\nIIIS + S EST IV",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(
BinOp(Fractio("IIIS"), Fractio("S"), "SYMBOL_PLUS"),
Numeral("IV"), "KEYWORD_EST"))
]),
ValBool(True)
),
("CVM FRACTIO\nS + S EST I",
Program([ModuleCall("FRACTIO")], [
ExpressionStatement(BinOp(
BinOp(Fractio("S"), Fractio("S"), "SYMBOL_PLUS"),
Numeral("I"), "KEYWORD_EST"))
]),
ValBool(True)
),
]
class TestFractioComparisons(unittest.TestCase):
@parameterized.expand(fractio_comparison_tests)
def test_fractio_comparison(self, source, nodes, value):
run_test(self, source, nodes, value)
class TestFractioHelpers(unittest.TestCase):
def test_frac_to_fraction_ordering(self):
with self.assertRaises(CentvrionError):
frac_to_fraction(".S") # . before S violates highest-to-lowest
def test_frac_to_fraction_level_overflow(self):
with self.assertRaises(CentvrionError):
frac_to_fraction("SSSSSS") # SS means S twice = 12/12 = 1, violating < 12/12 constraint
def test_frac_to_fraction_iiis(self):
self.assertEqual(frac_to_fraction("IIIS"), Fraction(7, 2))
def test_frac_to_fraction_s_colon_dot(self):
self.assertEqual(frac_to_fraction("S:."), Fraction(3, 4))
def test_frac_to_fraction_dot(self):
self.assertEqual(frac_to_fraction("."), Fraction(1, 12))
def test_frac_to_fraction_multilevel(self):
self.assertEqual(frac_to_fraction("VIIS:|::"), Fraction(7) + Fraction(100, 144))
def test_fraction_to_frac_iiis(self):
self.assertEqual(fraction_to_frac(Fraction(7, 2)), "IIIS")
def test_fraction_to_frac_s_colon_dot(self):
self.assertEqual(fraction_to_frac(Fraction(3, 4)), "S:.")
def test_fraction_to_frac_dot(self):
self.assertEqual(fraction_to_frac(Fraction(1, 12)), ".")
def test_fraction_to_frac_multilevel(self):
self.assertEqual(
fraction_to_frac(Fraction(7) + Fraction(100, 144)),
"VIIS:|::"
)
def test_roundtrip(self):
# Only canonical forms roundtrip — fraction_to_frac always uses max colons before dots
for s in ["IIIS", "S:.", ".", "::", "VIIS:|::", "S"]:
self.assertEqual(fraction_to_frac(frac_to_fraction(s)), s)