\set ECHO none
\pset format unaligned

-- The joint-width UCQ compiler evaluates an arbitrary union of conjunctive
-- queries -- including the queries that are #P-hard under the Dalvi-Suciu
-- dichotomy (H0 = R(x),S(x,y),T(y) and the Hk family) -- exactly, in time
-- tractable whenever the joint treewidth of the data and its correlation
-- structure is bounded (the data-graph regime here: every fact an
-- independent gate_input, so the joint graph is the Gaifman graph of the
-- facts and the screen is the data treewidth).  A UCQ-specialised
-- homomorphism-type DP runs over a tree decomposition of the joint graph,
-- emitting a certified d-D by construction; the width columns and the
-- probability come from ucq_joint_compile_stats, exercised here against
-- closed-form probabilities.

-- Convenience: a UUID per name.
CREATE OR REPLACE FUNCTION u(name text) RETURNS uuid AS $$
  SELECT public.uuid_generate_v5(uuid_ns_provsql(), name);
$$ LANGUAGE sql IMMUTABLE;

-- H0 = R(x), S(x,y), T(y) over two disjoint chains 0-2, 1-3, all events
-- 0.5.  Closed form: 1 - (1 - 0.5^3)^2 = 0.234375.
SELECT round((SELECT probability FROM ucq_joint_compile_stats(
  '{"disjuncts":[{"n_vars":4,"atoms":[
      {"rel":0,"vars":[0]},{"rel":1,"vars":[0,1]},{"rel":2,"vars":[1]}]}]}'::jsonb,
  ARRAY[0,0, 1,1, 2,2],
  ARRAY[0, 1, 0,2, 1,3, 2, 3],
  ARRAY[1,1, 2,2, 1,1],
  ARRAY[u('r0'),u('r1'),u('s02'),u('s13'),u('t2'),u('t3')]::uuid[],
  ARRAY[0.5,0.5,0.5,0.5,0.5,0.5]))::numeric, 6) AS h0_two_chains;

-- H0 with a shared y (correlation through the query, not the data):
-- r0=0.7, r1=0.3, s02=0.6, s12=0.4, t2=0.8.  y=2 only:
-- 0.8 * (1 - (1-0.7*0.6)(1-0.3*0.4)) = 0.39168.
SELECT round((SELECT probability FROM ucq_joint_compile_stats(
  '{"disjuncts":[{"n_vars":3,"atoms":[
      {"rel":0,"vars":[0]},{"rel":1,"vars":[0,1]},{"rel":2,"vars":[1]}]}]}'::jsonb,
  ARRAY[0,0, 1,1, 2],
  ARRAY[0, 1, 0,2, 1,2, 2],
  ARRAY[1,1, 2,2, 1],
  ARRAY[u('r0'),u('r1'),u('s02'),u('s12'),u('t2')]::uuid[],
  ARRAY[0.7,0.3,0.6,0.4,0.8]))::numeric, 6) AS h0_shared_y;

-- Prop 4.2.11(a): chi = OR_i (A(a_i,b_i) AND B(b_i,c_i)) with disjoint
-- variables per conjunct -> joint width O(1).  a0=0.3,b0=0.4 (witness
-- 0,1,2), a1=0.5,b1=0.6 (witness 3,4,5).
-- 1 - (1 - 0.3*0.4)(1 - 0.5*0.6) = 0.384.
SELECT round((SELECT probability FROM ucq_joint_compile_stats(
  '{"disjuncts":[{"n_vars":3,"atoms":[
      {"rel":0,"vars":[0,1]},{"rel":1,"vars":[1,2]}]}]}'::jsonb,
  ARRAY[0,1, 0,1],
  ARRAY[0,1, 1,2, 3,4, 4,5],
  ARRAY[2,2, 2,2],
  ARRAY[u('a0'),u('b0'),u('a1'),u('b1')]::uuid[],
  ARRAY[0.3,0.4,0.5,0.6]))::numeric, 6) AS prop_4_2_11_a;

-- Prop 4.2.11(c): a 6-cycle over S, query exists x,y S(x,y), all 0.5 ->
-- 1 - (1-0.5)^6 = 0.984375; accepted with joint treewidth 2.
SELECT round((SELECT probability FROM ucq_joint_compile_stats(
  '{"disjuncts":[{"n_vars":2,"atoms":[{"rel":0,"vars":[0,1]}]}]}'::jsonb,
  ARRAY[0,0,0,0,0,0],
  ARRAY[0,1, 1,2, 2,3, 3,4, 4,5, 5,0],
  ARRAY[2,2,2,2,2,2],
  ARRAY[u('c0'),u('c1'),u('c2'),u('c3'),u('c4'),u('c5')]::uuid[],
  ARRAY[0.5,0.5,0.5,0.5,0.5,0.5]))::numeric, 6) AS prop_4_2_11_c_cycle;

