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)