\pset tuples_only on
\set resolution 9
\set coverage_size 2
\set raster_size 25
\set pixel_size 0.0005
\set value_num 5
\set lat 51.5
\set lng -0.025

CREATE TABLE h3_test_rasters (id SERIAL, rast raster);

INSERT INTO h3_test_rasters (rast) (
    WITH
        vals AS (
            SELECT array_agg(row) AS vals
            FROM (
                SELECT array_agg((x + y) % :value_num + 1) AS row
                FROM
                    generate_series(1, :raster_size) AS x,
                    generate_series(1, :raster_size) AS y
                GROUP BY y
            ) t),
        rasts AS (
            SELECT
                ST_AddBand(
                    ST_MakeEmptyCoverage(
                        :raster_size, :raster_size,
                        :raster_size * :coverage_size, :raster_size * :coverage_size,
                        :lng, :lat,
                        :pixel_size, -(:pixel_size),
                        0, 0,
                        4326),
                    ARRAY[ROW(1, '8BUI', 1, 0)]::addbandarg[]
                ) AS rast)
    SELECT ST_SetValues(r.rast, 1, 1, 1, v.vals)
    FROM rasts r, vals v
);

CREATE FUNCTION h3_test_equal(
    v1 double precision,
    v2 double precision)
RETURNS boolean
AS $$
    SELECT ABS(v1 - v2) < 1e-12;
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;

CREATE FUNCTION h3_test_raster_summary_stats_equal(
    s1 h3_raster_summary_stats,
    s2 h3_raster_summary_stats)
RETURNS boolean
AS $$
    SELECT s1 IS NOT NULL AND s2 IS NOT NULL
        AND h3_test_equal((s1).count, (s2).count)
        AND h3_test_equal((s1).sum, (s2).sum)
        AND h3_test_equal((s1).mean, (s2).mean)
        AND h3_test_equal((s1).stddev, (s2).stddev)
        AND h3_test_equal((s1).min, (s2).min)
        AND h3_test_equal((s1).max, (s2).max);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;

CREATE FUNCTION h3_test_raster_class_summary_item_equal(
    i1 h3_raster_class_summary_item,
    i2 h3_raster_class_summary_item)
RETURNS boolean
AS $$
    SELECT i1 IS NOT NULL AND i2 IS NOT NULL
        AND h3_test_equal((i1).val, (i2).val)
        AND h3_test_equal((i1).count, (i2).count)
        AND h3_test_equal((i1).area, (i2).area);
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;

-- Results of `h3_raster_summary_clip` and `h3_raster_summary_centroids`
-- should be identical
WITH
    clip AS (
        SELECT
            h3,
            h3_raster_summary_stats_agg(stats) AS stats
        FROM (
            -- h3, stats
            SELECT (h3_raster_summary_clip(rast, :resolution)).*
            FROM h3_test_rasters
        ) t
        GROUP BY 1),
    centroids AS (
        SELECT
            h3,
            h3_raster_summary_stats_agg(stats) AS stats
        FROM (
            -- h3, stats
            SELECT (h3_raster_summary_centroids(rast, :resolution)).*
            FROM h3_test_rasters
        ) t
        GROUP BY 1)
SELECT COUNT(*)
FROM clip a FULL OUTER JOIN centroids b ON a.h3 = b.h3
WHERE NOT h3_test_raster_summary_stats_equal(a.stats, b.stats);

-- Results of `h3_raster_class_summary_clip` and `h3_raster_class_summary_clip`
-- should be identical
WITH
    clip AS (
        SELECT
            h3,
            val,
            h3_raster_class_summary_item_agg(summary) AS summary
        FROM (
            -- h3, val, summary
            SELECT (h3_raster_class_summary_clip(rast, :resolution)).*
            FROM h3_test_rasters
        ) t
        GROUP BY 1, 2),
    centroids AS (
        SELECT
            h3,
            val,
            h3_raster_class_summary_item_agg(summary) AS summary
        FROM (
            -- h3, val, summary
            SELECT (h3_raster_class_summary_centroids(rast, :resolution)).*
            FROM h3_test_rasters
        ) t
        GROUP BY 1, 2)
SELECT COUNT(*)
FROM clip a FULL OUTER JOIN centroids b ON a.h3 = b.h3 AND a.val = b.val
WHERE NOT h3_test_raster_class_summary_item_equal(a.summary, b.summary);

-- Stats aggregation check:
-- stats for a cell intersecting multiple rasters (with aggregation) should be
-- the same when calculated on a union of rasters (without aggregation).
WITH
    rast AS (
        -- Union all test rasters
        SELECT ST_Union(rast) AS rast FROM h3_test_rasters),
    middle AS (
        -- Find an H3 cell in a bottom-right corner of a first raster
        -- (intersecting 4 rasters)
        SELECT
            h3_lat_lng_to_cell(
                ST_MakePoint(
                    ST_RasterToWorldCoordX(rast, :raster_size),
                    ST_RasterToWorldCoordY(rast, :raster_size)),
                :resolution
            ) AS h3
        FROM rast),
    summary1 AS (
        -- Get summary from combined raster
        SELECT t.stats
        FROM (
            -- h3, stats
            SELECT (h3_raster_summary_clip(rast, :resolution)).*
            FROM rast
        ) t, middle m
        WHERE t.h3 = m.h3),
    summary2 AS (
        -- Get aggregates summary from separate rasters
        SELECT h3_raster_summary_stats_agg(t.stats) AS stats
        FROM (
            -- h3, stats
            SELECT (h3_raster_summary_clip(rast, :resolution)).*
            FROM h3_test_rasters
        ) t, middle m
        WHERE t.h3 = m.h3
        GROUP BY t.h3)
SELECT h3_test_raster_summary_stats_equal(s1.stats, s2.stats)
FROM summary1 s1, summary2 s2;

DROP FUNCTION h3_test_raster_class_summary_item_equal(
    h3_raster_class_summary_item,
    h3_raster_class_summary_item);
DROP FUNCTION h3_test_raster_summary_stats_equal(
    h3_raster_summary_stats,
    h3_raster_summary_stats);
DROP FUNCTION h3_test_equal(double precision, double precision);

DROP TABLE h3_test_rasters;
