SET LOCAL search_path TO @extschema@;


------------------------------------------------------------
-- dummy input/output functions for dummy index key types --
------------------------------------------------------------

CREATE OR REPLACE FUNCTION ekey_point_in_dummy(cstring)
  RETURNS ekey_point
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_notimpl';

CREATE OR REPLACE FUNCTION ekey_point_out_dummy(ekey_point)
  RETURNS cstring
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_notimpl';

CREATE OR REPLACE FUNCTION ekey_area_in_dummy(cstring)
  RETURNS ekey_area
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_notimpl';

CREATE OR REPLACE FUNCTION ekey_area_out_dummy(ekey_area)
  RETURNS cstring
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_notimpl';


--------------------------
-- text input functions --
--------------------------

CREATE OR REPLACE FUNCTION epoint_in(cstring)
  RETURNS epoint
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_in';

CREATE OR REPLACE FUNCTION epoint_with_sample_count_in(cstring)
  RETURNS epoint_with_sample_count
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_with_sample_count_in';

CREATE OR REPLACE FUNCTION ebox_in(cstring)
  RETURNS ebox
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_in';

CREATE OR REPLACE FUNCTION ecircle_in(cstring)
  RETURNS ecircle
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_in';

CREATE OR REPLACE FUNCTION ecluster_in(cstring)
  RETURNS ecluster
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecluster_in';


---------------------------
-- text output functions --
---------------------------

CREATE OR REPLACE FUNCTION epoint_out(epoint)
  RETURNS cstring
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_out';

CREATE OR REPLACE FUNCTION epoint_with_sample_count_out(epoint_with_sample_count)
  RETURNS cstring
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_with_sample_count_out';

CREATE OR REPLACE FUNCTION ebox_out(ebox)
  RETURNS cstring
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_out';

CREATE OR REPLACE FUNCTION ecircle_out(ecircle)
  RETURNS cstring
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_out';

CREATE OR REPLACE FUNCTION ecluster_out(ecluster)
  RETURNS cstring
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecluster_out';


--------------------------
-- binary I/O functions --
--------------------------

CREATE OR REPLACE FUNCTION epoint_recv(internal)
  RETURNS epoint
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_recv';

CREATE OR REPLACE FUNCTION ebox_recv(internal)
  RETURNS ebox
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_recv';

CREATE OR REPLACE FUNCTION ecircle_recv(internal)
  RETURNS ecircle
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_recv';

CREATE OR REPLACE FUNCTION epoint_send(epoint)
  RETURNS bytea
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_send';

CREATE OR REPLACE FUNCTION ebox_send(ebox)
  RETURNS bytea
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_send';

CREATE OR REPLACE FUNCTION ecircle_send(ecircle)
  RETURNS bytea
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_send';


--------------------
-- B-tree support --
--------------------

-- begin of B-tree support for epoint

CREATE OR REPLACE FUNCTION epoint_btree_lt(epoint, epoint)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_lt';

CREATE OR REPLACE FUNCTION epoint_btree_le(epoint, epoint)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_le';

CREATE OR REPLACE FUNCTION epoint_btree_eq(epoint, epoint)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_eq';

CREATE OR REPLACE FUNCTION epoint_btree_ne(epoint, epoint)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_ne';

CREATE OR REPLACE FUNCTION epoint_btree_ge(epoint, epoint)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_ge';

CREATE OR REPLACE FUNCTION epoint_btree_gt(epoint, epoint)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_gt';

CREATE OR REPLACE FUNCTION epoint_btree_cmp(epoint, epoint)
  RETURNS int4
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_epoint_cmp';

-- end of B-tree support for epoint

-- begin of B-tree support for ebox

CREATE OR REPLACE FUNCTION ebox_btree_lt(ebox, ebox)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_lt';

CREATE OR REPLACE FUNCTION ebox_btree_le(ebox, ebox)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_le';

CREATE OR REPLACE FUNCTION ebox_btree_eq(ebox, ebox)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_eq';

CREATE OR REPLACE FUNCTION ebox_btree_ne(ebox, ebox)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_ne';

CREATE OR REPLACE FUNCTION ebox_btree_ge(ebox, ebox)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_ge';

