checkpoint before fixing consistency tests
This commit is contained in:
parent
130d369734
commit
36f63ed355
413
new.exs
413
new.exs
@ -682,8 +682,7 @@ defmodule Tdd.Consistency.Engine do
|
||||
end)
|
||||
|
||||
# Attempt to merge the new implications into the set of all assumptions.
|
||||
case Enum.reduce(implications, {:ok, %{}}, fn {implied_var, implied_val},
|
||||
acc ->
|
||||
case Enum.reduce(implications, {:ok, %{}}, fn {implied_var, implied_val}, acc ->
|
||||
reduce_implication({implied_var, implied_val}, all_assumptions, acc)
|
||||
end) do
|
||||
{:error, :contradiction} ->
|
||||
@ -792,7 +791,12 @@ defmodule Tdd.Consistency.Engine do
|
||||
end
|
||||
end)
|
||||
|
||||
case result, do: (:invalid -> :error; _ -> :ok)
|
||||
case result,
|
||||
do:
|
||||
(
|
||||
:invalid -> :error
|
||||
_ -> :ok
|
||||
)
|
||||
end
|
||||
|
||||
# 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
|
||||
%{type: :list_recursive_ambient, ambient_constraint_spec: spec} ->
|
||||
Map.merge(acc, %{head: spec, tail: spec})
|
||||
_ -> acc
|
||||
|
||||
_ ->
|
||||
acc
|
||||
end
|
||||
|
||||
_, acc ->
|
||||
@ -869,8 +875,11 @@ defmodule Tdd.Algo do
|
||||
def apply(op_name, op_lambda, u1_id, u2_id) do
|
||||
# Memoization wrapper
|
||||
cache_key = {:apply, op_name, Enum.sort([u1_id, u2_id])}
|
||||
|
||||
case Store.get_op_cache(cache_key) do
|
||||
{:ok, result_id} -> result_id
|
||||
{:ok, result_id} ->
|
||||
result_id
|
||||
|
||||
:not_found ->
|
||||
result_id = do_apply(op_name, op_lambda, u1_id, u2_id)
|
||||
Store.put_op_cache(cache_key, result_id)
|
||||
@ -884,19 +893,59 @@ defmodule Tdd.Algo do
|
||||
cond do
|
||||
(u1_details == :true_terminal or u1_details == :false_terminal) and
|
||||
(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()
|
||||
(u1_details == :true_terminal or u1_details == :false_terminal) ->
|
||||
if op_lambda.(u1_details, u2_details) == :true_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
|
||||
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
|
||||
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 ->
|
||||
{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])
|
||||
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_d = apply(op_name, op_lambda, if(var1 == top_var, do: d1, else: u1_id), if(var2 == top_var, do: d2, else: u2_id))
|
||||
|
||||
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_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)
|
||||
end
|
||||
end
|
||||
@ -906,15 +955,24 @@ defmodule Tdd.Algo do
|
||||
@spec negate(non_neg_integer) :: non_neg_integer
|
||||
def negate(tdd_id) do
|
||||
cache_key = {:negate, tdd_id}
|
||||
|
||||
case Store.get_op_cache(cache_key) do
|
||||
{:ok, result_id} -> result_id
|
||||
{:ok, result_id} ->
|
||||
result_id
|
||||
|
||||
:not_found ->
|
||||
result_id =
|
||||
case Store.get_node(tdd_id) do
|
||||
{:ok, :true_terminal} -> Store.false_node_id()
|
||||
{:ok, :false_terminal} -> Store.true_node_id()
|
||||
{:ok, {var, y, n, d}} -> Store.find_or_create_node(var, negate(y), negate(n), negate(d))
|
||||
{:ok, :true_terminal} ->
|
||||
Store.false_node_id()
|
||||
|
||||
{: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
|
||||
|
||||
Store.put_op_cache(cache_key, result_id)
|
||||
result_id
|
||||
end
|
||||
@ -926,8 +984,11 @@ defmodule Tdd.Algo do
|
||||
# Sort assumptions to ensure the cache key is canonical.
|
||||
sorted_assumptions = Enum.sort(assumptions)
|
||||
cache_key = {:simplify, tdd_id, sorted_assumptions}
|
||||
|
||||
case Store.get_op_cache(cache_key) do
|
||||
{:ok, result_id} -> result_id
|
||||
{:ok, result_id} ->
|
||||
result_id
|
||||
|
||||
:not_found ->
|
||||
result_id = do_simplify(tdd_id, assumptions)
|
||||
Store.put_op_cache(cache_key, result_id)
|
||||
@ -942,16 +1003,25 @@ defmodule Tdd.Algo do
|
||||
else
|
||||
case Store.get_node(tdd_id) do
|
||||
# 2. Terminal nodes are already simple.
|
||||
{:ok, :true_terminal} -> Store.true_node_id()
|
||||
{:ok, :false_terminal} -> Store.false_node_id()
|
||||
{:ok, :true_terminal} ->
|
||||
Store.true_node_id()
|
||||
|
||||
{:ok, :false_terminal} ->
|
||||
Store.false_node_id()
|
||||
|
||||
# 3. Handle non-terminal nodes.
|
||||
{:ok, {var, y, n, d}} ->
|
||||
# 4. Check if the variable's value is already known or implied.
|
||||
case Map.get(assumptions, var) do
|
||||
true -> simplify(y, assumptions)
|
||||
false -> simplify(n, assumptions)
|
||||
:dc -> simplify(d, assumptions)
|
||||
true ->
|
||||
simplify(y, assumptions)
|
||||
|
||||
false ->
|
||||
simplify(n, assumptions)
|
||||
|
||||
:dc ->
|
||||
simplify(d, assumptions)
|
||||
|
||||
nil ->
|
||||
# The variable is not explicitly constrained. Check for implied constraints.
|
||||
# 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
|
||||
|
||||
cond do
|
||||
implies_true and implies_false -> Store.false_node_id()
|
||||
implies_true -> simplify(y, assumptions)
|
||||
implies_false -> simplify(n, assumptions)
|
||||
implies_true and implies_false ->
|
||||
Store.false_node_id()
|
||||
|
||||
implies_true ->
|
||||
simplify(y, assumptions)
|
||||
|
||||
implies_false ->
|
||||
simplify(n, assumptions)
|
||||
|
||||
true ->
|
||||
# No constraint, so recursively simplify all branches.
|
||||
s_y = simplify(y, Map.put(assumptions, var, true))
|
||||
@ -974,6 +1050,7 @@ defmodule Tdd.Algo do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Tdd.TypeReconstructor do
|
||||
@moduledoc """
|
||||
Reconstructs a high-level `TypeSpec` from a low-level assumption map.
|
||||
@ -1089,6 +1166,7 @@ defmodule Tdd.TypeReconstructor do
|
||||
TypeSpec.normalize({:intersect, specs})
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Tdd.Compiler do
|
||||
@moduledoc "Compiles a `TypeSpec` into a canonical TDD ID."
|
||||
alias Tdd.TypeSpec
|
||||
@ -1104,7 +1182,9 @@ defmodule Tdd.Compiler do
|
||||
cache_key = {:spec_to_id, normalized_spec}
|
||||
|
||||
case Store.get_op_cache(cache_key) do
|
||||
{:ok, id} -> id
|
||||
{:ok, id} ->
|
||||
id
|
||||
|
||||
:not_found ->
|
||||
id = do_spec_to_id(normalized_spec)
|
||||
Store.put_op_cache(cache_key, id)
|
||||
@ -1117,46 +1197,121 @@ defmodule Tdd.Compiler do
|
||||
raw_id =
|
||||
case spec do
|
||||
# --- Base Types ---
|
||||
:any -> Store.true_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())
|
||||
:any ->
|
||||
Store.true_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.
|
||||
: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, 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())
|
||||
Store.find_or_create_node(Variable.v_is_atom(), eq_node, Store.false_node_id(), Store.false_node_id())
|
||||
eq_node =
|
||||
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) ->
|
||||
eq_node = 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())
|
||||
eq_node =
|
||||
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, []} ->
|
||||
empty_node = 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())
|
||||
empty_node =
|
||||
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
|
||||
|
||||
# --- Set-Theoretic Combinators ---
|
||||
{:union, specs} ->
|
||||
ids = Enum.map(specs, &spec_to_id/1)
|
||||
|
||||
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
|
||||
:false_terminal, t2 -> t2
|
||||
t1, :false_terminal -> t1
|
||||
end, id, acc)
|
||||
end,
|
||||
id,
|
||||
acc
|
||||
)
|
||||
end)
|
||||
|
||||
{:intersect, specs} ->
|
||||
ids = Enum.map(specs, &spec_to_id/1)
|
||||
|
||||
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
|
||||
:true_terminal, t2 -> t2
|
||||
t1, :true_terminal -> t1
|
||||
end, id, acc)
|
||||
end,
|
||||
id,
|
||||
acc
|
||||
)
|
||||
end)
|
||||
|
||||
{:negation, sub_spec} ->
|
||||
@ -1164,9 +1319,14 @@ defmodule Tdd.Compiler do
|
||||
|
||||
# --- Recursive Types (STUBS for now) ---
|
||||
# These will be implemented in Step 3
|
||||
{:list_of, _} -> raise "Tdd.Compiler: :list_of not yet implemented"
|
||||
{:tuple, _} -> raise "Tdd.Compiler: {:tuple, [...]} not yet implemented"
|
||||
{:cons, _, _} -> raise "Tdd.Compiler: :cons not yet implemented"
|
||||
{:list_of, _} ->
|
||||
raise "Tdd.Compiler: :list_of not yet implemented"
|
||||
|
||||
{:tuple, _} ->
|
||||
raise "Tdd.Compiler: {:tuple, [...]} not yet implemented"
|
||||
|
||||
{:cons, _, _} ->
|
||||
raise "Tdd.Compiler: :cons not yet implemented"
|
||||
|
||||
# --- Default ---
|
||||
_ ->
|
||||
@ -1178,6 +1338,7 @@ defmodule Tdd.Compiler do
|
||||
Algo.simplify(raw_id)
|
||||
end
|
||||
end
|
||||
|
||||
####
|
||||
# xxx
|
||||
####
|
||||
@ -2002,18 +2163,30 @@ defmodule TypeReconstructorTests do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
defmodule CompilerAlgoTests do
|
||||
alias Tdd.Compiler
|
||||
alias Tdd.Store
|
||||
alias Tdd.Algo
|
||||
|
||||
# High-level helpers that mimic the final API
|
||||
defp is_subtype(spec1, spec2) do
|
||||
id1 = Compiler.spec_to_id(spec1)
|
||||
id2 = Compiler.spec_to_id(spec2)
|
||||
# is_subtype(A, B) <=> (A & ~B) == none
|
||||
neg_id2 = Tdd.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)
|
||||
# The subtyping check is: `A <: B` if and only if `A & ~B` is empty (`:none`).
|
||||
neg_id2 = Algo.negate(id2)
|
||||
|
||||
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()
|
||||
end
|
||||
|
||||
@ -2021,11 +2194,16 @@ defmodule CompilerAlgoTests do
|
||||
Compiler.spec_to_id(spec1) == Compiler.spec_to_id(spec2)
|
||||
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_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]"
|
||||
IO.puts("#{status} #{name}")
|
||||
|
||||
@ -2036,19 +2214,22 @@ defmodule CompilerAlgoTests do
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def run() do
|
||||
IO.puts("\n--- Running Compiler & Algo Integration Tests ---")
|
||||
Process.put(:test_failures, [])
|
||||
# Setup
|
||||
Tdd.Store.init()
|
||||
|
||||
# --- Section: Basic Compilation & Equivalence ---
|
||||
IO.puts("\n--- Section: Basic Equivalences ---")
|
||||
test_equiv("atom & any == atom", true, {:intersect, [:atom, :any]}, :atom)
|
||||
test_equiv("atom | none == atom", true, {:union, [:atom, :none]}, :atom)
|
||||
test_equiv("atom & int == none", true, {:intersect, [:atom, :integer]}, :none)
|
||||
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("atom <: :foo", false, :atom, {:literal, :foo})
|
||||
test_subtype(":foo <: integer", false, {:literal, :foo}, :integer)
|
||||
@ -2056,26 +2237,136 @@ defmodule CompilerAlgoTests do
|
||||
test_subtype("none <: atom", true, :none, :atom)
|
||||
test_subtype("atom <: any", true, :atom, :any)
|
||||
|
||||
# --- Section: De Morgan's Law Check ---
|
||||
# ¬(A | B) == (¬A & ¬B)
|
||||
# --- Section: Integer Range Logic ---
|
||||
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_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 ---
|
||||
failures = Process.get(:test_failures, [])
|
||||
|
||||
if failures == [] do
|
||||
IO.puts("\n✅ All CompilerAlgoTests tests passed!")
|
||||
IO.puts("\n✅ All Compiler & Algo Integration tests passed!")
|
||||
else
|
||||
IO.puts("\n❌ Found #{length(failures)} test failures.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
TypeSpecTests.run()
|
||||
TddStoreTests.run()
|
||||
TddVariableTests.run()
|
||||
# TddAlgoTests.run()
|
||||
# ConsistencyEngineTests.run()
|
||||
TypeReconstructorTests.run()
|
||||
CompilerAlgoTests.run()
|
||||
ConsistencyEngineTests.run()
|
||||
# TypeReconstructorTests.run()
|
||||
# CompilerAlgoTests.run()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user