\set ECHO none
\pset format unaligned

-- Each binary arithmetic operator builds a gate_arith carrying the
-- expected provsql_arith_op tag in info1
-- (PLUS=0, TIMES=1, MINUS=2, DIV=3, NEG=4) and returns a new
-- random_variable whose UUID is the gate's UUID.  We use as_random
-- for stable UUIDs so the test reads as a pure structural check.

WITH e AS (
  SELECT provsql.as_random(2) AS a, provsql.as_random(3) AS b
)
SELECT
  get_gate_type((a + b)::uuid) AS plus_kind,
  (get_infos((a + b)::uuid)).info1 AS plus_op,
  get_gate_type((a - b)::uuid) AS minus_kind,
  (get_infos((a - b)::uuid)).info1 AS minus_op,
  get_gate_type((a * b)::uuid) AS times_kind,
  (get_infos((a * b)::uuid)).info1 AS times_op,
  get_gate_type((a / b)::uuid) AS div_kind,
  (get_infos((a / b)::uuid)).info1 AS div_op,
  get_gate_type((-a)::uuid)    AS neg_kind,
  (get_infos((-a)::uuid)).info1 AS neg_op
FROM e;

-- Children of each arith gate are the operand UUIDs in operand order.
WITH e AS (
  SELECT provsql.as_random(2) AS a, provsql.as_random(3) AS b
)
SELECT
  get_children((a + b)::uuid) = ARRAY[a::uuid, b::uuid] AS plus_children,
  get_children((a - b)::uuid) = ARRAY[a::uuid, b::uuid] AS minus_children_lr,
  get_children((b - a)::uuid) = ARRAY[b::uuid, a::uuid] AS minus_children_rl,
  get_children((-a)::uuid)    = ARRAY[a::uuid]          AS neg_children
FROM e;

-- provenance_arith is deterministic on (op, children): two writes of
-- the same expression land on the same gate (the v5 derivation in the
-- helper makes this so).  Different operators on the same children land
-- on different gates because the op tag enters the namespace key.
WITH e AS (
  SELECT provsql.as_random(2) AS a, provsql.as_random(3) AS b
)
SELECT
  (a + b)::uuid =  (a + b)::uuid AS plus_dedup,
  (a + b)::uuid <> (a * b)::uuid AS plus_neq_times,
  (a - b)::uuid <> (b - a)::uuid AS minus_orientation_matters
FROM e;

-- Implicit casts: PG operator resolution falls back on the implicit
-- integer/numeric/float8 -> random_variable casts because we only
-- declared (rv, rv) operators.  Mixed shapes are equivalent to
-- wrapping the literal in as_random by hand.  (Comparison operators
-- return boolean placeholders that are rewritten by the planner hook
-- in WHERE; for direct UUID construction in the SELECT list we use
-- the rv_cmp_* helpers.)
WITH e AS (SELECT provsql.as_random(2) AS a)
SELECT
  (a + 2)::uuid             = (a + provsql.as_random(2))::uuid             AS plus_int,
  (a + 2.5)::uuid           = (a + provsql.as_random(2.5))::uuid           AS plus_numeric,
  (a + 2.5::float8)::uuid   = (a + provsql.as_random(2.5::float8))::uuid   AS plus_float8,
  (2 + a)::uuid             = (provsql.as_random(2) + a)::uuid             AS plus_reversed,
  rv_cmp_gt(a, 2::random_variable) = rv_cmp_gt(a, provsql.as_random(2)) AS gt_int,
  rv_cmp_lt(2::random_variable, a) = rv_cmp_lt(provsql.as_random(2), a) AS lt_reversed
FROM e;