CREATE OR REPLACE FUNCTION ebox_btree_gt(ebox, ebox)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_gt';

CREATE OR REPLACE FUNCTION ebox_btree_cmp(ebox, ebox)
  RETURNS int4
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ebox_cmp';

-- end of B-tree support for ebox

-- begin of B-tree support for ecircle

CREATE OR REPLACE FUNCTION ecircle_btree_lt(ecircle, ecircle)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_lt';

CREATE OR REPLACE FUNCTION ecircle_btree_le(ecircle, ecircle)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_le';

CREATE OR REPLACE FUNCTION ecircle_btree_eq(ecircle, ecircle)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_eq';

CREATE OR REPLACE FUNCTION ecircle_btree_ne(ecircle, ecircle)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_ne';

CREATE OR REPLACE FUNCTION ecircle_btree_ge(ecircle, ecircle)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_ge';

CREATE OR REPLACE FUNCTION ecircle_btree_gt(ecircle, ecircle)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_gt';

CREATE OR REPLACE FUNCTION ecircle_btree_cmp(ecircle, ecircle)
  RETURNS int4
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_btree_ecircle_cmp';

-- end of B-tree support for ecircle


----------------
-- type casts --
----------------

CREATE OR REPLACE FUNCTION cast_epoint_to_ebox(epoint)
  RETURNS ebox
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_to_ebox';

CREATE OR REPLACE FUNCTION cast_epoint_to_ecircle(epoint)
  RETURNS ecircle
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_to_ecircle';

CREATE OR REPLACE FUNCTION cast_epoint_to_ecluster(epoint)
  RETURNS ecluster
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_to_ecluster';

CREATE OR REPLACE FUNCTION cast_ebox_to_ecluster(ebox)
  RETURNS ecluster
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_to_ecluster';


---------------------------
-- constructor functions --
---------------------------

CREATE OR REPLACE FUNCTION epoint(float8, float8)
  RETURNS epoint
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_create_epoint';

CREATE OR REPLACE FUNCTION epoint_latlon(float8, float8)
  RETURNS epoint
  LANGUAGE SQL IMMUTABLE STRICT AS $$
    SELECT @extschema@.epoint($1, $2)
  $$;

CREATE OR REPLACE FUNCTION epoint_lonlat(float8, float8)
  RETURNS epoint
  LANGUAGE SQL IMMUTABLE STRICT AS $$
    SELECT @extschema@.epoint($2, $1)
  $$;

CREATE OR REPLACE FUNCTION epoint_with_sample_count(epoint, int4)
  RETURNS epoint_with_sample_count
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_create_epoint_with_sample_count';

CREATE OR REPLACE FUNCTION empty_ebox()
  RETURNS ebox
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_create_empty_ebox';

CREATE OR REPLACE FUNCTION ebox(float8, float8, float8, float8)
  RETURNS ebox
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_create_ebox';

CREATE OR REPLACE FUNCTION ebox(epoint, epoint)
  RETURNS ebox
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_create_ebox_from_epoints';

CREATE OR REPLACE FUNCTION ecircle(float8, float8, float8)
  RETURNS ecircle
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_create_ecircle';

CREATE OR REPLACE FUNCTION ecircle(epoint, float8)
  RETURNS ecircle
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_create_ecircle_from_epoint';

CREATE OR REPLACE FUNCTION ecluster_concat(ecluster[])
  RETURNS ecluster
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT pg_catalog.array_to_string($1, ' ')::@extschema@.ecluster
  $$;

CREATE OR REPLACE FUNCTION ecluster_concat(ecluster, ecluster)
  RETURNS ecluster
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT (
      $1::pg_catalog.text OPERATOR(pg_catalog.||) ' ' OPERATOR(pg_catalog.||)
      $2::pg_catalog.text
    )::@extschema@.ecluster
  $$;

CREATE OR REPLACE FUNCTION ecluster_create_multipoint(epoint[])
  RETURNS ecluster
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT
      pg_catalog.array_to_string(
        pg_catalog.array_agg(
          'point (' OPERATOR(pg_catalog.||) unnest OPERATOR(pg_catalog.||) ')'
        ),
        ' '
      )::@extschema@.ecluster
    FROM pg_catalog.unnest($1)
  $$;

