@@ -153,6 +153,33 @@ fn lt_incompatible_types_error() {
153153 assert ! ( Value :: Int ( 1 ) . lt( & Value :: Symbolic ( "x" . to_string( ) ) ) . is_err( ) ) ;
154154}
155155
156+ // Cross-type EQUAL values: lt must return false, gt must return false
157+ // These kill `< with <=` and `> with >=` mutations on cross-type arms
158+ #[ test]
159+ fn lt_cross_type_equal_is_false ( ) {
160+ // Int(2) < Float(2.0) should be false
161+ assert_eq ! ( Value :: Int ( 2 ) . lt( & Value :: Float ( 2.0 ) ) . unwrap( ) , false ) ;
162+ assert_eq ! ( Value :: Float ( 2.0 ) . lt( & Value :: Int ( 2 ) ) . unwrap( ) , false ) ;
163+ assert_eq ! ( Value :: Int ( 1 ) . lt( & Value :: Rational ( Ratio :: new( 1 , 1 ) ) ) . unwrap( ) , false ) ;
164+ assert_eq ! ( Value :: Rational ( Ratio :: new( 1 , 1 ) ) . lt( & Value :: Int ( 1 ) ) . unwrap( ) , false ) ;
165+ assert_eq ! ( Value :: Hex ( 5 ) . lt( & Value :: Int ( 5 ) ) . unwrap( ) , false ) ;
166+ assert_eq ! ( Value :: Int ( 5 ) . lt( & Value :: Hex ( 5 ) ) . unwrap( ) , false ) ;
167+ assert_eq ! ( Value :: Binary ( 5 ) . lt( & Value :: Int ( 5 ) ) . unwrap( ) , false ) ;
168+ assert_eq ! ( Value :: Int ( 5 ) . lt( & Value :: Binary ( 5 ) ) . unwrap( ) , false ) ;
169+ }
170+
171+ #[ test]
172+ fn gt_cross_type_equal_is_false ( ) {
173+ assert_eq ! ( Value :: Int ( 2 ) . gt( & Value :: Float ( 2.0 ) ) . unwrap( ) , false ) ;
174+ assert_eq ! ( Value :: Float ( 2.0 ) . gt( & Value :: Int ( 2 ) ) . unwrap( ) , false ) ;
175+ assert_eq ! ( Value :: Int ( 1 ) . gt( & Value :: Rational ( Ratio :: new( 1 , 1 ) ) ) . unwrap( ) , false ) ;
176+ assert_eq ! ( Value :: Rational ( Ratio :: new( 1 , 1 ) ) . gt( & Value :: Int ( 1 ) ) . unwrap( ) , false ) ;
177+ assert_eq ! ( Value :: Hex ( 5 ) . gt( & Value :: Int ( 5 ) ) . unwrap( ) , false ) ;
178+ assert_eq ! ( Value :: Int ( 5 ) . gt( & Value :: Hex ( 5 ) ) . unwrap( ) , false ) ;
179+ assert_eq ! ( Value :: Binary ( 5 ) . gt( & Value :: Int ( 5 ) ) . unwrap( ) , false ) ;
180+ assert_eq ! ( Value :: Int ( 5 ) . gt( & Value :: Binary ( 5 ) ) . unwrap( ) , false ) ;
181+ }
182+
156183// ============================================================================
157184// Value::gt — greater than comparisons
158185// ============================================================================
@@ -433,6 +460,33 @@ fn ge_binary_int() {
433460// Value::eq and Value::ne
434461// ============================================================================
435462
463+ // Kills "delete match arm" mutations for negate and string add
464+ #[ test]
465+ fn negate_hex ( ) {
466+ let result = Value :: Hex ( 5 ) . negate ( ) . unwrap ( ) ;
467+ assert_eq ! ( result, Value :: Hex ( -5 ) ) ;
468+ }
469+
470+ #[ test]
471+ fn negate_binary ( ) {
472+ let result = Value :: Binary ( 5 ) . negate ( ) . unwrap ( ) ;
473+ assert_eq ! ( result, Value :: Binary ( -5 ) ) ;
474+ }
475+
476+ #[ test]
477+ fn negate_symbolic ( ) {
478+ let result = Value :: Symbolic ( "x" . to_string ( ) ) . negate ( ) . unwrap ( ) ;
479+ assert ! ( matches!( result, Value :: Symbolic ( _) ) ) ;
480+ }
481+
482+ #[ test]
483+ fn add_string_string ( ) {
484+ let a = Value :: String ( "hello" . to_string ( ) ) ;
485+ let b = Value :: String ( " world" . to_string ( ) ) ;
486+ let result = a. add ( & b) . unwrap ( ) ;
487+ assert_eq ! ( result, Value :: String ( "hello world" . to_string( ) ) ) ;
488+ }
489+
436490#[ test]
437491fn eq_int_true ( ) {
438492 assert_eq ! ( Value :: Int ( 5 ) . eq( & Value :: Int ( 5 ) ) . unwrap( ) , true ) ;
@@ -894,6 +948,199 @@ fn interp_for_loop_step() {
894948 assert_eq ! ( interp. get_variable( "sum" ) . unwrap( ) , Value :: Int ( 20 ) ) ;
895949}
896950
951+ // ============================================================================
952+ // Type checker: add_result and negate_result (kills type coercion mutations)
953+ // ============================================================================
954+
955+ use jtv_core:: typechecker:: Type ;
956+
957+ #[ test]
958+ fn type_add_same_types ( ) {
959+ assert_eq ! ( Type :: Int . add_result( & Type :: Int ) , Some ( Type :: Int ) ) ;
960+ assert_eq ! ( Type :: Float . add_result( & Type :: Float ) , Some ( Type :: Float ) ) ;
961+ assert_eq ! ( Type :: Rational . add_result( & Type :: Rational ) , Some ( Type :: Rational ) ) ;
962+ assert_eq ! ( Type :: Complex . add_result( & Type :: Complex ) , Some ( Type :: Complex ) ) ;
963+ assert_eq ! ( Type :: Hex . add_result( & Type :: Hex ) , Some ( Type :: Hex ) ) ;
964+ assert_eq ! ( Type :: Binary . add_result( & Type :: Binary ) , Some ( Type :: Binary ) ) ;
965+ assert_eq ! ( Type :: Symbolic . add_result( & Type :: Symbolic ) , Some ( Type :: Symbolic ) ) ;
966+ assert_eq ! ( Type :: String . add_result( & Type :: String ) , Some ( Type :: String ) ) ;
967+ }
968+
969+ #[ test]
970+ fn type_add_coercions ( ) {
971+ // Int + Float = Float
972+ assert_eq ! ( Type :: Int . add_result( & Type :: Float ) , Some ( Type :: Float ) ) ;
973+ assert_eq ! ( Type :: Float . add_result( & Type :: Int ) , Some ( Type :: Float ) ) ;
974+ // Int + Rational = Rational
975+ assert_eq ! ( Type :: Int . add_result( & Type :: Rational ) , Some ( Type :: Rational ) ) ;
976+ assert_eq ! ( Type :: Rational . add_result( & Type :: Int ) , Some ( Type :: Rational ) ) ;
977+ // Int + Complex = Complex
978+ assert_eq ! ( Type :: Int . add_result( & Type :: Complex ) , Some ( Type :: Complex ) ) ;
979+ assert_eq ! ( Type :: Complex . add_result( & Type :: Int ) , Some ( Type :: Complex ) ) ;
980+ // Float + Complex = Complex
981+ assert_eq ! ( Type :: Float . add_result( & Type :: Complex ) , Some ( Type :: Complex ) ) ;
982+ assert_eq ! ( Type :: Complex . add_result( & Type :: Float ) , Some ( Type :: Complex ) ) ;
983+ // Hex + Int = Int
984+ assert_eq ! ( Type :: Hex . add_result( & Type :: Int ) , Some ( Type :: Int ) ) ;
985+ assert_eq ! ( Type :: Int . add_result( & Type :: Hex ) , Some ( Type :: Int ) ) ;
986+ // Binary + Int = Int
987+ assert_eq ! ( Type :: Binary . add_result( & Type :: Int ) , Some ( Type :: Int ) ) ;
988+ assert_eq ! ( Type :: Int . add_result( & Type :: Binary ) , Some ( Type :: Int ) ) ;
989+ }
990+
991+ #[ test]
992+ fn type_add_incompatible ( ) {
993+ assert_eq ! ( Type :: Int . add_result( & Type :: String ) , None ) ;
994+ assert_eq ! ( Type :: Bool . add_result( & Type :: Int ) , None ) ;
995+ assert_eq ! ( Type :: String . add_result( & Type :: Float ) , None ) ;
996+ }
997+
998+ #[ test]
999+ fn type_add_any ( ) {
1000+ assert_eq ! ( Type :: Any . add_result( & Type :: Int ) , Some ( Type :: Int ) ) ;
1001+ assert_eq ! ( Type :: Float . add_result( & Type :: Any ) , Some ( Type :: Float ) ) ;
1002+ }
1003+
1004+ #[ test]
1005+ fn type_negate_numeric ( ) {
1006+ assert_eq ! ( Type :: Int . negate_result( ) , Some ( Type :: Int ) ) ;
1007+ assert_eq ! ( Type :: Float . negate_result( ) , Some ( Type :: Float ) ) ;
1008+ assert_eq ! ( Type :: Rational . negate_result( ) , Some ( Type :: Rational ) ) ;
1009+ assert_eq ! ( Type :: Complex . negate_result( ) , Some ( Type :: Complex ) ) ;
1010+ assert_eq ! ( Type :: Hex . negate_result( ) , Some ( Type :: Hex ) ) ;
1011+ assert_eq ! ( Type :: Binary . negate_result( ) , Some ( Type :: Binary ) ) ;
1012+ assert_eq ! ( Type :: Symbolic . negate_result( ) , Some ( Type :: Symbolic ) ) ;
1013+ assert_eq ! ( Type :: Any . negate_result( ) , Some ( Type :: Any ) ) ;
1014+ }
1015+
1016+ #[ test]
1017+ fn type_negate_non_numeric ( ) {
1018+ assert_eq ! ( Type :: Bool . negate_result( ) , None ) ;
1019+ assert_eq ! ( Type :: String . negate_result( ) , None ) ;
1020+ assert_eq ! ( Type :: Unit . negate_result( ) , None ) ;
1021+ }
1022+
1023+ #[ test]
1024+ fn type_coercible ( ) {
1025+ assert ! ( Type :: Int . coercible_to( & Type :: Int ) ) ;
1026+ assert ! ( Type :: Int . coercible_to( & Type :: Float ) ) ;
1027+ assert ! ( Type :: Int . coercible_to( & Type :: Rational ) ) ;
1028+ assert ! ( Type :: Int . coercible_to( & Type :: Complex ) ) ;
1029+ assert ! ( Type :: Hex . coercible_to( & Type :: Int ) ) ;
1030+ assert ! ( Type :: Binary . coercible_to( & Type :: Int ) ) ;
1031+ assert ! ( Type :: Float . coercible_to( & Type :: Complex ) ) ;
1032+ assert ! ( Type :: Any . coercible_to( & Type :: Int ) ) ;
1033+ assert ! ( Type :: Int . coercible_to( & Type :: Any ) ) ;
1034+ // Not coercible
1035+ assert ! ( !Type :: Int . coercible_to( & Type :: String ) ) ;
1036+ assert ! ( !Type :: String . coercible_to( & Type :: Int ) ) ;
1037+ assert ! ( !Type :: Bool . coercible_to( & Type :: Float ) ) ;
1038+ }
1039+
1040+ // ============================================================================
1041+ // Pretty printer: control statement indentation (kills depth+1 mutations)
1042+ // ============================================================================
1043+
1044+ #[ test]
1045+ fn pretty_print_if_indented_body ( ) {
1046+ let code = "if x > 0 { y = 1 }" ;
1047+ let program = parse_program ( code) . unwrap ( ) ;
1048+ let printer = PrettyPrinter :: new ( ) ;
1049+ let output = printer. print_program ( & program) ;
1050+ // Body should be indented (2 spaces by default)
1051+ assert ! ( output. contains( " y = 1" ) , "If body should be indented:\n {}" , output) ;
1052+ }
1053+
1054+ #[ test]
1055+ fn pretty_print_while_indented_body ( ) {
1056+ let code = "while x > 0 { x = x + 1 }" ;
1057+ let program = parse_program ( code) . unwrap ( ) ;
1058+ let printer = PrettyPrinter :: new ( ) ;
1059+ let output = printer. print_program ( & program) ;
1060+ assert ! ( output. contains( " x = x" ) , "While body should be indented:\n {}" , output) ;
1061+ }
1062+
1063+ #[ test]
1064+ fn pretty_print_for_indented_body ( ) {
1065+ let code = "for i in 0..10 { x = x + 1 }" ;
1066+ let program = parse_program ( code) . unwrap ( ) ;
1067+ let printer = PrettyPrinter :: new ( ) ;
1068+ let output = printer. print_program ( & program) ;
1069+ assert ! ( output. contains( " x = x" ) , "For body should be indented:\n {}" , output) ;
1070+ }
1071+
1072+ #[ test]
1073+ fn pretty_print_reverse_indented_body ( ) {
1074+ let code = "reverse { x += 5 }" ;
1075+ let program = parse_program ( code) . unwrap ( ) ;
1076+ let printer = PrettyPrinter :: new ( ) ;
1077+ let output = printer. print_program ( & program) ;
1078+ assert ! ( output. contains( " x += 5" ) , "Reverse body should be indented:\n {}" , output) ;
1079+ }
1080+
1081+ #[ test]
1082+ fn pretty_print_if_else_both_branches_indented ( ) {
1083+ let code = "if x > 0 { y = 1 } else { y = 0 }" ;
1084+ let program = parse_program ( code) . unwrap ( ) ;
1085+ let printer = PrettyPrinter :: new ( ) ;
1086+ let output = printer. print_program ( & program) ;
1087+ assert ! ( output. contains( " y = 1" ) , "Then branch should be indented:\n {}" , output) ;
1088+ assert ! ( output. contains( " y = 0" ) , "Else branch should be indented:\n {}" , output) ;
1089+ }
1090+
1091+ #[ test]
1092+ fn pretty_print_module ( ) {
1093+ let code = "module M { fn f(): Int { return 1 } }" ;
1094+ let program = parse_program ( code) . unwrap ( ) ;
1095+ let printer = PrettyPrinter :: new ( ) ;
1096+ let output = printer. print_program ( & program) ;
1097+ assert ! ( output. contains( "module M" ) , "Module header:\n {}" , output) ;
1098+ assert ! ( output. contains( " fn f" ) , "Function should be indented in module:\n {}" , output) ;
1099+ }
1100+
1101+ #[ test]
1102+ fn pretty_print_program_multiple_stmts ( ) {
1103+ let code = "x = 1\n y = 2" ;
1104+ let program = parse_program ( code) . unwrap ( ) ;
1105+ let printer = PrettyPrinter :: new ( ) ;
1106+ let output = printer. print_program ( & program) ;
1107+ assert ! ( output. contains( "x = 1" ) , "First stmt:\n {}" , output) ;
1108+ assert ! ( output. contains( "y = 2" ) , "Second stmt:\n {}" , output) ;
1109+ }
1110+
1111+ #[ test]
1112+ fn pretty_print_return_with_value ( ) {
1113+ let printer = PrettyPrinter :: new ( ) ;
1114+ let stmt = ControlStmt :: Return ( Some ( DataExpr :: Number ( Number :: Int ( 42 ) ) ) ) ;
1115+ let output = printer. print_control_stmt ( & stmt, 0 ) ;
1116+ assert_eq ! ( output, "return 42" ) ;
1117+ }
1118+
1119+ #[ test]
1120+ fn pretty_print_return_without_value ( ) {
1121+ let printer = PrettyPrinter :: new ( ) ;
1122+ let stmt = ControlStmt :: Return ( None ) ;
1123+ let output = printer. print_control_stmt ( & stmt, 0 ) ;
1124+ assert_eq ! ( output, "return" ) ;
1125+ }
1126+
1127+ #[ test]
1128+ fn pretty_print_complex_number_formats ( ) {
1129+ let printer = PrettyPrinter :: new ( ) ;
1130+ // Complex with real and positive imaginary
1131+ assert_eq ! ( printer. print_number( & Number :: Complex ( 3.0 , 4.0 ) ) , "3+4i" ) ;
1132+ // Complex with real and negative imaginary
1133+ assert_eq ! ( printer. print_number( & Number :: Complex ( 3.0 , -4.0 ) ) , "3-4i" ) ;
1134+ // Pure imaginary
1135+ assert_eq ! ( printer. print_number( & Number :: Complex ( 0.0 , 5.0 ) ) , "5i" ) ;
1136+ }
1137+
1138+ #[ test]
1139+ fn pretty_print_symbolic ( ) {
1140+ let printer = PrettyPrinter :: new ( ) ;
1141+ assert_eq ! ( printer. print_number( & Number :: Symbolic ( "pi" . to_string( ) ) ) , "#pi" ) ;
1142+ }
1143+
8971144// ============================================================================
8981145// Value::Display formatting
8991146// ============================================================================
0 commit comments