checkpoint before fixing consistency tests

This commit is contained in:
Kacper Marzecki 2025-06-18 15:56:12 +02:00
parent 130d369734
commit 36f63ed355

413
new.exs
View File

@ -682,8 +682,7 @@ defmodule Tdd.Consistency.Engine do
end) end)
# Attempt to merge the new implications into the set of all assumptions. # Attempt to merge the new implications into the set of all assumptions.
case Enum.reduce(implications, {:ok, %{}}, fn {implied_var, implied_val}, case Enum.reduce(implications, {:ok, %{}}, fn {implied_var, implied_val}, acc ->
acc ->
reduce_implication({implied_var, implied_val}, all_assumptions, acc) reduce_implication({implied_var, implied_val}, all_assumptions, acc)
end) do end) do
{:error, :contradiction} -> {:error, :contradiction} ->
@ -792,7 +791,12 @@ defmodule Tdd.Consistency.Engine do
end end
end) end)
case result, do: (:invalid -> :error; _ -> :ok) case result,
do:
(
:invalid -> :error
_ -> :ok
)
end end
# Helper to check if a new range is valid and continue/halt the reduction. # Helper to check if a new range is valid and continue/halt the reduction.
@ -814,7 +818,9 @@ defmodule Tdd.Consistency.Engine do
case Info.get_traits(var) do case Info.get_traits(var) do
%{type: :list_recursive_ambient, ambient_constraint_spec: spec} -> %{type: :list_recursive_ambient, ambient_constraint_spec: spec} ->
Map.merge(acc, %{head: spec, tail: spec}) Map.merge(acc, %{head: spec, tail: spec})
_ -> acc
_ ->
acc
end end
_, acc -> _, acc ->
@ -869,8 +875,11 @@ defmodule Tdd.Algo do
def apply(op_name, op_lambda, u1_id, u2_id) do def apply(op_name, op_lambda, u1_id, u2_id) do
# Memoization wrapper # Memoization wrapper
cache_key = {:apply, op_name, Enum.sort([u1_id, u2_id])} cache_key = {:apply, op_name, Enum.sort([u1_id, u2_id])}
case Store.get_op_cache(cache_key) do case Store.get_op_cache(cache_key) do
{:ok, result_id} -> result_id {:ok, result_id} ->
result_id
:not_found -> :not_found ->
result_id = do_apply(op_name, op_lambda, u1_id, u2_id) result_id = do_apply(op_name, op_lambda, u1_id, u2_id)
Store.put_op_cache(cache_key, result_id) Store.put_op_cache(cache_key, result_id)
@ -884,19 +893,59 @@ defmodule Tdd.Algo do
cond do cond do
(u1_details == :true_terminal or u1_details == :false_terminal) and (u1_details == :true_terminal or u1_details == :false_terminal) and
(u2_details == :true_terminal or u2_details == :false_terminal) -> (u2_details == :true_terminal or u2_details == :false_terminal) ->
if op_lambda.(u1_details, u2_details) == :true_terminal, do: Store.true_node_id(), else: Store.false_node_id() if op_lambda.(u1_details, u2_details) == :true_terminal,
(u1_details == :true_terminal or u1_details == :false_terminal) -> do: Store.true_node_id(),
else: Store.false_node_id()
u1_details == :true_terminal or u1_details == :false_terminal ->
{var2, y2, n2, d2} = u2_details {var2, y2, n2, d2} = u2_details
Store.find_or_create_node(var2, apply(op_name, op_lambda, u1_id, y2), apply(op_name, op_lambda, u1_id, n2), apply(op_name, op_lambda, u1_id, d2))
(u2_details == :true_terminal or u2_details == :false_terminal) -> Store.find_or_create_node(
var2,
apply(op_name, op_lambda, u1_id, y2),
apply(op_name, op_lambda, u1_id, n2),
apply(op_name, op_lambda, u1_id, d2)
)
u2_details == :true_terminal or u2_details == :false_terminal ->
{var1, y1, n1, d1} = u1_details {var1, y1, n1, d1} = u1_details
Store.find_or_create_node(var1, apply(op_name, op_lambda, y1, u2_id), apply(op_name, op_lambda, n1, u2_id), apply(op_name, op_lambda, d1, u2_id))
Store.find_or_create_node(
var1,
apply(op_name, op_lambda, y1, u2_id),
apply(op_name, op_lambda, n1, u2_id),
apply(op_name, op_lambda, d1, u2_id)
)
true -> true ->
{var1, y1, n1, d1} = u1_details; {var2, y2, n2, d2} = u2_details {var1, y1, n1, d1} = u1_details
{var2, y2, n2, d2} = u2_details
top_var = Enum.min([var1, var2]) top_var = Enum.min([var1, var2])
res_y = apply(op_name, op_lambda, if(var1 == top_var, do: y1, else: u1_id), if(var2 == top_var, do: y2, else: u2_id))
res_n = apply(op_name, op_lambda, if(var1 == top_var, do: n1, else: u1_id), if(var2 == top_var, do: n2, else: u2_id)) res_y =
res_d = apply(op_name, op_lambda, if(var1 == top_var, do: d1, else: u1_id), if(var2 == top_var, do: d2, else: u2_id)) apply(
op_name,
op_lambda,
if(var1 == top_var, do: y1, else: u1_id),
if(var2 == top_var, do: y2, else: u2_id)
)
res_n =
apply(
op_name,
op_lambda,
if(var1 == top_var, do: n1, else: u1_id),
if(var2 == top_var, do: n2, else: u2_id)
)
res_d =
apply(
op_name,
op_lambda,
if(var1 == top_var, do: d1, else: u1_id),
if(var2 == top_var, do: d2, else: u2_id)
)
Store.find_or_create_node(top_var, res_y, res_n, res_d) Store.find_or_create_node(top_var, res_y, res_n, res_d)
end end
end end
@ -906,15 +955,24 @@ defmodule Tdd.Algo do
@spec negate(non_neg_integer) :: non_neg_integer @spec negate(non_neg_integer) :: non_neg_integer
def negate(tdd_id) do def negate(tdd_id) do
cache_key = {:negate, tdd_id} cache_key = {:negate, tdd_id}
case Store.get_op_cache(cache_key) do case Store.get_op_cache(cache_key) do
{:ok, result_id} -> result_id {:ok, result_id} ->
result_id
:not_found -> :not_found ->
result_id = result_id =
case Store.get_node(tdd_id) do case Store.get_node(tdd_id) do
{:ok, :true_terminal} -> Store.false_node_id() {:ok, :true_terminal} ->
{:ok, :false_terminal} -> Store.true_node_id() Store.false_node_id()
{:ok, {var, y, n, d}} -> Store.find_or_create_node(var, negate(y), negate(n), negate(d))
{:ok, :false_terminal} ->
Store.true_node_id()
{:ok, {var, y, n, d}} ->
Store.find_or_create_node(var, negate(y), negate(n), negate(d))
end end
Store.put_op_cache(cache_key, result_id) Store.put_op_cache(cache_key, result_id)
result_id result_id
end end
@ -926,8 +984,11 @@ defmodule Tdd.Algo do
# Sort assumptions to ensure the cache key is canonical. # Sort assumptions to ensure the cache key is canonical.
sorted_assumptions = Enum.sort(assumptions) sorted_assumptions = Enum.sort(assumptions)
cache_key = {:simplify, tdd_id, sorted_assumptions} cache_key = {:simplify, tdd_id, sorted_assumptions}
case Store.get_op_cache(cache_key) do case Store.get_op_cache(cache_key) do
{:ok, result_id} -> result_id {:ok, result_id} ->
result_id
:not_found -> :not_found ->
result_id = do_simplify(tdd_id, assumptions) result_id = do_simplify(tdd_id, assumptions)
Store.put_op_cache(cache_key, result_id) Store.put_op_cache(cache_key, result_id)
@ -942,16 +1003,25 @@ defmodule Tdd.Algo do
else else
case Store.get_node(tdd_id) do case Store.get_node(tdd_id) do
# 2. Terminal nodes are already simple. # 2. Terminal nodes are already simple.
{:ok, :true_terminal} -> Store.true_node_id() {:ok, :true_terminal} ->
{:ok, :false_terminal} -> Store.false_node_id() Store.true_node_id()
{:ok, :false_terminal} ->
Store.false_node_id()
# 3. Handle non-terminal nodes. # 3. Handle non-terminal nodes.
{:ok, {var, y, n, d}} -> {:ok, {var, y, n, d}} ->
# 4. Check if the variable's value is already known or implied. # 4. Check if the variable's value is already known or implied.
case Map.get(assumptions, var) do case Map.get(assumptions, var) do
true -> simplify(y, assumptions) true ->
false -> simplify(n, assumptions) simplify(y, assumptions)
:dc -> simplify(d, assumptions)
false ->
simplify(n, assumptions)
:dc ->
simplify(d, assumptions)
nil -> nil ->
# The variable is not explicitly constrained. Check for implied constraints. # The variable is not explicitly constrained. Check for implied constraints.
# Note: For now, the Engine only performs flat checks. # Note: For now, the Engine only performs flat checks.
@ -959,9 +1029,15 @@ defmodule Tdd.Algo do
implies_false = Engine.check(Map.put(assumptions, var, true)) == :contradiction implies_false = Engine.check(Map.put(assumptions, var, true)) == :contradiction
cond do cond do
implies_true and implies_false -> Store.false_node_id() implies_true and implies_false ->
implies_true -> simplify(y, assumptions) Store.false_node_id()
implies_false -> simplify(n, assumptions)
implies_true ->
simplify(y, assumptions)
implies_false ->
simplify(n, assumptions)
true -> true ->
# No constraint, so recursively simplify all branches. # No constraint, so recursively simplify all branches.
s_y = simplify(y, Map.put(assumptions, var, true)) s_y = simplify(y, Map.put(assumptions, var, true))
@ -974,6 +1050,7 @@ defmodule Tdd.Algo do
end end
end end
end end
defmodule Tdd.TypeReconstructor do defmodule Tdd.TypeReconstructor do
@moduledoc """ @moduledoc """
Reconstructs a high-level `TypeSpec` from a low-level assumption map. Reconstructs a high-level `TypeSpec` from a low-level assumption map.
@ -1089,6 +1166,7 @@ defmodule Tdd.TypeReconstructor do
TypeSpec.normalize({:intersect, specs}) TypeSpec.normalize({:intersect, specs})
end end
end end
defmodule Tdd.Compiler do defmodule Tdd.Compiler do
@moduledoc "Compiles a `TypeSpec` into a canonical TDD ID." @moduledoc "Compiles a `TypeSpec` into a canonical TDD ID."
alias Tdd.TypeSpec alias Tdd.TypeSpec
@ -1104,7 +1182,9 @@ defmodule Tdd.Compiler do
cache_key = {:spec_to_id, normalized_spec} cache_key = {:spec_to_id, normalized_spec}
case Store.get_op_cache(cache_key) do case Store.get_op_cache(cache_key) do
{:ok, id} -> id {:ok, id} ->
id
:not_found -> :not_found ->
id = do_spec_to_id(normalized_spec) id = do_spec_to_id(normalized_spec)
Store.put_op_cache(cache_key, id) Store.put_op_cache(cache_key, id)
@ -1117,46 +1197,121 @@ defmodule Tdd.Compiler do
raw_id = raw_id =
case spec do case spec do
# --- Base Types --- # --- Base Types ---
:any -> Store.true_node_id() :any ->
:none -> Store.false_node_id() Store.true_node_id()
:atom -> Store.find_or_create_node(Variable.v_is_atom(), Store.true_node_id(), Store.false_node_id(), Store.false_node_id())
:integer -> Store.find_or_create_node(Variable.v_is_integer(), Store.true_node_id(), Store.false_node_id(), Store.false_node_id()) :none ->
Store.false_node_id()
:atom ->
Store.find_or_create_node(
Variable.v_is_atom(),
Store.true_node_id(),
Store.false_node_id(),
Store.false_node_id()
)
:integer ->
Store.find_or_create_node(
Variable.v_is_integer(),
Store.true_node_id(),
Store.false_node_id(),
Store.false_node_id()
)
# Add :list, :tuple etc. here. They are simple structural TDDs. # Add :list, :tuple etc. here. They are simple structural TDDs.
:list -> Store.find_or_create_node(Variable.v_is_list(), Store.true_node_id(), Store.false_node_id(), Store.false_node_id()) :list ->
Store.find_or_create_node(
Variable.v_is_list(),
Store.true_node_id(),
Store.false_node_id(),
Store.false_node_id()
)
# --- Literal Types --- # --- Literal Types ---
{:literal, val} when is_atom(val) -> {:literal, val} when is_atom(val) ->
eq_node = Store.find_or_create_node(Variable.v_atom_eq(val), Store.true_node_id(), Store.false_node_id(), Store.false_node_id()) eq_node =
Store.find_or_create_node(Variable.v_is_atom(), eq_node, Store.false_node_id(), Store.false_node_id()) Store.find_or_create_node(
Variable.v_atom_eq(val),
Store.true_node_id(),
Store.false_node_id(),
Store.false_node_id()
)
Store.find_or_create_node(
Variable.v_is_atom(),
eq_node,
Store.false_node_id(),
Store.false_node_id()
)
{:literal, val} when is_integer(val) -> {:literal, val} when is_integer(val) ->
eq_node = Store.find_or_create_node(Variable.v_int_eq(val), Store.true_node_id(), Store.false_node_id(), Store.false_node_id()) eq_node =
Store.find_or_create_node(Variable.v_is_integer(), eq_node, Store.false_node_id(), Store.false_node_id()) Store.find_or_create_node(
Variable.v_int_eq(val),
Store.true_node_id(),
Store.false_node_id(),
Store.false_node_id()
)
Store.find_or_create_node(
Variable.v_is_integer(),
eq_node,
Store.false_node_id(),
Store.false_node_id()
)
{:literal, []} -> {:literal, []} ->
empty_node = Store.find_or_create_node(Variable.v_list_is_empty(), Store.true_node_id(), Store.false_node_id(), Store.false_node_id()) empty_node =
Store.find_or_create_node(Variable.v_is_list(), empty_node, Store.false_node_id(), Store.false_node_id()) Store.find_or_create_node(
Variable.v_list_is_empty(),
Store.true_node_id(),
Store.false_node_id(),
Store.false_node_id()
)
Store.find_or_create_node(
Variable.v_is_list(),
empty_node,
Store.false_node_id(),
Store.false_node_id()
)
# Add other literals as needed # Add other literals as needed
# --- Set-Theoretic Combinators --- # --- Set-Theoretic Combinators ---
{:union, specs} -> {:union, specs} ->
ids = Enum.map(specs, &spec_to_id/1) ids = Enum.map(specs, &spec_to_id/1)
Enum.reduce(ids, Store.false_node_id(), fn id, acc -> Enum.reduce(ids, Store.false_node_id(), fn id, acc ->
Algo.apply(:sum, fn Algo.apply(
:sum,
fn
:true_terminal, _ -> :true_terminal :true_terminal, _ -> :true_terminal
_, :true_terminal -> :true_terminal _, :true_terminal -> :true_terminal
:false_terminal, t2 -> t2 :false_terminal, t2 -> t2
t1, :false_terminal -> t1 t1, :false_terminal -> t1
end, id, acc) end,
id,
acc
)
end) end)
{:intersect, specs} -> {:intersect, specs} ->
ids = Enum.map(specs, &spec_to_id/1) ids = Enum.map(specs, &spec_to_id/1)
Enum.reduce(ids, Store.true_node_id(), fn id, acc -> Enum.reduce(ids, Store.true_node_id(), fn id, acc ->
Algo.apply(:intersect, fn Algo.apply(
:intersect,
fn
:false_terminal, _ -> :false_terminal :false_terminal, _ -> :false_terminal
_, :false_terminal -> :false_terminal _, :false_terminal -> :false_terminal
:true_terminal, t2 -> t2 :true_terminal, t2 -> t2
t1, :true_terminal -> t1 t1, :true_terminal -> t1
end, id, acc) end,
id,
acc
)
end) end)
{:negation, sub_spec} -> {:negation, sub_spec} ->
@ -1164,9 +1319,14 @@ defmodule Tdd.Compiler do
# --- Recursive Types (STUBS for now) --- # --- Recursive Types (STUBS for now) ---
# These will be implemented in Step 3 # These will be implemented in Step 3
{:list_of, _} -> raise "Tdd.Compiler: :list_of not yet implemented" {:list_of, _} ->
{:tuple, _} -> raise "Tdd.Compiler: {:tuple, [...]} not yet implemented" raise "Tdd.Compiler: :list_of not yet implemented"
{:cons, _, _} -> raise "Tdd.Compiler: :cons not yet implemented"
{:tuple, _} ->
raise "Tdd.Compiler: {:tuple, [...]} not yet implemented"
{:cons, _, _} ->
raise "Tdd.Compiler: :cons not yet implemented"
# --- Default --- # --- Default ---
_ -> _ ->
@ -1178,6 +1338,7 @@ defmodule Tdd.Compiler do
Algo.simplify(raw_id) Algo.simplify(raw_id)
end end
end end
#### ####
# xxx # xxx
#### ####
@ -2002,18 +2163,30 @@ defmodule TypeReconstructorTests do
end end
end end
end end
defmodule CompilerAlgoTests do defmodule CompilerAlgoTests do
alias Tdd.Compiler alias Tdd.Compiler
alias Tdd.Store alias Tdd.Store
alias Tdd.Algo
# High-level helpers that mimic the final API # High-level helpers that mimic the final API
defp is_subtype(spec1, spec2) do defp is_subtype(spec1, spec2) do
id1 = Compiler.spec_to_id(spec1) id1 = Compiler.spec_to_id(spec1)
id2 = Compiler.spec_to_id(spec2) id2 = Compiler.spec_to_id(spec2)
# is_subtype(A, B) <=> (A & ~B) == none # The subtyping check is: `A <: B` if and only if `A & ~B` is empty (`:none`).
neg_id2 = Tdd.Algo.negate(id2) neg_id2 = Algo.negate(id2)
intersect_id = Tdd.Algo.apply(:intersect, &(&1 == :false_terminal or &2 == :false_terminal), id1, neg_id2)
final_id = Tdd.Algo.simplify(intersect_id) op_intersect = fn
:false_terminal, _ -> :false_terminal
_, :false_terminal -> :false_terminal
t, :true_terminal -> t
:true_terminal, t -> t
# Default case for non-terminal nodes, though apply handles recursion
_t1, _t2 -> :non_terminal
end
intersect_id = Algo.apply(:intersect, op_intersect, id1, neg_id2)
final_id = Algo.simplify(intersect_id)
final_id == Store.false_node_id() final_id == Store.false_node_id()
end end
@ -2021,11 +2194,16 @@ defmodule CompilerAlgoTests do
Compiler.spec_to_id(spec1) == Compiler.spec_to_id(spec2) Compiler.spec_to_id(spec1) == Compiler.spec_to_id(spec2)
end end
defp is_contradiction(spec) do
Compiler.spec_to_id(spec) == Store.false_node_id()
end
defp test_subtype(name, expected, s1, s2), do: test(name, expected, is_subtype(s1, s2)) defp test_subtype(name, expected, s1, s2), do: test(name, expected, is_subtype(s1, s2))
defp test_equiv(name, expected, s1, s2), do: test(name, expected, are_equivalent(s1, s2)) defp test_equiv(name, expected, s1, s2), do: test(name, expected, are_equivalent(s1, s2))
defp test(name, exp, res) do defp test_contradiction(name, expected \\ true), do: &test(name, expected, is_contradiction(&1))
is_ok = exp==res defp test(name, exp, res) do
is_ok = exp == res
status = if is_ok, do: "[PASS]", else: "[FAIL]" status = if is_ok, do: "[PASS]", else: "[FAIL]"
IO.puts("#{status} #{name}") IO.puts("#{status} #{name}")
@ -2036,19 +2214,22 @@ defmodule CompilerAlgoTests do
end end
end end
def run() do def run() do
IO.puts("\n--- Running Compiler & Algo Integration Tests ---") IO.puts("\n--- Running Compiler & Algo Integration Tests ---")
Process.put(:test_failures, [])
# Setup # Setup
Tdd.Store.init() Tdd.Store.init()
# --- Section: Basic Compilation & Equivalence --- # --- Section: Basic Compilation & Equivalence ---
IO.puts("\n--- Section: Basic Equivalences ---")
test_equiv("atom & any == atom", true, {:intersect, [:atom, :any]}, :atom) test_equiv("atom & any == atom", true, {:intersect, [:atom, :any]}, :atom)
test_equiv("atom | none == atom", true, {:union, [:atom, :none]}, :atom) test_equiv("atom | none == atom", true, {:union, [:atom, :none]}, :atom)
test_equiv("atom & int == none", true, {:intersect, [:atom, :integer]}, :none) test_equiv("atom & int == none", true, {:intersect, [:atom, :integer]}, :none)
test_equiv("¬(¬atom) == atom", true, {:negation, {:negation, :atom}}, :atom) test_equiv("¬(¬atom) == atom", true, {:negation, {:negation, :atom}}, :atom)
test_equiv("atom | atom == atom", true, {:union, [:atom, :atom]}, :atom)
# --- Section: Subtyping Tests --- # --- Section: Basic Subtyping ---
IO.puts("\n--- Section: Basic Subtyping ---")
test_subtype(":foo <: atom", true, {:literal, :foo}, :atom) test_subtype(":foo <: atom", true, {:literal, :foo}, :atom)
test_subtype("atom <: :foo", false, :atom, {:literal, :foo}) test_subtype("atom <: :foo", false, :atom, {:literal, :foo})
test_subtype(":foo <: integer", false, {:literal, :foo}, :integer) test_subtype(":foo <: integer", false, {:literal, :foo}, :integer)
@ -2056,26 +2237,136 @@ defmodule CompilerAlgoTests do
test_subtype("none <: atom", true, :none, :atom) test_subtype("none <: atom", true, :none, :atom)
test_subtype("atom <: any", true, :atom, :any) test_subtype("atom <: any", true, :atom, :any)
# --- Section: De Morgan's Law Check --- # --- Section: Integer Range Logic ---
# ¬(A | B) == (¬A & ¬B) IO.puts("\n--- Section: Integer Range Logic ---")
int_5_to_10 = {:integer_range, 5, 10}
int_7_to_8 = {:integer_range, 7, 8}
int_15_to_20 = {:integer_range, 15, 20}
int_0_to_100 = {:integer_range, 0, 100}
test_subtype("range(7..8) <: range(5..10)", true, int_7_to_8, int_5_to_10)
test_subtype("range(5..10) <: range(7..8)", false, int_5_to_10, int_7_to_8)
test_subtype("range(5..10) <: range(15..20)", false, int_5_to_10, int_15_to_20)
test_equiv(
"range(5..10) & range(7..8) == range(7..8)",
true,
{:intersect, [int_5_to_10, int_7_to_8]},
int_7_to_8
)
test_equiv(
"range(5..10) & range(0..100) == range(5..10)",
true,
{:intersect, [int_5_to_10, int_0_to_100]},
int_5_to_10
)
test_equiv(
"range(5..10) | range(7..8) == range(5..10)",
true,
{:union, [int_5_to_10, int_7_to_8]},
int_5_to_10
)
# --- Section: Contradictions & Simplifications ---
IO.puts("\n--- Section: Contradictions & Simplifications ---")
test_contradiction("atom & integer").({:intersect, [:atom, :integer]})
test_contradiction(":foo & :bar").({:intersect, [{:literal, :foo}, {:literal, :bar}]})
test_contradiction("atom & (int==5)").({:intersect, [:atom, {:literal, 5}]})
test_contradiction("range(5..10) & range(15..20)").({:intersect, [int_5_to_10, int_15_to_20]})
test_contradiction("integer & ¬integer").({:intersect, [:integer, {:negation, :integer}]})
# --- Section: Subtype Reduction in Normalization ---
IO.puts("\n--- Section: Subtype Reduction Logic ---")
test_equiv(
"(:foo | :bar | atom) simplifies to atom",
true,
{:union, [{:literal, :foo}, {:literal, :bar}, :atom]},
:atom
)
test_equiv(
"(range(5..10) | integer) simplifies to integer",
true,
{:union, [int_5_to_10, :integer]},
:integer
)
test_equiv(
"(:foo & atom) simplifies to :foo",
true,
{:intersect, [{:literal, :foo}, :atom]},
{:literal, :foo}
)
test_equiv(
"(range(5..10) & integer) simplifies to range(5..10)",
true,
{:intersect, [int_5_to_10, :integer]},
int_5_to_10
)
# --- Section: Logical Laws (Distribution and De Morgan's) ---
IO.puts("\n--- Section: Logical Laws ---")
# De Morgan's Law: ¬(A | B) == (¬A & ¬B)
spec_not_a_or_b = {:negation, {:union, [:atom, :integer]}} spec_not_a_or_b = {:negation, {:union, [:atom, :integer]}}
spec_not_a_and_not_b = {:intersect, [{:negation, :atom}, {:negation, :integer}]} spec_not_a_and_not_b = {:intersect, [{:negation, :atom}, {:negation, :integer}]}
test_equiv("De Morgan's Law holds", true, spec_not_a_or_b, spec_not_a_and_not_b)
test_equiv(
"De Morgan's (¬(A|B) == ¬A & ¬B) holds",
true,
spec_not_a_or_b,
spec_not_a_and_not_b
)
# De Morgan's Law: ¬(A & B) == (¬A | ¬B)
spec_not_a_and_b = {:negation, {:intersect, [{:literal, :foo}, int_5_to_10]}}
spec_not_a_or_not_b = {:union, [{:negation, {:literal, :foo}}, {:negation, int_5_to_10}]}
test_equiv(
"De Morgan's (¬(A&B) == ¬A | ¬B) holds",
true,
spec_not_a_and_b,
spec_not_a_or_not_b
)
# Distributive Law: A & (B | C) == (A & B) | (A & C)
spec_a = :integer
spec_b = {:integer_range, 0, 10}
spec_c = {:integer_range, 20, 30}
spec_dist_lhs = {:intersect, [spec_a, {:union, [spec_b, spec_c]}]}
spec_dist_rhs = {:union, [{:intersect, [spec_a, spec_b]}, {:intersect, [spec_a, spec_c]}]}
test_equiv(
"Distributive Law (A & (B|C)) holds",
true,
spec_dist_lhs,
spec_dist_rhs
)
# --- Final Report --- # --- Final Report ---
failures = Process.get(:test_failures, []) failures = Process.get(:test_failures, [])
if failures == [] do if failures == [] do
IO.puts("\n✅ All CompilerAlgoTests tests passed!") IO.puts("\n✅ All Compiler & Algo Integration tests passed!")
else else
IO.puts("\n❌ Found #{length(failures)} test failures.") IO.puts("\n❌ Found #{length(failures)} test failures.")
end end
end end
end end
TypeSpecTests.run() TypeSpecTests.run()
TddStoreTests.run() TddStoreTests.run()
TddVariableTests.run() TddVariableTests.run()
# TddAlgoTests.run() # TddAlgoTests.run()
# ConsistencyEngineTests.run() ConsistencyEngineTests.run()
TypeReconstructorTests.run() # TypeReconstructorTests.run()
CompilerAlgoTests.run() # CompilerAlgoTests.run()