CREATE OR REPLACE FUNCTION ecluster_create_path(epoint[])
  RETURNS ecluster
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT CASE WHEN "str" OPERATOR(pg_catalog.=) '' THEN
      'empty'::@extschema@.ecluster
    ELSE
      (
        'path (' OPERATOR(pg_catalog.||) "str" OPERATOR(pg_catalog.||) ')'
      )::@extschema@.ecluster
    END
    FROM pg_catalog.array_to_string($1, ' ') AS "str"
  $$;

CREATE OR REPLACE FUNCTION ecluster_create_outline(epoint[])
  RETURNS ecluster
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT CASE WHEN "str" OPERATOR(pg_catalog.=) '' THEN
      'empty'::@extschema@.ecluster
    ELSE
      (
        'outline (' OPERATOR(pg_catalog.||) "str" OPERATOR(pg_catalog.||) ')'
      )::@extschema@.ecluster
    END
    FROM pg_catalog.array_to_string($1, ' ') AS "str"
  $$;

CREATE OR REPLACE FUNCTION ecluster_create_polygon(epoint[])
  RETURNS ecluster
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT CASE WHEN "str" OPERATOR(pg_catalog.=) '' THEN
      'empty'::@extschema@.ecluster
    ELSE
      (
        'polygon (' OPERATOR(pg_catalog.||) pg_catalog.array_to_string($1, ' ')
        OPERATOR(pg_catalog.||) ')'
      )::@extschema@.ecluster
    END
    FROM pg_catalog.array_to_string($1, ' ') AS "str"
  $$;


----------------------
-- getter functions --
----------------------

CREATE OR REPLACE FUNCTION latitude(epoint)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_lat';

CREATE OR REPLACE FUNCTION longitude(epoint)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_lon';

CREATE OR REPLACE FUNCTION min_latitude(ebox)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_lat_min';

CREATE OR REPLACE FUNCTION max_latitude(ebox)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_lat_max';

CREATE OR REPLACE FUNCTION min_longitude(ebox)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_lon_min';

CREATE OR REPLACE FUNCTION max_longitude(ebox)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_lon_max';

CREATE OR REPLACE FUNCTION center(ecircle)
  RETURNS epoint
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_center';

CREATE OR REPLACE FUNCTION radius(ecircle)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_radius';

CREATE OR REPLACE FUNCTION ecluster_extract_points(ecluster)
  RETURNS SETOF epoint
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT "match"[2]::@extschema@.epoint
    FROM pg_catalog.regexp_matches(
      $1::pg_catalog.text, e'(^| )point \\(([^)]+)\\)', 'g'
    ) AS "match"
  $$;

CREATE OR REPLACE FUNCTION ecluster_extract_paths(ecluster)
  RETURNS SETOF epoint[]
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT (
      SELECT pg_catalog.array_agg("m2"[1]::@extschema@.epoint)
      FROM pg_catalog.regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
    )
    FROM pg_catalog.regexp_matches(
      $1::pg_catalog.text, e'(^| )path \\(([^)]+)\\)', 'g'
    ) AS "m1"
  $$;

CREATE OR REPLACE FUNCTION ecluster_extract_outlines(ecluster)
  RETURNS SETOF epoint[]
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT (
      SELECT pg_catalog.array_agg("m2"[1]::@extschema@.epoint)
      FROM pg_catalog.regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
    )
    FROM pg_catalog.regexp_matches(
      $1::pg_catalog.text, e'(^| )outline \\(([^)]+)\\)', 'g'
    ) AS "m1"
  $$;

CREATE OR REPLACE FUNCTION ecluster_extract_polygons(ecluster)
  RETURNS SETOF epoint[]
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT (
      SELECT pg_catalog.array_agg("m2"[1]::@extschema@.epoint)
      FROM pg_catalog.regexp_matches("m1"[2], e'[^ ]+ [^ ]+', 'g') AS "m2"
    )
    FROM pg_catalog.regexp_matches(
      $1::pg_catalog.text, e'(^| )polygon \\(([^)]+)\\)', 'g'
    ) AS "m1"
  $$;


---------------
-- operators --
---------------

