\set ECHO none
\pset format unaligned

-- LogNormal(μ, σ) (§A.2): exp of a Normal(μ, σ), parameterised by the
-- underlying normal.  The multiplicative counterpart of Normal: the
-- family registers a product closure (parameters add in log space), the
-- exp/ln transform bridges to and from Normal, a comparator closed
-- form, closed-form (truncated) moments, and an exact quantile -- so
-- everything below runs with the MC fallback DISABLED, proving the
-- whole surface is analytic.

SET provsql.rv_mc_samples = 0;

-- (1) Closed-form moments: E = e^{μ+σ²/2}, Var = (e^{σ²}-1)e^{2μ+σ²},
-- E[X²] = e^{2μ+2σ²}.
SELECT abs(provsql.expected(provsql.lognormal(0, 1)) - exp(0.5)) < 1e-12
       AS lognormal_mean_exact;
SELECT abs(provsql.variance(provsql.lognormal(0, 1))
           - (exp(1.0) - 1) * exp(1.0)) < 1e-12 AS lognormal_var_exact;
SELECT abs(provsql.moment(provsql.lognormal(0.5, 2), 2)
           - exp(2 * 0.5 + 2 * 4)) < 1e-6 AS lognormal_m2_exact;

-- (2) CDF and support: P(X <= e^μ) = 1/2 (the median is e^μ);
-- P(LogN(0,1) <= e) = Φ(1).
SELECT provsql.probability_evaluate(
         provsql.rv_cmp_le(provsql.lognormal(0, 1), 1::random_variable),
         'independent') = 0.5 AS lognormal_median_mass_exact;
SELECT abs(provsql.probability_evaluate(
             provsql.rv_cmp_le(provsql.lognormal(0, 1),
                               exp(1.0)::random_variable),
             'independent') - 0.8413447460685429) < 1e-12
       AS lognormal_cdf_exact;
SELECT lo, hi FROM support(provsql.lognormal(0, 1));

-- (3) Quantiles: median exactly e^μ; the 97.5% quantile is
-- exp(Φ⁻¹(0.975)) for LogN(0,1).
SELECT abs(provsql.quantile(provsql.lognormal(0, 1), 0.5) - 1) < 1e-12
       AS lognormal_median_exact;
SELECT abs(provsql.quantile(provsql.lognormal(0, 1), 0.975)
           - exp(1.959963984540054)) < 1e-10 AS lognormal_q975_exact;

-- (4) Comparator closed form: P(X < Y) for independent lognormals is
-- the underlying Normal comparison, Φ((μ_Y-μ_X)/√(σ_X²+σ_Y²)).
SELECT abs(provsql.probability_evaluate(
             provsql.rv_cmp_lt(provsql.lognormal(0, 1),
                               provsql.lognormal(1, 1)),
             'independent') - 0.7602499389065233) < 1e-12
       AS lognormal_vs_lognormal_exact;

-- (5) The exp/ln transform bridges: exp(Normal(μ,σ)) IS LogNormal(μ,σ)
-- and ln(LogNormal(μ,σ)) is Normal(μ,σ).  The moment / quantile
-- evaluators consult the transform registry read-only (no rewrite, so
-- no shared-RV identity can be decoupled), exact with MC off...
SELECT abs(provsql.expected(exp(provsql.normal(0, 1))) - exp(0.5)) < 1e-12
       AS exp_normal_bridge_exact;
SELECT provsql.expected(ln(provsql.lognormal(2, 3))) = 2
       AS ln_lognormal_bridge_exact;
SELECT abs(provsql.quantile(exp(provsql.normal(0, 1)), 0.5) - 1) < 1e-12
       AS exp_normal_quantile_exact;
-- ...while the probability path runs the full simplifier, where chains
-- fold bottom-up: exp(N(0,1) + N(0,1)) = LogN(0, √2), whose mass below
-- 1 is exactly 1/2.
SELECT provsql.probability_evaluate(
         provsql.rv_cmp_le(exp(provsql.normal(0, 1) + provsql.normal(0, 1)),
                           1::random_variable),
         'independent') = 0.5 AS exp_normal_sum_chain_exact;

-- (6) Product closure: independent lognormals multiply to a lognormal
-- (parameters add in log space): LogN(0,1) · LogN(1,2) = LogN(1, √5),
-- mean e^{1+5/2} = e^3.5.
SELECT abs(provsql.expected(provsql.lognormal(0, 1) * provsql.lognormal(1, 2))
           - exp(3.5)) < 1e-12 AS lognormal_product_exact;
-- Positive scalars fold through affine: 2·LogN(0,1) = LogN(ln 2, 1).
SELECT abs(provsql.expected(2 * provsql.lognormal(0, 1)) - 2 * exp(0.5))
       < 1e-12 AS lognormal_scale_exact;
-- Scalar factors inside an n-ary product fold too: 3·(X·Y).
SELECT abs(provsql.expected(3 * (provsql.lognormal(0, 1)
                                 * provsql.lognormal(1, 2)))
           - 3 * exp(3.5)) < 1e-12 AS lognormal_scaled_product_exact;
-- Quantiles of a product go through the same closed-form image: the
-- median of LogN(1, √5) is e; and its variance is exact through the
-- disjoint-product decomposition, (e⁵-1)e⁷.
SELECT abs(provsql.quantile(provsql.lognormal(0, 1) * provsql.lognormal(1, 2),
                            0.5) - exp(1.0)) < 1e-12
       AS lognormal_product_median_exact;
SELECT abs(provsql.variance(provsql.lognormal(0, 1) * provsql.lognormal(1, 2))
           - (exp(5.0) - 1) * exp(7.0)) < 1e-6
       AS lognormal_product_var_exact;

-- (7) Conditioning: closed-form truncated moments in log space.
-- E[LogN(0,1) | X > 1] = e^{1/2}·Φ(1)/(1 - Φ(0)) ≈ 2.7742859576700096.
WITH r AS (SELECT provsql.lognormal(0, 1) AS x)
SELECT abs(provsql.expected(x | (x > 1)) - 2.7742859576700096) < 1e-12
       AS lognormal_truncated_mean_exact
  FROM r;

RESET provsql.rv_mc_samples;

-- (8) The sampler draws real lognormals (seeded MC agreement on a
-- statistic with no closed form registered: the mean of a lognormal
-- times an independent uniform).
SET provsql.monte_carlo_seed = 42;
SELECT abs(provsql.expected(provsql.lognormal(0, 1) * provsql.uniform(0, 1))
           - exp(0.5) / 2) < 0.05 AS lognormal_mixed_product_mc;
RESET provsql.monte_carlo_seed;

-- (9) Constructor validation; σ=0 routes through as_random (a Dirac at
-- e^μ, sharing the constant's gate).
\set VERBOSITY terse
SELECT provsql.lognormal('NaN'::double precision, 1);
SELECT provsql.lognormal(0, -1);
\set VERBOSITY default
SELECT (provsql.lognormal(1, 0))::uuid = (provsql.as_random(exp(1.0)))::uuid
       AS lognormal_sigma0_dedups;

-- (10) The family registry lists it (Studio renders it with no client
-- change).
SELECT name, nparams, param_names, label
  FROM provsql.rv_families() WHERE name = 'lognormal';

SELECT 'ok'::text AS continuous_lognormal_done;