-- A self-join R(x), S(x,y), R(y) over a path 0-1-2, all 0.5.
-- Satisfied iff some edge (x,y) has both endpoints in R: edges (0,1),(1,2).
-- P = 1 - (1 - r0 r1 s01)(1 - r1 r2 s12) with overlap on r1:
-- worlds: enumerate -> 0.21875.
SELECT round((SELECT probability FROM ucq_joint_compile_stats(
  '{"disjuncts":[{"n_vars":2,"atoms":[
      {"rel":0,"vars":[0]},{"rel":1,"vars":[0,1]},{"rel":0,"vars":[1]}]}]}'::jsonb,
  ARRAY[0,0,0, 1,1],
  ARRAY[0, 1, 2, 0,1, 1,2],
  ARRAY[1,1,1, 2,2],
  ARRAY[u('r0'),u('r1'),u('r2'),u('s01'),u('s12')]::uuid[],
  ARRAY[0.5,0.5,0.5,0.5,0.5]))::numeric, 6) AS self_join_R_S_R;

-- UCQ with two disjuncts: H0 over 0-1, OR exists x R(x).  R has r0,r2;
-- the second disjunct alone gives 1-(1-0.5)^2 = 0.75 over {r0,r2}; the
-- first adds nothing new (R(0) already in the OR) -> 0.75.
SELECT round((SELECT probability FROM ucq_joint_compile_stats(
  '{"disjuncts":[
      {"n_vars":2,"atoms":[{"rel":0,"vars":[0]},{"rel":1,"vars":[0,1]},{"rel":2,"vars":[1]}]},
      {"n_vars":1,"atoms":[{"rel":0,"vars":[0]}]}]}'::jsonb,
  ARRAY[0,1,2, 0],
  ARRAY[0, 0,1, 1, 2],
  ARRAY[1,2,1, 1],
  ARRAY[u('r0'),u('s01'),u('t1'),u('r2')]::uuid[],
  ARRAY[0.5,0.5,0.5,0.5]))::numeric, 6) AS ucq_two_disjuncts;

-- Stats: the three width columns on the two-chain H0 instance.
SELECT joint_treewidth, data_treewidth_lb, circuit_treewidth_lb
FROM ucq_joint_compile_stats(
  '{"disjuncts":[{"n_vars":4,"atoms":[
      {"rel":0,"vars":[0]},{"rel":1,"vars":[0,1]},{"rel":2,"vars":[1]}]}]}'::jsonb,
  ARRAY[0,0, 1,1, 2,2],
  ARRAY[0, 1, 0,2, 1,3, 2, 3],
  ARRAY[1,1, 2,2, 1,1],
  ARRAY[u('r0'),u('r1'),u('s02'),u('s13'),u('t2'),u('t3')]::uuid[],
  ARRAY[0.5,0.5,0.5,0.5,0.5,0.5]);

-- FD-aware essential-variable count (n_enumerating): a join variable
-- functionally determined by others -- via an FD *mined from the gathered
-- tuples* -- is not counted in the exponential parameter e.  H0 =
-- R(x),S(x,y),T(y): when S carries x -> y in the data, y is determined by x
-- so e = 1; add a second y for some x and the FD breaks, so e = 2.
SELECT n_enumerating AS e_fd_holds FROM ucq_joint_compile_stats(
  '{"disjuncts":[{"n_vars":2,"atoms":[
      {"rel":0,"vars":[0]},{"rel":1,"vars":[0,1]},{"rel":2,"vars":[1]}]}]}'::jsonb,
  ARRAY[0,0, 1,1, 2,2],
  ARRAY[0, 1, 0,2, 1,3, 2, 3],
  ARRAY[1,1, 2,2, 1,1],
  ARRAY[u('r0'),u('r1'),u('s02'),u('s13'),u('t2'),u('t3')]::uuid[],
  ARRAY[0.5,0.5,0.5,0.5,0.5,0.5]);
SELECT n_enumerating AS e_fd_broken FROM ucq_joint_compile_stats(
  '{"disjuncts":[{"n_vars":2,"atoms":[
      {"rel":0,"vars":[0]},{"rel":1,"vars":[0,1]},{"rel":2,"vars":[1]}]}]}'::jsonb,
  ARRAY[0,0, 1,1,1, 2,2],
  ARRAY[0, 1, 0,2, 1,3, 0,3, 2, 3],
  ARRAY[1,1, 2,2,2, 1,1],
  ARRAY[u('r0'),u('r1'),u('s02'),u('s13'),u('s03'),u('t2'),u('t3')]::uuid[],
  ARRAY[0.5,0.5,0.5,0.5,0.5,0.5,0.5]);

-- Prop 4.2.11(b): a high-joint-width instance must be rejected (the
-- caller then falls back to the ladder).  With the joint-width cap
-- lowered to 1, a triangle over S (Gaifman treewidth 2) is rejected by
-- the degeneracy screen.
SET provsql.joint_max_treewidth = 1;
-- terse: the error's CONTEXT echoes the wrapper's SELECT ... INTO ... FROM,
-- whose blanked-INTO whitespace is platform-dependent; keep only the message.
\set VERBOSITY terse
SELECT (SELECT probability FROM ucq_joint_compile_stats(
  '{"disjuncts":[{"n_vars":2,"atoms":[{"rel":0,"vars":[0,1]}]}]}'::jsonb,
  ARRAY[0,0,0],
  ARRAY[0,1, 1,2, 0,2],
  ARRAY[2,2,2],
  ARRAY[u('e01'),u('e12'),u('e02')]::uuid[],
  ARRAY[0.5,0.5,0.5])) AS should_error;
\set VERBOSITY default
RESET provsql.joint_max_treewidth;

DROP FUNCTION u(text);