CREATE OR REPLACE FUNCTION epoint_ebox_overlap_proc(epoint, ebox)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_ebox_overlap';

CREATE OR REPLACE FUNCTION epoint_ecircle_overlap_proc(epoint, ecircle)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_ecircle_overlap';

CREATE OR REPLACE FUNCTION epoint_ecluster_overlap_proc(epoint, ecluster)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_ecluster_overlap';

CREATE OR REPLACE FUNCTION epoint_ecluster_may_overlap_proc(epoint, ecluster)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_ecluster_may_overlap';

CREATE OR REPLACE FUNCTION ebox_overlap_proc(ebox, ebox)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_overlap';

CREATE OR REPLACE FUNCTION ebox_ecircle_may_overlap_proc(ebox, ecircle)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_ecircle_may_overlap';

CREATE OR REPLACE FUNCTION ebox_ecluster_may_overlap_proc(ebox, ecluster)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ebox_ecluster_may_overlap';

CREATE OR REPLACE FUNCTION ecircle_overlap_proc(ecircle, ecircle)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_overlap';

CREATE OR REPLACE FUNCTION ecircle_ecluster_overlap_proc(ecircle, ecluster)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_ecluster_overlap';

CREATE OR REPLACE FUNCTION ecircle_ecluster_may_overlap_proc(ecircle, ecluster)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_ecluster_may_overlap';

CREATE OR REPLACE FUNCTION ecluster_overlap_proc(ecluster, ecluster)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecluster_overlap';

CREATE OR REPLACE FUNCTION ecluster_may_overlap_proc(ecluster, ecluster)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecluster_may_overlap';

CREATE OR REPLACE FUNCTION ecluster_contains_proc(ecluster, ecluster)
  RETURNS boolean
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecluster_contains';

CREATE OR REPLACE FUNCTION epoint_distance_proc(epoint, epoint)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_distance';

CREATE OR REPLACE FUNCTION epoint_ecircle_distance_proc(epoint, ecircle)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_ecircle_distance';

CREATE OR REPLACE FUNCTION epoint_ecluster_distance_proc(epoint, ecluster)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_epoint_ecluster_distance';

CREATE OR REPLACE FUNCTION ecircle_distance_proc(ecircle, ecircle)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_distance';

CREATE OR REPLACE FUNCTION ecircle_ecluster_distance_proc(ecircle, ecluster)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecircle_ecluster_distance';

CREATE OR REPLACE FUNCTION ecluster_distance_proc(ecluster, ecluster)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecluster_distance';

CREATE OR REPLACE FUNCTION fair_distance_operator_proc(ecluster, epoint_with_sample_count)
  RETURNS float8
  LANGUAGE C IMMUTABLE STRICT
  AS '$libdir/latlon-v0010', 'pgl_ecluster_epoint_sc_fair_distance';

CREATE OR REPLACE FUNCTION epoint_ebox_overlap_commutator(ebox, epoint)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.&&) $1';

CREATE OR REPLACE FUNCTION epoint_ecircle_overlap_commutator(ecircle, epoint)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.&&) $1';

CREATE OR REPLACE FUNCTION epoint_ecluster_overlap_commutator(ecluster, epoint)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.&&) $1';

CREATE OR REPLACE FUNCTION ecircle_ecluster_overlap_commutator(ecluster, ecircle)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.&&) $1';

CREATE OR REPLACE FUNCTION ebox_ecircle_overlap_castwrap(ebox, ecircle)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1::@extschema@.ecluster OPERATOR(@extschema@.&&) $2';

CREATE OR REPLACE FUNCTION ebox_ecircle_overlap_castwrap(ecircle, ebox)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1 OPERATOR(@extschema@.&&) $2::@extschema@.ecluster';

CREATE OR REPLACE FUNCTION ebox_ecluster_overlap_castwrap(ebox, ecluster)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1::@extschema@.ecluster OPERATOR(@extschema@.&&) $2';

CREATE OR REPLACE FUNCTION ebox_ecluster_overlap_castwrap(ecluster, ebox)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1 OPERATOR(@extschema@.&&) $2::@extschema@.ecluster';

CREATE OR REPLACE FUNCTION epoint_ecluster_may_overlap_commutator(ecluster, epoint)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.&&+) $1';

