From 6a91950d02834ce864bee0fcded349438d93d982 Mon Sep 17 00:00:00 2001 From: Kacper Marzecki Date: Sun, 15 Jun 2025 20:29:35 +0200 Subject: [PATCH] chceckpoint --- test.exs | 386 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 201 insertions(+), 185 deletions(-) diff --git a/test.exs b/test.exs index f54a7e6..77c53ef 100644 --- a/test.exs +++ b/test.exs @@ -89,6 +89,10 @@ defmodule Tdd do _otherwise, acc_set -> acc_set end) + IO.inspect({assumptions_map, primary_true_predicates, MapSet.size(primary_true_predicates)}, + label: "CheckConsistency" + ) + if MapSet.size(primary_true_predicates) > 1 do :contradiction else @@ -114,6 +118,8 @@ defmodule Tdd do check_assumptions_consistency(assumptions_map) == :contradiction -> # Path is semantically impossible + # Important + IO.inspect({tdd_id, assumptions_map}, label: "SimplifyContradiction") update_state(%{op_cache: Map.put(state.op_cache, cache_key, @false_node_id)}) @false_node_id @@ -144,6 +150,7 @@ defmodule Tdd do make_node_raw(var, simplified_y, simplified_n, simplified_d) end + IO.inspect({tdd_id, assumptions_map, result_id}, label: "SimplifyExit") update_state(%{op_cache: Map.put(state.op_cache, cache_key, result_id)}) result_id end @@ -402,31 +409,6 @@ tdd_empty_tuple = Tdd.type_empty_tuple() tdd_any = Tdd.type_any() tdd_none = Tdd.type_none() -IO.puts("\n--- TDD for :foo ---") -Tdd.print_tdd(tdd_foo) - -IO.puts("\n--- TDD for not :foo ---") -Tdd.print_tdd(Tdd.negate(tdd_foo)) - -IO.puts("\n--- TDD for atom ---") -Tdd.print_tdd(tdd_atom) - -IO.puts("\n--- TDD for not atom ---") -# Expected: make_node(@v_is_atom, @false_node_id, @true_node_id, @true_node_id) -# This represents "anything that is not an atom". The DC branch becomes true because if -# "is_atom" test is irrelevant for "not atom", it means it's part of "not atom". -Tdd.print_tdd(Tdd.negate(tdd_atom)) - -IO.puts("\n--- TDD for :foo and :bar (should be none) ---") -tdd_foo_and_bar = Tdd.intersect(tdd_foo, tdd_bar) -# Expected ID 0: :false_terminal -Tdd.print_tdd(tdd_foo_and_bar) - -IO.puts("\n--- TDD for :foo and atom (should be :foo) ---") -tdd_foo_and_atom = Tdd.intersect(tdd_foo, tdd_atom) -# Expected to be structurally identical to tdd_foo -Tdd.print_tdd(tdd_foo_and_atom) - test = fn name, expected, result -> current_failures = Process.get(:test_failures, []) @@ -450,176 +432,210 @@ tdd_tuple_s2 = Tdd.type_tuple_sized_any(2) tdd_any = Tdd.type_any() tdd_none = Tdd.type_none() -IO.puts("\n--- Basic Subtyping Tests ---") -test.(":foo <: atom", true, Tdd.is_subtype(tdd_foo, tdd_atom)) -test.("atom <: :foo", false, Tdd.is_subtype(tdd_atom, tdd_foo)) -test.(":foo <: :bar", false, Tdd.is_subtype(tdd_foo, tdd_bar)) -test.(":foo <: :foo", true, Tdd.is_subtype(tdd_foo, tdd_foo)) -test.("{} <: tuple", true, Tdd.is_subtype(tdd_empty_tuple, tdd_tuple)) -test.("tuple <: {}", false, Tdd.is_subtype(tdd_tuple, tdd_empty_tuple)) -test.(":foo <: {}", false, Tdd.is_subtype(tdd_foo, tdd_empty_tuple)) -test.("tuple_size_2 <: tuple", true, Tdd.is_subtype(tdd_tuple_s2, tdd_tuple)) -test.("tuple <: tuple_size_2", false, Tdd.is_subtype(tdd_tuple, tdd_tuple_s2)) -test.("tuple_size_2 <: {}", false, Tdd.is_subtype(tdd_tuple_s2, tdd_empty_tuple)) +test_all = fn -> + IO.puts("\n--- TDD for :foo ---") + Tdd.print_tdd(tdd_foo) -IO.puts("\n--- Any/None Subtyping Tests ---") -test.("any <: :foo", false, Tdd.is_subtype(tdd_any, tdd_foo)) -test.(":foo <: any", true, Tdd.is_subtype(tdd_foo, tdd_any)) -test.("none <: :foo", true, Tdd.is_subtype(tdd_none, tdd_foo)) -test.(":foo <: none", false, Tdd.is_subtype(tdd_foo, tdd_none)) -test.("none <: any", true, Tdd.is_subtype(tdd_none, tdd_any)) -test.("any <: none", false, Tdd.is_subtype(tdd_any, tdd_none)) -test.("any <: any", true, Tdd.is_subtype(tdd_any, tdd_any)) -test.("none <: none", true, Tdd.is_subtype(tdd_none, tdd_none)) + IO.puts("\n--- TDD for not :foo ---") + Tdd.print_tdd(Tdd.negate(tdd_foo)) -IO.puts("\n--- Union related Subtyping ---") -tdd_foo_or_bar = Tdd.sum(tdd_foo, tdd_bar) -tdd_foo_or_bar_or_baz = Tdd.sum(tdd_foo_or_bar, tdd_baz) + IO.puts("\n--- TDD for atom ---") + Tdd.print_tdd(tdd_atom) -test.(":foo <: (:foo | :bar)", true, Tdd.is_subtype(tdd_foo, tdd_foo_or_bar)) -test.(":baz <: (:foo | :bar)", false, Tdd.is_subtype(tdd_baz, tdd_foo_or_bar)) -test.("(:foo | :bar) <: atom", true, Tdd.is_subtype(tdd_foo_or_bar, tdd_atom)) -test.("atom <: (:foo | :bar)", false, Tdd.is_subtype(tdd_atom, tdd_foo_or_bar)) + IO.puts("\n--- TDD for not atom ---") + # Expected: make_node(@v_is_atom, @false_node_id, @true_node_id, @true_node_id) + # This represents "anything that is not an atom". The DC branch becomes true because if + # "is_atom" test is irrelevant for "not atom", it means it's part of "not atom". + Tdd.print_tdd(Tdd.negate(tdd_atom)) -test.( - "(:foo | :bar) <: (:foo | :bar | :baz)", - true, - Tdd.is_subtype(tdd_foo_or_bar, tdd_foo_or_bar_or_baz) -) + IO.puts("\n--- TDD for :foo and :bar (should be none) ---") + tdd_foo_and_bar = Tdd.intersect(tdd_foo, tdd_bar) + # Expected ID 0: :false_terminal + Tdd.print_tdd(tdd_foo_and_bar) -test.( - "(:foo | :bar | :baz) <: (:foo | :bar)", - false, - Tdd.is_subtype(tdd_foo_or_bar_or_baz, tdd_foo_or_bar) -) + IO.puts("\n--- TDD for :foo and atom (should be :foo) ---") + tdd_foo_and_atom = Tdd.intersect(tdd_foo, tdd_atom) + # Expected to be structurally identical to tdd_foo + Tdd.print_tdd(tdd_foo_and_atom) + IO.puts("\n--- Basic Subtyping Tests ---") + test.(":foo <: atom", true, Tdd.is_subtype(tdd_foo, tdd_atom)) + test.("atom <: :foo", false, Tdd.is_subtype(tdd_atom, tdd_foo)) + test.(":foo <: :bar", false, Tdd.is_subtype(tdd_foo, tdd_bar)) + test.(":foo <: :foo", true, Tdd.is_subtype(tdd_foo, tdd_foo)) + test.("{} <: tuple", true, Tdd.is_subtype(tdd_empty_tuple, tdd_tuple)) + test.("tuple <: {}", false, Tdd.is_subtype(tdd_tuple, tdd_empty_tuple)) + test.(":foo <: {}", false, Tdd.is_subtype(tdd_foo, tdd_empty_tuple)) + test.("tuple_size_2 <: tuple", true, Tdd.is_subtype(tdd_tuple_s2, tdd_tuple)) + test.("tuple <: tuple_size_2", false, Tdd.is_subtype(tdd_tuple, tdd_tuple_s2)) + test.("tuple_size_2 <: {}", false, Tdd.is_subtype(tdd_tuple_s2, tdd_empty_tuple)) -# Test against a non-member of the union -test.("(:foo | :bar) <: :baz", false, Tdd.is_subtype(tdd_foo_or_bar, tdd_baz)) + IO.puts("\n--- Any/None Subtyping Tests ---") + test.("any <: :foo", false, Tdd.is_subtype(tdd_any, tdd_foo)) + test.(":foo <: any", true, Tdd.is_subtype(tdd_foo, tdd_any)) + test.("none <: :foo", true, Tdd.is_subtype(tdd_none, tdd_foo)) + test.(":foo <: none", false, Tdd.is_subtype(tdd_foo, tdd_none)) + test.("none <: any", true, Tdd.is_subtype(tdd_none, tdd_any)) + test.("any <: none", false, Tdd.is_subtype(tdd_any, tdd_none)) + test.("any <: any", true, Tdd.is_subtype(tdd_any, tdd_any)) + test.("none <: none", true, Tdd.is_subtype(tdd_none, tdd_none)) + + IO.puts("\n--- Union related Subtyping ---") + tdd_foo_or_bar = Tdd.sum(tdd_foo, tdd_bar) + tdd_foo_or_bar_or_baz = Tdd.sum(tdd_foo_or_bar, tdd_baz) + + test.(":foo <: (:foo | :bar)", true, Tdd.is_subtype(tdd_foo, tdd_foo_or_bar)) + test.(":baz <: (:foo | :bar)", false, Tdd.is_subtype(tdd_baz, tdd_foo_or_bar)) + test.("(:foo | :bar) <: atom", true, Tdd.is_subtype(tdd_foo_or_bar, tdd_atom)) + test.("atom <: (:foo | :bar)", false, Tdd.is_subtype(tdd_atom, tdd_foo_or_bar)) + + test.( + "(:foo | :bar) <: (:foo | :bar | :baz)", + true, + Tdd.is_subtype(tdd_foo_or_bar, tdd_foo_or_bar_or_baz) + ) + + test.( + "(:foo | :bar | :baz) <: (:foo | :bar)", + false, + Tdd.is_subtype(tdd_foo_or_bar_or_baz, tdd_foo_or_bar) + ) + + # Test against a non-member of the union + test.("(:foo | :bar) <: :baz", false, Tdd.is_subtype(tdd_foo_or_bar, tdd_baz)) + + IO.puts("\n--- Intersection related Subtyping ---") + # Should be equivalent to tdd_foo + tdd_atom_and_foo = Tdd.intersect(tdd_atom, tdd_foo) + # Should be tdd_none + tdd_atom_and_tuple = Tdd.intersect(tdd_atom, tdd_tuple) + + test.("(atom & :foo) <: :foo", true, Tdd.is_subtype(tdd_atom_and_foo, tdd_foo)) + test.(":foo <: (atom & :foo)", true, Tdd.is_subtype(tdd_foo, tdd_atom_and_foo)) + test.("(atom & tuple) <: none", true, Tdd.is_subtype(tdd_atom_and_tuple, tdd_none)) + test.("none <: (atom & tuple)", true, Tdd.is_subtype(tdd_none, tdd_atom_and_tuple)) + test.("(atom & :foo) <: :bar", false, Tdd.is_subtype(tdd_atom_and_foo, tdd_bar)) + # An intersection is a subtype of its components + test.("(atom & :foo) <: atom", true, Tdd.is_subtype(tdd_atom_and_foo, tdd_atom)) + # (none <: atom) + test.("(atom & tuple) <: atom", true, Tdd.is_subtype(tdd_atom_and_tuple, tdd_atom)) + # (none <: tuple) + test.("(atom & tuple) <: tuple", true, Tdd.is_subtype(tdd_atom_and_tuple, tdd_tuple)) + + IO.puts("\n--- Negation related Subtyping (Contrapositives) ---") + # Reminder: ¬A <: ¬B is equivalent to B <: A (contrapositive) + + # Test 1: ¬atom <: ¬:foo (Equivalent to :foo <: atom, which is true) + test.("¬atom <: ¬:foo", true, Tdd.is_subtype(Tdd.negate(tdd_atom), Tdd.negate(tdd_foo))) + + # Test 2: ¬:foo <: ¬atom (Equivalent to atom <: :foo, which is false) + test.("¬:foo <: ¬atom", false, Tdd.is_subtype(Tdd.negate(tdd_foo), Tdd.negate(tdd_atom))) + + # Double negation + test.("¬(¬:foo) <: :foo", true, Tdd.is_subtype(Tdd.negate(Tdd.negate(tdd_foo)), tdd_foo)) + test.(":foo <: ¬(¬:foo)", true, Tdd.is_subtype(tdd_foo, Tdd.negate(Tdd.negate(tdd_foo)))) + + # Disjoint types + test.("atom <: ¬tuple", true, Tdd.is_subtype(tdd_atom, Tdd.negate(tdd_tuple))) + test.("tuple <: ¬atom", true, Tdd.is_subtype(tdd_tuple, Tdd.negate(tdd_atom))) + test.(":foo <: ¬{}", true, Tdd.is_subtype(tdd_foo, Tdd.negate(tdd_empty_tuple))) + + IO.puts("\n--- Mixed Types & Complex Subtyping ---") + tdd_atom_or_tuple = Tdd.sum(tdd_atom, tdd_tuple) + tdd_foo_or_empty_tuple = Tdd.sum(tdd_foo, tdd_empty_tuple) + + test.( + "(:foo | {}) <: (atom | tuple)", + true, + Tdd.is_subtype(tdd_foo_or_empty_tuple, tdd_atom_or_tuple) + ) + + test.( + "(atom | tuple) <: (:foo | {})", + false, + Tdd.is_subtype(tdd_atom_or_tuple, tdd_foo_or_empty_tuple) + ) + + test.(":foo <: (atom | tuple)", true, Tdd.is_subtype(tdd_foo, tdd_atom_or_tuple)) + test.("{} <: (atom | tuple)", true, Tdd.is_subtype(tdd_empty_tuple, tdd_atom_or_tuple)) + + # De Morgan's Law illustration (A | B = ¬(¬A & ¬B)) + # (:foo | :bar) <: ¬(¬:foo & ¬:bar) + tdd_not_foo_and_not_bar = Tdd.intersect(Tdd.negate(tdd_foo), Tdd.negate(tdd_bar)) + + test.( + "(:foo | :bar) <: ¬(¬:foo & ¬:bar)", + true, + Tdd.is_subtype(tdd_foo_or_bar, Tdd.negate(tdd_not_foo_and_not_bar)) + ) + + test.( + "¬(¬:foo & ¬:bar) <: (:foo | :bar)", + true, + Tdd.is_subtype(Tdd.negate(tdd_not_foo_and_not_bar), tdd_foo_or_bar) + ) + + # Type difference: atom - :foo (represented as atom & ¬:foo) + tdd_atom_minus_foo = Tdd.intersect(tdd_atom, Tdd.negate(tdd_foo)) + test.("(atom - :foo) <: atom", true, Tdd.is_subtype(tdd_atom_minus_foo, tdd_atom)) + test.("(atom - :foo) <: :foo", false, Tdd.is_subtype(tdd_atom_minus_foo, tdd_foo)) + # True if :bar is in (atom - :foo) + test.("(atom - :foo) <: :bar", true, Tdd.is_subtype(tdd_atom_minus_foo, tdd_bar)) + test.(":bar <: (atom - :foo)", true, Tdd.is_subtype(tdd_bar, tdd_atom_minus_foo)) + + # (atom - :foo) | :foo should be atom + tdd_recombined_atom = Tdd.sum(tdd_atom_minus_foo, tdd_foo) + test.("((atom - :foo) | :foo) <: atom", true, Tdd.is_subtype(tdd_recombined_atom, tdd_atom)) + test.("atom <: ((atom - :foo) | :foo)", true, Tdd.is_subtype(tdd_atom, tdd_recombined_atom)) + + # (atom | {}) & (tuple | :foo) must be (:foo | {}) + # Represents `atom() | {}` + tdd_atom_or_empty = Tdd.sum(tdd_atom, tdd_empty_tuple) + # Represents `tuple() | :foo` + tdd_tuple_or_foo = Tdd.sum(tdd_tuple, tdd_foo) + intersected_complex = Tdd.intersect(tdd_atom_or_empty, tdd_tuple_or_foo) + # Expected result for intersected_complex is tdd_foo_or_empty_tuple + + test.( + "(atom | {}) & (tuple | :foo) <: (:foo | {})", + true, + Tdd.is_subtype(intersected_complex, tdd_foo_or_empty_tuple) + ) + + test.( + "(:foo | {}) <: (atom | {}) & (tuple | :foo)", + true, + Tdd.is_subtype(tdd_foo_or_empty_tuple, intersected_complex) + ) + + # {} | tuple_size_2 should be a subtype of tuple + tdd_empty_or_s2 = Tdd.sum(tdd_empty_tuple, tdd_tuple_s2) + test.("({} | tuple_size_2) <: tuple", true, Tdd.is_subtype(tdd_empty_or_s2, tdd_tuple)) + + test.( + "({} | tuple_size_2) <: ({} | tuple_size_2)", + true, + Tdd.is_subtype(tdd_empty_or_s2, tdd_empty_or_s2) + ) + + test.( + "({} | tuple_size_2) <: tuple_size_2", + false, + Tdd.is_subtype(tdd_empty_or_s2, tdd_tuple_s2) + ) + + IO.puts("\n--- TDD structure for (atom - :foo) ---") + Tdd.print_tdd(tdd_atom_minus_foo) + IO.puts("\n--- TDD structure for ((atom - :foo) | :foo) which should be 'atom' ---") + Tdd.print_tdd(tdd_recombined_atom) + IO.puts("\n--- TDD structure for 'atom' for comparison ---") + Tdd.print_tdd(tdd_atom) + + IO.inspect(Process.get(:test_failures, [])) +end -IO.puts("\n--- Intersection related Subtyping ---") -# Should be equivalent to tdd_foo -tdd_atom_and_foo = Tdd.intersect(tdd_atom, tdd_foo) # Should be tdd_none tdd_atom_and_tuple = Tdd.intersect(tdd_atom, tdd_tuple) - -test.("(atom & :foo) <: :foo", true, Tdd.is_subtype(tdd_atom_and_foo, tdd_foo)) -test.(":foo <: (atom & :foo)", true, Tdd.is_subtype(tdd_foo, tdd_atom_and_foo)) test.("(atom & tuple) <: none", true, Tdd.is_subtype(tdd_atom_and_tuple, tdd_none)) -test.("none <: (atom & tuple)", true, Tdd.is_subtype(tdd_none, tdd_atom_and_tuple)) -test.("(atom & :foo) <: :bar", false, Tdd.is_subtype(tdd_atom_and_foo, tdd_bar)) -# An intersection is a subtype of its components -test.("(atom & :foo) <: atom", true, Tdd.is_subtype(tdd_atom_and_foo, tdd_atom)) -# (none <: atom) -test.("(atom & tuple) <: atom", true, Tdd.is_subtype(tdd_atom_and_tuple, tdd_atom)) -# (none <: tuple) -test.("(atom & tuple) <: tuple", true, Tdd.is_subtype(tdd_atom_and_tuple, tdd_tuple)) - -IO.puts("\n--- Negation related Subtyping (Contrapositives) ---") -# Reminder: ¬A <: ¬B is equivalent to B <: A (contrapositive) - -# Test 1: ¬atom <: ¬:foo (Equivalent to :foo <: atom, which is true) -test.("¬atom <: ¬:foo", true, Tdd.is_subtype(Tdd.negate(tdd_atom), Tdd.negate(tdd_foo))) - -# Test 2: ¬:foo <: ¬atom (Equivalent to atom <: :foo, which is false) -test.("¬:foo <: ¬atom", false, Tdd.is_subtype(Tdd.negate(tdd_foo), Tdd.negate(tdd_atom))) - -# Double negation -test.("¬(¬:foo) <: :foo", true, Tdd.is_subtype(Tdd.negate(Tdd.negate(tdd_foo)), tdd_foo)) -test.(":foo <: ¬(¬:foo)", true, Tdd.is_subtype(tdd_foo, Tdd.negate(Tdd.negate(tdd_foo)))) - -# Disjoint types -test.("atom <: ¬tuple", true, Tdd.is_subtype(tdd_atom, Tdd.negate(tdd_tuple))) -test.("tuple <: ¬atom", true, Tdd.is_subtype(tdd_tuple, Tdd.negate(tdd_atom))) -test.(":foo <: ¬{}", true, Tdd.is_subtype(tdd_foo, Tdd.negate(tdd_empty_tuple))) - -IO.puts("\n--- Mixed Types & Complex Subtyping ---") -tdd_atom_or_tuple = Tdd.sum(tdd_atom, tdd_tuple) -tdd_foo_or_empty_tuple = Tdd.sum(tdd_foo, tdd_empty_tuple) - -test.( - "(:foo | {}) <: (atom | tuple)", - true, - Tdd.is_subtype(tdd_foo_or_empty_tuple, tdd_atom_or_tuple) -) - -test.( - "(atom | tuple) <: (:foo | {})", - false, - Tdd.is_subtype(tdd_atom_or_tuple, tdd_foo_or_empty_tuple) -) - -test.(":foo <: (atom | tuple)", true, Tdd.is_subtype(tdd_foo, tdd_atom_or_tuple)) -test.("{} <: (atom | tuple)", true, Tdd.is_subtype(tdd_empty_tuple, tdd_atom_or_tuple)) - -# De Morgan's Law illustration (A | B = ¬(¬A & ¬B)) -# (:foo | :bar) <: ¬(¬:foo & ¬:bar) -tdd_not_foo_and_not_bar = Tdd.intersect(Tdd.negate(tdd_foo), Tdd.negate(tdd_bar)) - -test.( - "(:foo | :bar) <: ¬(¬:foo & ¬:bar)", - true, - Tdd.is_subtype(tdd_foo_or_bar, Tdd.negate(tdd_not_foo_and_not_bar)) -) - -test.( - "¬(¬:foo & ¬:bar) <: (:foo | :bar)", - true, - Tdd.is_subtype(Tdd.negate(tdd_not_foo_and_not_bar), tdd_foo_or_bar) -) - -# Type difference: atom - :foo (represented as atom & ¬:foo) -tdd_atom_minus_foo = Tdd.intersect(tdd_atom, Tdd.negate(tdd_foo)) -test.("(atom - :foo) <: atom", true, Tdd.is_subtype(tdd_atom_minus_foo, tdd_atom)) -test.("(atom - :foo) <: :foo", false, Tdd.is_subtype(tdd_atom_minus_foo, tdd_foo)) -# True if :bar is in (atom - :foo) -test.("(atom - :foo) <: :bar", true, Tdd.is_subtype(tdd_atom_minus_foo, tdd_bar)) -test.(":bar <: (atom - :foo)", true, Tdd.is_subtype(tdd_bar, tdd_atom_minus_foo)) - -# (atom - :foo) | :foo should be atom -tdd_recombined_atom = Tdd.sum(tdd_atom_minus_foo, tdd_foo) -test.("((atom - :foo) | :foo) <: atom", true, Tdd.is_subtype(tdd_recombined_atom, tdd_atom)) -test.("atom <: ((atom - :foo) | :foo)", true, Tdd.is_subtype(tdd_atom, tdd_recombined_atom)) - -# (atom | {}) & (tuple | :foo) must be (:foo | {}) -# Represents `atom() | {}` -tdd_atom_or_empty = Tdd.sum(tdd_atom, tdd_empty_tuple) -# Represents `tuple() | :foo` -tdd_tuple_or_foo = Tdd.sum(tdd_tuple, tdd_foo) -intersected_complex = Tdd.intersect(tdd_atom_or_empty, tdd_tuple_or_foo) -# Expected result for intersected_complex is tdd_foo_or_empty_tuple - -test.( - "(atom | {}) & (tuple | :foo) <: (:foo | {})", - true, - Tdd.is_subtype(intersected_complex, tdd_foo_or_empty_tuple) -) - -test.( - "(:foo | {}) <: (atom | {}) & (tuple | :foo)", - true, - Tdd.is_subtype(tdd_foo_or_empty_tuple, intersected_complex) -) - -# {} | tuple_size_2 should be a subtype of tuple -tdd_empty_or_s2 = Tdd.sum(tdd_empty_tuple, tdd_tuple_s2) -test.("({} | tuple_size_2) <: tuple", true, Tdd.is_subtype(tdd_empty_or_s2, tdd_tuple)) - -test.( - "({} | tuple_size_2) <: ({} | tuple_size_2)", - true, - Tdd.is_subtype(tdd_empty_or_s2, tdd_empty_or_s2) -) - -test.("({} | tuple_size_2) <: tuple_size_2", false, Tdd.is_subtype(tdd_empty_or_s2, tdd_tuple_s2)) - -IO.puts("\n--- TDD structure for (atom - :foo) ---") -Tdd.print_tdd(tdd_atom_minus_foo) -IO.puts("\n--- TDD structure for ((atom - :foo) | :foo) which should be 'atom' ---") -Tdd.print_tdd(tdd_recombined_atom) -IO.puts("\n--- TDD structure for 'atom' for comparison ---") -Tdd.print_tdd(tdd_atom) - -IO.inspect(Process.get(:test_failures, [])) IO.inspect("tdd_atom_and_tuple") Tdd.print_tdd(tdd_atom_and_tuple) IO.inspect("tdd_none")