-- Comparison helpers build a gate_cmp whose info1 holds the float8
-- comparator OID (any OID with the right opname works -- cmpOpFromOid
-- in src/Aggregation.cpp keys on the symbol).  Children are the two
-- operand UUIDs in left-right order.
WITH e AS (
  SELECT provsql.as_random(2) AS a, provsql.as_random(3) AS b
)
SELECT
  get_gate_type(rv_cmp_lt(a, b)) AS lt_kind,
  get_gate_type(rv_cmp_le(a, b)) AS le_kind,
  get_gate_type(rv_cmp_eq(a, b)) AS eq_kind,
  get_gate_type(rv_cmp_ne(a, b)) AS ne_kind,
  get_gate_type(rv_cmp_ge(a, b)) AS ge_kind,
  get_gate_type(rv_cmp_gt(a, b)) AS gt_kind,
  get_children(rv_cmp_gt(a, b)) = ARRAY[a::uuid, b::uuid] AS gt_children
FROM e;

-- The OID stored in info1 round-trips through pg_operator back to
-- the symbol.  Pin the six comparators.
WITH e AS (
  SELECT provsql.as_random(2) AS a, provsql.as_random(3) AS b
)
SELECT
  (SELECT oprname FROM pg_operator
    WHERE oid = ((get_infos(rv_cmp_lt(a, b))).info1)::oid) AS lt_sym,
  (SELECT oprname FROM pg_operator
    WHERE oid = ((get_infos(rv_cmp_le(a, b))).info1)::oid) AS le_sym,
  (SELECT oprname FROM pg_operator
    WHERE oid = ((get_infos(rv_cmp_eq(a, b))).info1)::oid) AS eq_sym,
  (SELECT oprname FROM pg_operator
    WHERE oid = ((get_infos(rv_cmp_ne(a, b))).info1)::oid) AS ne_sym,
  (SELECT oprname FROM pg_operator
    WHERE oid = ((get_infos(rv_cmp_ge(a, b))).info1)::oid) AS ge_sym,
  (SELECT oprname FROM pg_operator
    WHERE oid = ((get_infos(rv_cmp_gt(a, b))).info1)::oid) AS gt_sym
FROM e;

-- Composition: rv_cmp_gt(a + b, c) builds a gate_cmp whose left child
-- is a gate_arith over (a, b) and whose right child is c.  This is
-- the shape the planner hook produces from `(a + b) > c` in WHERE.
WITH e AS (
  SELECT provsql.as_random(1) AS a,
         provsql.as_random(2) AS b,
         provsql.as_random(3) AS c
)
SELECT
  get_gate_type(rv_cmp_gt(a + b, c))                      AS root_kind,
  get_gate_type((get_children(rv_cmp_gt(a + b, c)))[1])   AS lhs_kind,
  get_gate_type((get_children(rv_cmp_gt(a + b, c)))[2])   AS rhs_kind,
  (get_infos((get_children(rv_cmp_gt(a + b, c)))[1])).info1 AS lhs_arith_op
FROM e;

-- End-to-end Monte Carlo sanity check.  P(N(0,1) + N(0,1) > 0) = 0.5
-- by symmetry: the helper-built circuit must agree with the
-- hand-built one in continuous_sampler.sql.  100k samples with a
-- pinned seed lands well inside 0.01 of the truth.
SET provsql.monte_carlo_seed = 42;
WITH r AS (
  SELECT provsql.normal(0, 1) AS x, provsql.normal(0, 1) AS y
)
SELECT abs(provsql.probability_evaluate(
             rv_cmp_gt(x + y, 0::random_variable),
             'monte-carlo', '100000') - 0.5) < 0.01 AS sum_of_normals_gt_zero
FROM r;

-- Same expression, mixed shape: P(U(0,1) + U(0,1) > 1) = 0.5 by
-- symmetry (Irwin-Hall on [0,2] is symmetric around 1).  Exercises
-- the rv + rv > literal path (literal is auto-cast to random_variable).
WITH r AS (
  SELECT provsql.uniform(0, 1) AS x, provsql.uniform(0, 1) AS y
)
SELECT abs(provsql.probability_evaluate(
             rv_cmp_gt(x + y, 1::random_variable),
             'monte-carlo', '100000') - 0.5) < 0.01 AS sum_of_uniforms_gt_one
FROM r;
RESET provsql.monte_carlo_seed;

SELECT 'ok'::text AS continuous_arithmetic_done;