CREATE OR REPLACE FUNCTION ebox_ecircle_may_overlap_commutator(ecircle, ebox)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.&&+) $1';

CREATE OR REPLACE FUNCTION ebox_ecluster_may_overlap_commutator(ecluster, ebox)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.&&+) $1';

CREATE OR REPLACE FUNCTION ecircle_ecluster_may_overlap_commutator(ecluster, ecircle)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.&&+) $1';

CREATE OR REPLACE FUNCTION ecluster_contains_commutator(ecluster, ecluster)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.@>) $1';

CREATE OR REPLACE FUNCTION ebox_contains_castwrap(ebox, ebox)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS $$
    SELECT
    $1::@extschema@.ecluster OPERATOR(@extschema@.@>) $2::@extschema@.ecluster
  $$;

CREATE OR REPLACE FUNCTION ebox_contains_swapped_castwrap(ebox, ebox)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE AS $$
    SELECT
    $2::@extschema@.ecluster OPERATOR(@extschema@.@>) $1::@extschema@.ecluster
  $$;

CREATE OR REPLACE FUNCTION ebox_ecluster_contains_castwrap(ebox, ecluster)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1::@extschema@.ecluster OPERATOR(@extschema@.@>) $2';

CREATE OR REPLACE FUNCTION ebox_ecluster_contains_castwrap(ecluster, ebox)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $2::@extschema@.ecluster OPERATOR(@extschema@.@>) $1';

CREATE OR REPLACE FUNCTION ecluster_ebox_contains_castwrap(ecluster, ebox)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1 OPERATOR(@extschema@.@>) $2::@extschema@.ecluster';

CREATE OR REPLACE FUNCTION ecluster_ebox_contains_castwrap(ebox, ecluster)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $2 OPERATOR(@extschema@.@>) $1::@extschema@.ecluster';

CREATE OR REPLACE FUNCTION epoint_ecircle_distance_commutator(ecircle, epoint)
  RETURNS float8
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.<->) $1';

CREATE OR REPLACE FUNCTION epoint_ecluster_distance_commutator(ecluster, epoint)
  RETURNS float8
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.<->) $1';

CREATE OR REPLACE FUNCTION ecircle_ecluster_distance_commutator(ecluster, ecircle)
  RETURNS float8
  LANGUAGE sql IMMUTABLE AS 'SELECT $2 OPERATOR(@extschema@.<->) $1';

CREATE OR REPLACE FUNCTION epoint_ebox_distance_castwrap(epoint, ebox)
  RETURNS float8
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1 OPERATOR(@extschema@.<->) $2::@extschema@.ecluster';

CREATE OR REPLACE FUNCTION epoint_ebox_distance_castwrap(ebox, epoint)
  RETURNS float8
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1::@extschema@.ecluster OPERATOR(@extschema@.<->) $2';

CREATE OR REPLACE FUNCTION ebox_distance_castwrap(ebox, ebox)
  RETURNS float8
  LANGUAGE sql IMMUTABLE AS $$
    SELECT
    $1::@extschema@.ecluster OPERATOR(@extschema@.<->) $2::@extschema@.ecluster
  $$;

CREATE OR REPLACE FUNCTION ebox_ecircle_distance_castwrap(ebox, ecircle)
  RETURNS float8
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1::@extschema@.ecluster OPERATOR(@extschema@.<->) $2';

CREATE OR REPLACE FUNCTION ebox_ecircle_distance_castwrap(ecircle, ebox)
  RETURNS float8
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1 OPERATOR(@extschema@.<->) $2::@extschema@.ecluster';

CREATE OR REPLACE FUNCTION ebox_ecluster_distance_castwrap(ebox, ecluster)
  RETURNS float8
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1::@extschema@.ecluster OPERATOR(@extschema@.<->) $2';

CREATE OR REPLACE FUNCTION ebox_ecluster_distance_castwrap(ecluster, ebox)
  RETURNS float8
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1 OPERATOR(@extschema@.<->) $2::@extschema@.ecluster';


----------------
-- GiST index --
----------------

