\set ECHO none
\pset format unaligned

-- Inverse-gamma(α, β) and inverse-Gaussian/Wald(μ, λ): the reciprocal of
-- a gamma (conjugate prior for a Gaussian variance) and the Brownian
-- first-passage time.  Both CDFs are closed form -- everything above the
-- RESET runs with the Monte-Carlo fallback disabled.

SET provsql.rv_mc_samples = 0;

-- ═══════════ Inverse gamma ═══════════

-- (1) Moments: E[IΓ(3,2)] = β/(α-1) = 1, Var = β²/((α-1)²(α-2)) = 1,
-- E[X²] = β²/((α-1)(α-2)) = 2.
SELECT provsql.expected(provsql.inverse_gamma(3, 2)) = 1 AS igamma_mean_exact;
SELECT provsql.variance(provsql.inverse_gamma(3, 2)) = 1 AS igamma_var_exact;
SELECT provsql.moment(provsql.inverse_gamma(3, 2), 2) = 2 AS igamma_m2_exact;

-- (2) Divergent moments reported honestly as Infinity, not estimated
-- (the mean for α ≤ 1, the variance for α ≤ 2).
SELECT provsql.expected(provsql.inverse_gamma(1, 2)) = 'Infinity'
       AS igamma_mean_divergent;
SELECT provsql.variance(provsql.inverse_gamma(2, 2)) = 'Infinity'
       AS igamma_var_divergent;

-- (3) CDF through the upper incomplete gamma: IΓ(1,1) has F(x) = e^{-1/x},
-- so P(X ≤ 1) = 1/e, exactly, and the numeric quantile inverts it.
SELECT abs(provsql.probability_evaluate(
             provsql.rv_cmp_le(provsql.inverse_gamma(1, 1), 1::random_variable),
             'independent') - exp(-1.0)) < 1e-12
       AS igamma_cdf_exact;
SELECT abs(provsql.quantile(provsql.inverse_gamma(1, 1), exp(-1.0)) - 1)
       < 1e-9 AS igamma_quantile_numeric;
SELECT lo, hi FROM support(provsql.inverse_gamma(3, 2));

-- (4) Positive scaling rescales β: 2·IΓ(3,2) = IΓ(3,4), mean 4/2 = 2,
-- exact through the affine fold.
SELECT provsql.expected(2 * provsql.inverse_gamma(3, 2)) = 2
       AS igamma_scaled_mean_exact;

-- ═══════════ Inverse Gaussian (Wald) ═══════════

-- (5) Moments: E[IG(2,6)] = μ = 2, Var = μ³/λ = 4/3,
-- E[X²] = μ² + μ³/λ = 16/3.
SELECT provsql.expected(provsql.inverse_gaussian(2, 6)) = 2
       AS iwald_mean_exact;
SELECT abs(provsql.variance(provsql.inverse_gaussian(2, 6)) - 4.0 / 3) < 1e-12
       AS iwald_var_exact;
SELECT abs(provsql.moment(provsql.inverse_gaussian(2, 6), 2) - 16.0 / 3)
       < 1e-12 AS iwald_m2_exact;

-- (6) Closed-form CDF in Φ: P(IG(1,1) ≤ 1) = Φ(0) + e²Φ(-2)
-- ≈ 0.6681020012231706.
SELECT abs(provsql.probability_evaluate(
             provsql.rv_cmp_le(provsql.inverse_gaussian(1, 1), 1::random_variable),
             'independent') - 0.6681020012231706) < 1e-12
       AS iwald_cdf_exact;
SELECT lo, hi FROM support(provsql.inverse_gaussian(2, 6));

-- (7) wald is an alias of inverse_gaussian.
SELECT provsql.expected(provsql.wald(2, 6)) = 2 AS wald_alias_mean_exact;
SELECT provsql.get_extra((provsql.wald(2, 6))::uuid) = 'inverse_gaussian:2,6'
       AS wald_alias_encoding;

-- (8) Same-ratio sum closure: IG(1,2) + IG(2,8) both have λ/μ² = 2, so
-- the sum folds to IG(3, 18) in the simplifier; its mean is 3 and its
-- CDF matches the folded single-RV CDF exactly (no MC noise).
SELECT provsql.expected(
         provsql.inverse_gaussian(1, 2) + provsql.inverse_gaussian(2, 8)) = 3
       AS iwald_sum_closure_mean;
WITH s AS (SELECT provsql.inverse_gaussian(1, 2)
                + provsql.inverse_gaussian(2, 8) AS x)
SELECT abs(provsql.probability_evaluate(
             provsql.rv_cmp_le(x, 3::random_variable), 'independent')
           - provsql.probability_evaluate(
               provsql.rv_cmp_le(provsql.inverse_gaussian(3, 18),
                                 3::random_variable), 'independent')) < 1e-12
       AS iwald_sum_closure_cdf
  FROM s;

-- (9) Positive scaling: 3·IG(2,6) = IG(6,18), mean 6.
SELECT provsql.expected(3 * provsql.inverse_gaussian(2, 6)) = 6
       AS iwald_scaled_mean_exact;

RESET provsql.rv_mc_samples;

-- (10) The samplers draw the real distributions (seeded MC agreement on
-- the mean of a product, for which no closed form is registered).
SET provsql.monte_carlo_seed = 42;
SELECT abs(provsql.expected(provsql.inverse_gamma(4, 3) * provsql.uniform(0, 1))
           - 0.5) < 0.03 AS igamma_sampler_mc;
SELECT abs(provsql.expected(provsql.inverse_gaussian(2, 6) * provsql.uniform(0, 1))
           - 1.0) < 0.03 AS iwald_sampler_mc;
RESET provsql.monte_carlo_seed;

-- (11) End-to-end through the planner hook: WHERE rv <= c resolves to the
-- exact closed-form CDF answer.
CREATE TABLE inverse_sensors(id text, x provsql.random_variable);
INSERT INTO inverse_sensors VALUES
  ('a', provsql.inverse_gamma(1, 1)),
  ('b', provsql.inverse_gaussian(1, 1));
SELECT add_provenance('inverse_sensors');
CREATE TABLE inverse_result AS
  SELECT id, probability_evaluate(provenance(), 'independent') AS p
    FROM inverse_sensors WHERE x <= 1;
SELECT remove_provenance('inverse_result');
SELECT id,
       abs(p - CASE id WHEN 'a' THEN exp(-1.0)
                       WHEN 'b' THEN 0.6681020012231706 END) < 1e-12
       AS within_tolerance
  FROM inverse_result ORDER BY id;
DROP TABLE inverse_result;
DROP TABLE inverse_sensors;

-- (12) Constructor validation.
\set VERBOSITY terse
SELECT provsql.inverse_gamma(0, 1);
SELECT provsql.inverse_gamma(1, 'Infinity'::double precision);
SELECT provsql.inverse_gaussian(-1, 2);
SELECT provsql.inverse_gaussian(1, 0);
SELECT provsql.wald(2, -1);
\set VERBOSITY default

-- (13) Both families are in the registry (Studio renders them with no
-- client change).
SELECT name, nparams, param_names, label
  FROM provsql.rv_families()
 WHERE name IN ('inverse_gamma', 'inverse_gaussian')
 ORDER BY name;

SELECT 'ok'::text AS continuous_inverse_done;