CREATE OR REPLACE FUNCTION pgl_gist_consistent(internal, internal, smallint, oid, internal)
  RETURNS boolean
  LANGUAGE C STRICT
  AS '$libdir/latlon-v0010', 'pgl_gist_consistent';

CREATE OR REPLACE FUNCTION pgl_gist_union(internal, internal)
  RETURNS internal
  LANGUAGE C STRICT
  AS '$libdir/latlon-v0010', 'pgl_gist_union';

CREATE OR REPLACE FUNCTION pgl_gist_compress_epoint(internal)
  RETURNS internal
  LANGUAGE C STRICT
  AS '$libdir/latlon-v0010', 'pgl_gist_compress_epoint';

CREATE OR REPLACE FUNCTION pgl_gist_compress_ecircle(internal)
  RETURNS internal
  LANGUAGE C STRICT
  AS '$libdir/latlon-v0010', 'pgl_gist_compress_ecircle';

CREATE OR REPLACE FUNCTION pgl_gist_compress_ecluster(internal)
  RETURNS internal
  LANGUAGE C STRICT
  AS '$libdir/latlon-v0010', 'pgl_gist_compress_ecluster';

CREATE OR REPLACE FUNCTION pgl_gist_decompress(internal)
  RETURNS internal
  LANGUAGE C STRICT
  AS '$libdir/latlon-v0010', 'pgl_gist_decompress';

CREATE OR REPLACE FUNCTION pgl_gist_penalty(internal, internal, internal)
  RETURNS internal
  LANGUAGE C STRICT
  AS '$libdir/latlon-v0010', 'pgl_gist_penalty';

CREATE OR REPLACE FUNCTION pgl_gist_picksplit(internal, internal)
  RETURNS internal
  LANGUAGE C STRICT
  AS '$libdir/latlon-v0010', 'pgl_gist_picksplit';

CREATE OR REPLACE FUNCTION pgl_gist_same(internal, internal, internal)
  RETURNS internal
  LANGUAGE C STRICT
  AS '$libdir/latlon-v0010', 'pgl_gist_same';

CREATE OR REPLACE FUNCTION pgl_gist_distance(internal, internal, smallint, oid)
  RETURNS internal
  LANGUAGE C STRICT
  AS '$libdir/latlon-v0010', 'pgl_gist_distance';


---------------------
-- alias functions --
---------------------

CREATE OR REPLACE FUNCTION distance(epoint, epoint)
  RETURNS float8
  LANGUAGE sql IMMUTABLE AS 'SELECT $1 OPERATOR(@extschema@.<->) $2';

CREATE OR REPLACE FUNCTION distance(ecluster, epoint)
  RETURNS float8
  LANGUAGE sql IMMUTABLE AS 'SELECT $1 OPERATOR(@extschema@.<->) $2';

CREATE OR REPLACE FUNCTION distance_within(epoint, epoint, float8)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1 OPERATOR(@extschema@.&&) @extschema@.ecircle($2, $3)';

CREATE OR REPLACE FUNCTION distance_within(ecluster, epoint, float8)
  RETURNS boolean
  LANGUAGE sql IMMUTABLE
  AS 'SELECT $1 OPERATOR(@extschema@.&&) @extschema@.ecircle($2, $3)';

CREATE OR REPLACE FUNCTION fair_distance(ecluster, epoint, int4 = 10000)
  RETURNS float8
  LANGUAGE sql IMMUTABLE AS $$
    SELECT
    $1 OPERATOR(@extschema@.<=>) @extschema@.epoint_with_sample_count($2, $3)
  $$;


--------------------------------
-- other data storage formats --
--------------------------------

CREATE OR REPLACE FUNCTION coords_to_epoint(float8, float8, text = 'epoint')
  RETURNS epoint
  LANGUAGE plpgsql IMMUTABLE STRICT AS $$
    DECLARE
      "result" @extschema@.epoint;
    BEGIN
      IF $3 OPERATOR(pg_catalog.=) 'epoint_lonlat' THEN
        -- avoid dynamic command execution for better performance
        RETURN @extschema@.epoint($2, $1);
      END IF;
      IF
        $3 OPERATOR(pg_catalog.=) 'epoint' OR
        $3 OPERATOR(pg_catalog.=) 'epoint_latlon'
      THEN
        -- avoid dynamic command execution for better performance
        RETURN @extschema@.epoint($1, $2);
      END IF;
      EXECUTE
        'SELECT ' OPERATOR(pg_catalog.||) $3 OPERATOR(pg_catalog.||) '($1, $2)'         INTO STRICT "result" USING $1, $2;
      RETURN "result";
    END;
  $$;

CREATE OR REPLACE FUNCTION GeoJSON_LinearRing_vertices(jsonb, text = 'epoint_lonlat')
  RETURNS SETOF jsonb
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT "result" FROM
      ( SELECT pg_catalog.jsonb_array_length($1) - 1 )
      AS "lastindex_row" ("lastindex")
      CROSS JOIN LATERAL pg_catalog.jsonb_array_elements(
        CASE WHEN
          @extschema@.coords_to_epoint(
            ($1 OPERATOR(pg_catalog.->) 0 OPERATOR(pg_catalog.->>) 0)
            ::pg_catalog.float8,
            ($1 OPERATOR(pg_catalog.->) 0 OPERATOR(pg_catalog.->>) 1)
            ::pg_catalog.float8,
            $2
          ) OPERATOR(pg_catalog.=) @extschema@.coords_to_epoint(
            ($1 OPERATOR(pg_catalog.->) "lastindex" OPERATOR(pg_catalog.->>) 0)
            ::pg_catalog.float8,
            ($1 OPERATOR(pg_catalog.->) "lastindex" OPERATOR(pg_catalog.->>) 1)
            ::pg_catalog.float8,
            $2
          )
        THEN
          $1 - "lastindex"
        ELSE
          $1
        END
      ) AS "result_row" ("result")
  $$;

CREATE OR REPLACE FUNCTION GeoJSON_to_epoint(jsonb, text = 'epoint_lonlat')
  RETURNS epoint
  LANGUAGE sql IMMUTABLE STRICT AS $$
    SELECT CASE
    WHEN $1 OPERATOR(pg_catalog.->>) 'type' OPERATOR(pg_catalog.=) 'Point' THEN
      @extschema@.coords_to_epoint(
        ($1 OPERATOR(pg_catalog.->) 'coordinates' OPERATOR(pg_catalog.->>) 0)
        ::pg_catalog.float8,
        ($1 OPERATOR(pg_catalog.->) 'coordinates' OPERATOR(pg_catalog.->>) 1)
        ::pg_catalog.float8,
        $2
      )
    WHEN $1->>'type' = 'Feature' THEN
      @extschema@.GeoJSON_to_epoint($1 OPERATOR(pg_catalog.->) 'geometry', $2)
    ELSE
      NULL
    END
  $$;

CREATE OR REPLACE FUNCTION GeoJSON_to_ecluster(jsonb, text = 'epoint_lonlat')
  RETURNS ecluster
  LANGUAGE plpgsql IMMUTABLE STRICT AS $$
    DECLARE
      "tp" TEXT = $1 OPERATOR(pg_catalog.->>) 'type';
    BEGIN
      IF "tp" = 'Point' THEN RETURN
        @extschema@.coords_to_epoint(
          ($1 OPERATOR(pg_catalog.->) 'coordinates' OPERATOR(pg_catalog.->>) 0)
          ::pg_catalog.float8,
          ($1 OPERATOR(pg_catalog.->) 'coordinates' OPERATOR(pg_catalog.->>) 1)
          ::pg_catalog.float8,
          $2
        )::@extschema@.ecluster;
      END IF;
      raise notice 'DEBUG2';
      IF "tp" = 'MultiPoint' THEN RETURN
        ( SELECT @extschema@.ecluster_create_multipoint(pg_catalog.array_agg(
            @extschema@.coords_to_epoint(
              ("coord" OPERATOR(pg_catalog.->>) 0)::pg_catalog.float8,
              ("coord" OPERATOR(pg_catalog.->>) 1)::pg_catalog.float8,
              $2
            )
          ))
          FROM pg_catalog.jsonb_array_elements(
            $1 OPERATOR(pg_catalog.->) 'coordinates'
          ) AS "coord"
        );
      END IF;
      IF "tp" = 'LineString' THEN RETURN
        ( SELECT @extschema@.ecluster_create_path(pg_catalog.array_agg(
            @extschema@.coords_to_epoint(
              ("coord" OPERATOR(pg_catalog.->>) 0)::pg_catalog.float8,
              ("coord" OPERATOR(pg_catalog.->>) 1)::pg_catalog.float8,
              $2
            )
          ))
          FROM pg_catalog.jsonb_array_elements(
            $1 OPERATOR(pg_catalog.->) 'coordinates'
          ) AS "coord"
        );
      END IF;
      IF "tp" = 'MultiLineString' THEN RETURN
        ( SELECT @extschema@.ecluster_concat(pg_catalog.array_agg(
            ( SELECT @extschema@.ecluster_create_path(pg_catalog.array_agg(
                @extschema@.coords_to_epoint(
                  ("coord" OPERATOR(pg_catalog.->>) 0)::pg_catalog.float8,
                  ("coord" OPERATOR(pg_catalog.->>) 1)::pg_catalog.float8,
                  $2
                )
              ))
              FROM pg_catalog.jsonb_array_elements("coord_array") AS "coord"
            )
          ))
          FROM pg_catalog.jsonb_array_elements(
            $1 OPERATOR(pg_catalog.->) 'coordinates'
          ) AS "coord_array"
        );
      END IF;
      IF "tp" = 'Polygon' THEN RETURN
        ( SELECT @extschema@.ecluster_concat(pg_catalog.array_agg(
            ( SELECT @extschema@.ecluster_create_polygon(pg_catalog.array_agg(
                @extschema@.coords_to_epoint(
                  ("coord" OPERATOR(pg_catalog.->>) 0)::pg_catalog.float8,
                  ("coord" OPERATOR(pg_catalog.->>) 1)::pg_catalog.float8,
                  $2
                )
              ))
              FROM @extschema@.GeoJSON_LinearRing_vertices("coord_array", $2)
              AS "coord"
            )
          ))
          FROM pg_catalog.jsonb_array_elements(
            $1 OPERATOR(pg_catalog.->) 'coordinates'
          ) AS "coord_array"
        );
      END IF;
      IF "tp" = 'MultiPolygon' THEN RETURN
        ( SELECT @extschema@.ecluster_concat(pg_catalog.array_agg(
            ( SELECT @extschema@.ecluster_concat(pg_catalog.array_agg(
                ( SELECT @extschema@.ecluster_create_polygon(
                    pg_catalog.array_agg(
                      @extschema@.coords_to_epoint(
                        ("coord" OPERATOR(pg_catalog.->>) 0)
                        ::pg_catalog.float8,
                        ("coord" OPERATOR(pg_catalog.->>) 1)
                        ::pg_catalog.float8,
                        $2
                      )
                    )
                  )
                  FROM @extschema@.GeoJSON_LinearRing_vertices(
                    "coord_array", $2
                  ) AS "coord"
                )
              ))
              FROM pg_catalog.jsonb_array_elements("coord_array_array")
              AS "coord_array"
            )
          ))
          FROM jsonb_array_elements(
            $1 OPERATOR(pg_catalog.->) 'coordinates'
          ) AS "coord_array_array"
        );
      END IF;
      IF "tp" = 'GeometryCollection' THEN RETURN
        ( SELECT @extschema@.ecluster_concat(pg_catalog.array_agg(
            @extschema@.GeoJSON_to_ecluster("geometry", $2)
          ))
          FROM pg_catalog.jsonb_array_elements(
            $1 OPERATOR(pg_catalog.->) 'geometries'
          ) AS "geometry"
        );
      END IF;
      IF "tp" = 'Feature' THEN RETURN
        @extschema@.GeoJSON_to_ecluster(
          $1 OPERATOR(pg_catalog.->) 'geometry', $2
        );
      END IF;
      IF "tp" = 'FeatureCollection' THEN RETURN
        ( SELECT @extschema@.ecluster_concat(pg_catalog.array_agg(
            @extschema@.GeoJSON_to_ecluster("feature", $2)
          ))
          FROM pg_catalog.jsonb_array_elements(
            $1 OPERATOR(pg_catalog.->) 'features'
          ) AS "feature"
        );
      END IF;
      RETURN NULL;
    END;
  $$;

