diff --git a/contrib/cube/Makefile b/contrib/cube/Makefile
new file mode 100644
index 67f7867..e2a5d2c
*** a/contrib/cube/Makefile
--- b/contrib/cube/Makefile
*************** MODULE_big = cube
*** 4,10 ****
  OBJS= cube.o cubeparse.o $(WIN32RES)
  
  EXTENSION = cube
! DATA = cube--1.0.sql cube--unpackaged--1.0.sql
  PGFILEDESC = "cube - multidimensional cube data type"
  
  REGRESS = cube
--- 4,10 ----
  OBJS= cube.o cubeparse.o $(WIN32RES)
  
  EXTENSION = cube
! DATA = cube--1.1.sql cube--1.0--1.1.sql cube--unpackaged--1.0.sql
  PGFILEDESC = "cube - multidimensional cube data type"
  
  REGRESS = cube
diff --git a/contrib/cube/cube--1.0--1.1.sql b/contrib/cube/cube--1.0--1.1.sql
new file mode 100644
index ...f032f73
*** a/contrib/cube/cube--1.0--1.1.sql
--- b/contrib/cube/cube--1.0--1.1.sql
***************
*** 0 ****
--- 1,60 ----
+ /* contrib/cube/cube--1.0--1.1.sql */
+ 
+ -- complain if script is sourced in psql, rather than via ALTER EXTENSION
+ \echo Use "ALTER EXTENSION cube UPDATE TO '1.1'" to load this file. \quit
+ 
+ CREATE FUNCTION distance_chebyshev(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION distance_taxicab(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube_coord_llur(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE OPERATOR -> (
+ 	LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+ );
+ 
+ CREATE OPERATOR ~> (
+ 	LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord_llur
+ );
+ 
+ CREATE OPERATOR <#> (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ 	COMMUTATOR = '<#>'
+ );
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OPERATOR <=> (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ 	COMMUTATOR = '<=>'
+ );
+ 
+ CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ ALTER OPERATOR FAMILY gist_cube_ops USING gist ADD
+ 	OPERATOR	15	~> (cube, int) FOR ORDER BY float_ops,
+ 	OPERATOR	16	<#> (cube, cube) FOR ORDER BY float_ops,
+ 	OPERATOR	17	<-> (cube, cube) FOR ORDER BY float_ops,
+ 	OPERATOR	18	<=> (cube, cube) FOR ORDER BY float_ops,
+ 	FUNCTION	8	(cube, cube)	g_cube_distance (internal, cube, smallint, oid);
+ 
diff --git a/contrib/cube/cube--1.0.sql b/contrib/cube/cube--1.0.sql
new file mode .
index 0307811..e69de29
*** a/contrib/cube/cube--1.0.sql
--- b/contrib/cube/cube--1.0.sql
***************
*** 1,325 ****
- /* contrib/cube/cube--1.0.sql */
- 
- -- complain if script is sourced in psql, rather than via CREATE EXTENSION
- \echo Use "CREATE EXTENSION cube" to load this file. \quit
- 
- -- Create the user-defined type for N-dimensional boxes
- 
- CREATE FUNCTION cube_in(cstring)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube(float8[]) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_a_f8'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube_out(cube)
- RETURNS cstring
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE TYPE cube (
- 	INTERNALLENGTH = variable,
- 	INPUT = cube_in,
- 	OUTPUT = cube_out,
- 	ALIGNMENT = double
- );
- 
- COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
- 
- --
- -- External C-functions for R-tree methods
- --
- 
- -- Comparison methods
- 
- CREATE FUNCTION cube_eq(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
- 
- CREATE FUNCTION cube_ne(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
- 
- CREATE FUNCTION cube_lt(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
- 
- CREATE FUNCTION cube_gt(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
- 
- CREATE FUNCTION cube_le(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
- 
- CREATE FUNCTION cube_ge(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
- 
- CREATE FUNCTION cube_cmp(cube, cube)
- RETURNS int4
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
- 
- CREATE FUNCTION cube_contains(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
- 
- CREATE FUNCTION cube_contained(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
- 
- CREATE FUNCTION cube_overlap(cube, cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
- 
- -- support routines for indexing
- 
- CREATE FUNCTION cube_union(cube, cube)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube_inter(cube, cube)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube_size(cube)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- 
- -- Misc N-dimensional functions
- 
- CREATE FUNCTION cube_subset(cube, int4[])
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- -- proximity routines
- 
- CREATE FUNCTION cube_distance(cube, cube)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- -- Extracting elements functions
- 
- CREATE FUNCTION cube_dim(cube)
- RETURNS int4
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube_ll_coord(cube, int4)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube_ur_coord(cube, int4)
- RETURNS float8
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube(float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_f8'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube(float8, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube(cube, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_c_f8'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
- AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
- LANGUAGE C IMMUTABLE STRICT;
- 
- -- Test if cube is also a point
- 
- CREATE FUNCTION cube_is_point(cube)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- -- Increasing the size of a cube by a radius in at least n dimensions
- 
- CREATE FUNCTION cube_enlarge(cube, float8, int4)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- --
- -- OPERATORS
- --
- 
- CREATE OPERATOR < (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
- 	COMMUTATOR = '>', NEGATOR = '>=',
- 	RESTRICT = scalarltsel, JOIN = scalarltjoinsel
- );
- 
- CREATE OPERATOR > (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
- 	COMMUTATOR = '<', NEGATOR = '<=',
- 	RESTRICT = scalargtsel, JOIN = scalargtjoinsel
- );
- 
- CREATE OPERATOR <= (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
- 	COMMUTATOR = '>=', NEGATOR = '>',
- 	RESTRICT = scalarltsel, JOIN = scalarltjoinsel
- );
- 
- CREATE OPERATOR >= (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
- 	COMMUTATOR = '<=', NEGATOR = '<',
- 	RESTRICT = scalargtsel, JOIN = scalargtjoinsel
- );
- 
- CREATE OPERATOR && (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
- 	COMMUTATOR = '&&',
- 	RESTRICT = areasel, JOIN = areajoinsel
- );
- 
- CREATE OPERATOR = (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
- 	COMMUTATOR = '=', NEGATOR = '<>',
- 	RESTRICT = eqsel, JOIN = eqjoinsel,
- 	MERGES
- );
- 
- CREATE OPERATOR <> (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
- 	COMMUTATOR = '<>', NEGATOR = '=',
- 	RESTRICT = neqsel, JOIN = neqjoinsel
- );
- 
- CREATE OPERATOR @> (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- 	COMMUTATOR = '<@',
- 	RESTRICT = contsel, JOIN = contjoinsel
- );
- 
- CREATE OPERATOR <@ (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- 	COMMUTATOR = '@>',
- 	RESTRICT = contsel, JOIN = contjoinsel
- );
- 
- -- these are obsolete/deprecated:
- CREATE OPERATOR @ (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
- 	COMMUTATOR = '~',
- 	RESTRICT = contsel, JOIN = contjoinsel
- );
- 
- CREATE OPERATOR ~ (
- 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
- 	COMMUTATOR = '@',
- 	RESTRICT = contsel, JOIN = contjoinsel
- );
- 
- 
- -- define the GiST support methods
- CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
- RETURNS bool
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION g_cube_compress(internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION g_cube_decompress(internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION g_cube_penalty(internal,internal,internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION g_cube_picksplit(internal, internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION g_cube_union(internal, internal)
- RETURNS cube
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- CREATE FUNCTION g_cube_same(cube, cube, internal)
- RETURNS internal
- AS 'MODULE_PATHNAME'
- LANGUAGE C IMMUTABLE STRICT;
- 
- 
- -- Create the operator classes for indexing
- 
- CREATE OPERATOR CLASS cube_ops
-     DEFAULT FOR TYPE cube USING btree AS
-         OPERATOR        1       < ,
-         OPERATOR        2       <= ,
-         OPERATOR        3       = ,
-         OPERATOR        4       >= ,
-         OPERATOR        5       > ,
-         FUNCTION        1       cube_cmp(cube, cube);
- 
- CREATE OPERATOR CLASS gist_cube_ops
-     DEFAULT FOR TYPE cube USING gist AS
- 	OPERATOR	3	&& ,
- 	OPERATOR	6	= ,
- 	OPERATOR	7	@> ,
- 	OPERATOR	8	<@ ,
- 	OPERATOR	13	@ ,
- 	OPERATOR	14	~ ,
- 	FUNCTION	1	g_cube_consistent (internal, cube, int, oid, internal),
- 	FUNCTION	2	g_cube_union (internal, internal),
- 	FUNCTION	3	g_cube_compress (internal),
- 	FUNCTION	4	g_cube_decompress (internal),
- 	FUNCTION	5	g_cube_penalty (internal, internal, internal),
- 	FUNCTION	6	g_cube_picksplit (internal, internal),
- 	FUNCTION	7	g_cube_same (cube, cube, internal);
--- 0 ----
diff --git a/contrib/cube/cube--1.1.sql b/contrib/cube/cube--1.1.sql
new file mode 100644
index ...c944414
*** a/contrib/cube/cube--1.1.sql
--- b/contrib/cube/cube--1.1.sql
***************
*** 0 ****
--- 1,379 ----
+ /* contrib/cube/cube--1.1.sql */
+ 
+ -- complain if script is sourced in psql, rather than via CREATE EXTENSION
+ \echo Use "CREATE EXTENSION cube" to load this file. \quit
+ 
+ -- Create the user-defined type for N-dimensional boxes
+ 
+ CREATE FUNCTION cube_in(cstring)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube(float8[], float8[]) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_a_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube(float8[]) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_a_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube_out(cube)
+ RETURNS cstring
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE TYPE cube (
+ 	INTERNALLENGTH = variable,
+ 	INPUT = cube_in,
+ 	OUTPUT = cube_out,
+ 	ALIGNMENT = double
+ );
+ 
+ COMMENT ON TYPE cube IS 'multi-dimensional cube ''(FLOAT-1, FLOAT-2, ..., FLOAT-N), (FLOAT-1, FLOAT-2, ..., FLOAT-N)''';
+ 
+ --
+ -- External C-functions for R-tree methods
+ --
+ 
+ -- Comparison methods
+ 
+ CREATE FUNCTION cube_eq(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ COMMENT ON FUNCTION cube_eq(cube, cube) IS 'same as';
+ 
+ CREATE FUNCTION cube_ne(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ COMMENT ON FUNCTION cube_ne(cube, cube) IS 'different';
+ 
+ CREATE FUNCTION cube_lt(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ COMMENT ON FUNCTION cube_lt(cube, cube) IS 'lower than';
+ 
+ CREATE FUNCTION cube_gt(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ COMMENT ON FUNCTION cube_gt(cube, cube) IS 'greater than';
+ 
+ CREATE FUNCTION cube_le(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ COMMENT ON FUNCTION cube_le(cube, cube) IS 'lower than or equal to';
+ 
+ CREATE FUNCTION cube_ge(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ COMMENT ON FUNCTION cube_ge(cube, cube) IS 'greater than or equal to';
+ 
+ CREATE FUNCTION cube_cmp(cube, cube)
+ RETURNS int4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ COMMENT ON FUNCTION cube_cmp(cube, cube) IS 'btree comparison function';
+ 
+ CREATE FUNCTION cube_contains(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ COMMENT ON FUNCTION cube_contains(cube, cube) IS 'contains';
+ 
+ CREATE FUNCTION cube_contained(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ COMMENT ON FUNCTION cube_contained(cube, cube) IS 'contained in';
+ 
+ CREATE FUNCTION cube_overlap(cube, cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ COMMENT ON FUNCTION cube_overlap(cube, cube) IS 'overlaps';
+ 
+ -- support routines for indexing
+ 
+ CREATE FUNCTION cube_union(cube, cube)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube_inter(cube, cube)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube_size(cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ 
+ -- Misc N-dimensional functions
+ 
+ CREATE FUNCTION cube_subset(cube, int4[])
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ -- proximity routines
+ 
+ CREATE FUNCTION cube_distance(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION distance_chebyshev(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION distance_taxicab(cube, cube)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ -- Extracting elements functions
+ 
+ CREATE FUNCTION cube_dim(cube)
+ RETURNS int4
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube_ll_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube_ur_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube_coord(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube_coord_llur(cube, int4)
+ RETURNS float8
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube(float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube(float8, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube(cube, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_c_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION cube(cube, float8, float8) RETURNS cube
+ AS 'MODULE_PATHNAME', 'cube_c_f8_f8'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ -- Test if cube is also a point
+ 
+ CREATE FUNCTION cube_is_point(cube)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ -- Increasing the size of a cube by a radius in at least n dimensions
+ 
+ CREATE FUNCTION cube_enlarge(cube, float8, int4)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ --
+ -- OPERATORS
+ --
+ 
+ CREATE OPERATOR < (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_lt,
+ 	COMMUTATOR = '>', NEGATOR = '>=',
+ 	RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+ );
+ 
+ CREATE OPERATOR > (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_gt,
+ 	COMMUTATOR = '<', NEGATOR = '<=',
+ 	RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+ );
+ 
+ CREATE OPERATOR <= (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_le,
+ 	COMMUTATOR = '>=', NEGATOR = '>',
+ 	RESTRICT = scalarltsel, JOIN = scalarltjoinsel
+ );
+ 
+ CREATE OPERATOR >= (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ge,
+ 	COMMUTATOR = '<=', NEGATOR = '<',
+ 	RESTRICT = scalargtsel, JOIN = scalargtjoinsel
+ );
+ 
+ CREATE OPERATOR && (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_overlap,
+ 	COMMUTATOR = '&&',
+ 	RESTRICT = areasel, JOIN = areajoinsel
+ );
+ 
+ CREATE OPERATOR = (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_eq,
+ 	COMMUTATOR = '=', NEGATOR = '<>',
+ 	RESTRICT = eqsel, JOIN = eqjoinsel,
+ 	MERGES
+ );
+ 
+ CREATE OPERATOR <> (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_ne,
+ 	COMMUTATOR = '<>', NEGATOR = '=',
+ 	RESTRICT = neqsel, JOIN = neqjoinsel
+ );
+ 
+ CREATE OPERATOR @> (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ 	COMMUTATOR = '<@',
+ 	RESTRICT = contsel, JOIN = contjoinsel
+ );
+ 
+ CREATE OPERATOR <@ (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ 	COMMUTATOR = '@>',
+ 	RESTRICT = contsel, JOIN = contjoinsel
+ );
+ 
+ CREATE OPERATOR -> (
+ 	LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord
+ );
+ 
+ CREATE OPERATOR ~> (
+ 	LEFTARG = cube, RIGHTARG = int, PROCEDURE = cube_coord_llur
+ );
+ 
+ CREATE OPERATOR <#> (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_taxicab,
+ 	COMMUTATOR = '<#>'
+ );
+ 
+ CREATE OPERATOR <-> (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_distance,
+ 	COMMUTATOR = '<->'
+ );
+ 
+ CREATE OPERATOR <=> (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = distance_chebyshev,
+ 	COMMUTATOR = '<=>'
+ );
+ 
+ -- these are obsolete/deprecated:
+ CREATE OPERATOR @ (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contains,
+ 	COMMUTATOR = '~',
+ 	RESTRICT = contsel, JOIN = contjoinsel
+ );
+ 
+ CREATE OPERATOR ~ (
+ 	LEFTARG = cube, RIGHTARG = cube, PROCEDURE = cube_contained,
+ 	COMMUTATOR = '@',
+ 	RESTRICT = contsel, JOIN = contjoinsel
+ );
+ 
+ 
+ -- define the GiST support methods
+ CREATE FUNCTION g_cube_consistent(internal,cube,int,oid,internal)
+ RETURNS bool
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION g_cube_compress(internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION g_cube_decompress(internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION g_cube_penalty(internal,internal,internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION g_cube_picksplit(internal, internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION g_cube_union(internal, internal)
+ RETURNS cube
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION g_cube_same(cube, cube, internal)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ CREATE FUNCTION g_cube_distance (internal, cube, smallint, oid)
+ RETURNS internal
+ AS 'MODULE_PATHNAME'
+ LANGUAGE C IMMUTABLE STRICT;
+ 
+ -- Create the operator classes for indexing
+ 
+ CREATE OPERATOR CLASS cube_ops
+     DEFAULT FOR TYPE cube USING btree AS
+         OPERATOR        1       < ,
+         OPERATOR        2       <= ,
+         OPERATOR        3       = ,
+         OPERATOR        4       >= ,
+         OPERATOR        5       > ,
+         FUNCTION        1       cube_cmp(cube, cube);
+ 
+ CREATE OPERATOR CLASS gist_cube_ops
+     DEFAULT FOR TYPE cube USING gist AS
+ 	OPERATOR	3	&& ,
+ 	OPERATOR	6	= ,
+ 	OPERATOR	7	@> ,
+ 	OPERATOR	8	<@ ,
+ 	OPERATOR	13	@ ,
+ 	OPERATOR	14	~ ,
+ 	OPERATOR	15	~> (cube, int) FOR ORDER BY float_ops,
+ 	OPERATOR	16	<#> (cube, cube) FOR ORDER BY float_ops,
+ 	OPERATOR	17	<-> (cube, cube) FOR ORDER BY float_ops,
+ 	OPERATOR	18	<=> (cube, cube) FOR ORDER BY float_ops,
+ 
+ 	FUNCTION	1	g_cube_consistent (internal, cube, int, oid, internal),
+ 	FUNCTION	2	g_cube_union (internal, internal),
+ 	FUNCTION	3	g_cube_compress (internal),
+ 	FUNCTION	4	g_cube_decompress (internal),
+ 	FUNCTION	5	g_cube_penalty (internal, internal, internal),
+ 	FUNCTION	6	g_cube_picksplit (internal, internal),
+ 	FUNCTION	7	g_cube_same (cube, cube, internal),
+ 	FUNCTION	8	g_cube_distance (internal, cube, smallint, oid);
+ 
diff --git a/contrib/cube/cube.c b/contrib/cube/cube.c
new file mode 100644
index a6be59e..676d9a9
*** a/contrib/cube/cube.c
--- b/contrib/cube/cube.c
*************** PG_FUNCTION_INFO_V1(cube_c_f8_f8);
*** 40,45 ****
--- 40,47 ----
  PG_FUNCTION_INFO_V1(cube_dim);
  PG_FUNCTION_INFO_V1(cube_ll_coord);
  PG_FUNCTION_INFO_V1(cube_ur_coord);
+ PG_FUNCTION_INFO_V1(cube_coord);
+ PG_FUNCTION_INFO_V1(cube_coord_llur);
  PG_FUNCTION_INFO_V1(cube_subset);
  
  /*
*************** PG_FUNCTION_INFO_V1(g_cube_penalty);
*** 53,58 ****
--- 55,62 ----
  PG_FUNCTION_INFO_V1(g_cube_picksplit);
  PG_FUNCTION_INFO_V1(g_cube_union);
  PG_FUNCTION_INFO_V1(g_cube_same);
+ PG_FUNCTION_INFO_V1(g_cube_distance);
+ 
  
  /*
  ** B-tree support functions
*************** PG_FUNCTION_INFO_V1(cube_size);
*** 79,85 ****
--- 83,91 ----
  /*
  ** miscellaneous
  */
+ PG_FUNCTION_INFO_V1(distance_taxicab);
  PG_FUNCTION_INFO_V1(cube_distance);
+ PG_FUNCTION_INFO_V1(distance_chebyshev);
  PG_FUNCTION_INFO_V1(cube_is_point);
  PG_FUNCTION_INFO_V1(cube_enlarge);
  
*************** cube_overlap(PG_FUNCTION_ARGS)
*** 1202,1208 ****
  	PG_RETURN_BOOL(res);
  }
  
- 
  /* Distance */
  /* The distance is computed as a per axis sum of the squared distances
     between 1D projections of the boxes onto Cartesian axes. Assuming zero
--- 1208,1213 ----
*************** cube_distance(PG_FUNCTION_ARGS)
*** 1257,1262 ****
--- 1262,1405 ----
  	PG_RETURN_FLOAT8(sqrt(distance));
  }
  
+ Datum
+ distance_taxicab(PG_FUNCTION_ARGS)
+ {
+ 	NDBOX	   *a = PG_GETARG_NDBOX(0),
+ 			   *b = PG_GETARG_NDBOX(1);
+ 	bool		swapped = false;
+ 	double		distance;
+ 	int			i;
+ 
+ 	/* swap the box pointers if needed */
+ 	if (DIM(a) < DIM(b))
+ 	{
+ 		NDBOX	   *tmp = b;
+ 		b = a;
+ 		a = tmp;
+ 		swapped = true;
+ 	}
+ 
+ 	distance = 0.0;
+ 	/* compute within the dimensions of (b) */
+ 	for (i = 0; i < DIM(b); i++)
+ 		distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+ 
+ 	/* compute distance to zero for those dimensions in (a) absent in (b) */
+ 	for (i = DIM(b); i < DIM(a); i++)
+ 		distance += fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+ 
+ 	if (swapped)
+ 	{
+ 		PG_FREE_IF_COPY(b, 0);
+ 		PG_FREE_IF_COPY(a, 1);
+ 	}
+ 	else
+ 	{
+ 		PG_FREE_IF_COPY(a, 0);
+ 		PG_FREE_IF_COPY(b, 1);
+ 	}
+ 
+ 	PG_RETURN_FLOAT8(distance);
+ }
+ 
+ Datum
+ distance_chebyshev(PG_FUNCTION_ARGS)
+ {
+ 	NDBOX	   *a = PG_GETARG_NDBOX(0),
+ 			   *b = PG_GETARG_NDBOX(1);
+ 	bool		swapped = false;
+ 	double		d, distance;
+ 	int			i;
+ 
+ 	/* swap the box pointers if needed */
+ 	if (DIM(a) < DIM(b))
+ 	{
+ 		NDBOX	   *tmp = b;
+ 		b = a;
+ 		a = tmp;
+ 		swapped = true;
+ 	}
+ 
+ 	distance = 0.0;
+ 	/* compute within the dimensions of (b) */
+ 	for (i = 0; i < DIM(b); i++)
+ 	{
+ 		d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), LL_COORD(b,i), UR_COORD(b,i)));
+ 		if (d > distance)
+ 			distance = d;
+ 	}
+ 
+ 	/* compute distance to zero for those dimensions in (a) absent in (b) */
+ 	for (i = DIM(b); i < DIM(a); i++)
+ 	{
+ 		d = fabs(distance_1D(LL_COORD(a,i), UR_COORD(a,i), 0.0, 0.0));
+ 		if (d > distance)
+ 			distance = d;
+ 	}
+ 
+ 	if (swapped)
+ 	{
+ 		PG_FREE_IF_COPY(b, 0);
+ 		PG_FREE_IF_COPY(a, 1);
+ 	}
+ 	else
+ 	{
+ 		PG_FREE_IF_COPY(a, 0);
+ 		PG_FREE_IF_COPY(b, 1);
+ 	}
+ 
+ 	PG_RETURN_FLOAT8(distance);
+ }
+ 
+ Datum
+ g_cube_distance(PG_FUNCTION_ARGS)
+ {
+ 	GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
+ 	StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
+ 	NDBOX      *cube = DatumGetNDBOX(entry->key);
+ 	double      retval;
+ 
+ 	if (strategy == CubeKNNDistanceCoord)
+ 	{
+ 		int coord = PG_GETARG_INT32(1);
+ 
+ 		if IS_POINT(cube)
+ 		{
+ 			retval = (cube)->x[(coord-1)%DIM(cube)];
+ 		}
+ 		else
+ 		{
+ 			retval = Min(
+ 				(cube)->x[(coord-1)%DIM(cube)],
+ 				(cube)->x[(coord-1)%DIM(cube) + DIM(cube)]
+ 			);
+ 		}
+ 	}
+ 	else
+ 	{
+ 		NDBOX *query = PG_GETARG_NDBOX(1);
+ 		switch(strategy)
+ 		{
+ 		case CubeKNNDistanceTaxicab:
+ 			retval = DatumGetFloat8(DirectFunctionCall2(distance_taxicab,
+ 				PointerGetDatum(cube), PointerGetDatum(query)));
+ 			break;
+ 		case CubeKNNDistanceEuclid:
+ 			retval = DatumGetFloat8(DirectFunctionCall2(cube_distance,
+ 				PointerGetDatum(cube), PointerGetDatum(query)));
+ 			break;
+ 		case CubeKNNDistanceChebyshev:
+ 			retval = DatumGetFloat8(DirectFunctionCall2(distance_chebyshev,
+ 				PointerGetDatum(cube), PointerGetDatum(query)));
+ 			break;
+ 		default:
+ 			elog(ERROR, "Cube: unknown strategy number.");
+ 		}
+ 	}
+ 	PG_RETURN_FLOAT8(retval);
+ }
+ 
  static double
  distance_1D(double a1, double a2, double b1, double b2)
  {
*************** cube_ur_coord(PG_FUNCTION_ARGS)
*** 1352,1357 ****
--- 1495,1565 ----
  	PG_RETURN_FLOAT8(result);
  }
  
+ /*
+  * Function returns cube coordinate.
+  * Numbers from 1 to DIM denotes first corner coordinates.
+  * Numbers from DIM+1 to 2*DIM denotes second corner coordinates.
+  */
+ Datum
+ cube_coord(PG_FUNCTION_ARGS)
+ {
+ 	NDBOX	   *cube = PG_GETARG_NDBOX(0);
+ 	int			coord = PG_GETARG_INT16(1);
+ 
+ 	if ((coord > 0) && (coord <= 2*DIM(cube)))
+ 	{
+ 		if IS_POINT(cube)
+ 			PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ 		else
+ 			PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 					(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ 					 errmsg("Cube index out of bounds")));
+ 	}
+ }
+ 
+ 
+ /*
+  * This function works like cube_coord(),
+  * but rearranges coordinates of corners to get cube representation 
+  * in the form of (lower left, upper right).
+  * For historical reasons that extension allows us to create cubes in form
+  * ((2,1),(1,2)) and instead of normalizing such cube to ((1,1),(2,2)) it
+  * stores cube in original way. But to get cubes ordered by one of dimensions
+  * directly from the index without extra sort step we need some
+  * representation-independent coordinate getter. This function implements it.
+  */
+ Datum
+ cube_coord_llur(PG_FUNCTION_ARGS)
+ {
+ 	NDBOX	   *cube = PG_GETARG_NDBOX(0);
+ 	int			coord = PG_GETARG_INT16(1);
+ 
+ 	if ((coord > 0) && (coord <= DIM(cube)))
+ 	{
+ 		if IS_POINT(cube)
+ 			PG_RETURN_FLOAT8( (cube)->x[coord-1] );
+ 		else
+ 			PG_RETURN_FLOAT8( Min((cube)->x[coord-1], (cube)->x[coord-1+DIM(cube)]) );
+ 	}
+ 	else if ((coord > DIM(cube)) && (coord <= 2*DIM(cube)))
+ 	{
+ 		if IS_POINT(cube)
+ 			PG_RETURN_FLOAT8( (cube)->x[(coord-1)%DIM(cube)] );
+ 		else
+ 			PG_RETURN_FLOAT8( Max((cube)->x[coord-1], (cube)->x[coord-1-DIM(cube)]) );
+ 	}
+ 	else
+ 	{
+ 		ereport(ERROR,
+ 					(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+ 					 errmsg("Cube index out of bounds")));
+ 	}
+ }
+ 
  /* Increase or decrease box size by a radius in at least n dimensions. */
  Datum
  cube_enlarge(PG_FUNCTION_ARGS)
diff --git a/contrib/cube/cube.control b/contrib/cube/cube.control
new file mode 100644
index ddc8d2e..f84e6c5
*** a/contrib/cube/cube.control
--- b/contrib/cube/cube.control
***************
*** 1,5 ****
  # cube extension
  comment = 'data type for multidimensional cubes'
! default_version = '1.0'
  module_pathname = '$libdir/cube'
  relocatable = true
--- 1,5 ----
  # cube extension
  comment = 'data type for multidimensional cubes'
! default_version = '1.1'
  module_pathname = '$libdir/cube'
  relocatable = true
diff --git a/contrib/cube/cubedata.h b/contrib/cube/cubedata.h
new file mode 100644
index 59c23de..7eaac39
*** a/contrib/cube/cubedata.h
--- b/contrib/cube/cubedata.h
*************** typedef struct NDBOX
*** 47,52 ****
--- 47,57 ----
  #define PG_GETARG_NDBOX(x)	DatumGetNDBOX(PG_GETARG_DATUM(x))
  #define PG_RETURN_NDBOX(x)	PG_RETURN_POINTER(x)
  
+ #define CubeKNNDistanceCoord			15		/* ~> */
+ #define CubeKNNDistanceTaxicab			16		/* <#> */
+ #define CubeKNNDistanceEuclid			17		/* <-> */
+ #define CubeKNNDistanceChebyshev		18		/* <=> */
+ 
  /* in cubescan.l */
  extern int	cube_yylex(void);
  extern void cube_yyerror(NDBOX **result, const char *message) pg_attribute_noreturn();
diff --git a/contrib/cube/expected/cube.out b/contrib/cube/expected/cube.out
new file mode 100644
index ca9555e..4b2d135
*** a/contrib/cube/expected/cube.out
--- b/contrib/cube/expected/cube.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
           0
  (1 row)
  
+ -- Test of distances
+ -- 
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+  cube_distance 
+ ---------------
+              5
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+  d_e 
+ -----
+    5
+ (1 row)
+ 
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+  distance_chebyshev 
+ --------------------
+                   4
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+  d_c 
+ -----
+    4
+ (1 row)
+ 
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+  distance_taxicab 
+ ------------------
+                 7
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+  d_t 
+ -----
+    7
+ (1 row)
+ 
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  cube_distance 
+ ---------------
+              0
+ (1 row)
+ 
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  distance_chebyshev 
+ --------------------
+                   0
+ (1 row)
+ 
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  distance_taxicab 
+ ------------------
+                 0
+ (1 row)
+ 
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+  ?column? 
+ ----------
+        40
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+  ?column? 
+ ----------
+        60
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[10,20,30])->6;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[10,20,30])->-6;
+ ERROR:  Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+  ?column? 
+ ----------
+        20
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+  ?column? 
+ ----------
+        20
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+  ?column? 
+ ----------
+        40
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR:  Cube index out of bounds
  -- Load some example data and build the index
  --
  CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
   (2424, 160),(2424, 81)
  (5 rows)
  
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            |       dist       
+ -------------------------+------------------
+  (337, 455),(240, 359)   |                0
+  (759, 187),(662, 163)   |              162
+  (948, 1201),(907, 1156) | 772.000647668122
+  (1444, 403),(1346, 344) |              846
+  (369, 1457),(278, 1409) |              909
+ (5 rows)
+ 
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            | dist 
+ -------------------------+------
+  (337, 455),(240, 359)   |    0
+  (759, 187),(662, 163)   |  162
+  (948, 1201),(907, 1156) |  656
+  (1444, 403),(1346, 344) |  846
+  (369, 1457),(278, 1409) |  909
+ (5 rows)
+ 
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            | dist 
+ -------------------------+------
+  (337, 455),(240, 359)   |    0
+  (759, 187),(662, 163)   |  162
+  (1444, 403),(1346, 344) |  846
+  (369, 1457),(278, 1409) |  909
+  (948, 1201),(907, 1156) | 1063
+ (5 rows)
+ 
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+              c             
+ ---------------------------
+  (54, 38679),(3, 38602)
+  (83, 10271),(15, 10265)
+  (122, 46832),(64, 46762)
+  (167, 17214),(92, 17184)
+  (161, 24465),(107, 24374)
+  (162, 26040),(120, 25963)
+  (154, 4019),(138, 3990)
+  (259, 1850),(175, 1820)
+  (207, 40886),(179, 40879)
+  (288, 49588),(204, 49571)
+  (270, 32616),(226, 32607)
+  (318, 31489),(235, 31404)
+  (337, 455),(240, 359)
+  (270, 29508),(264, 29440)
+  (369, 1457),(278, 1409)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+              c             
+ ---------------------------
+  (30333, 50),(30273, 6)
+  (43301, 75),(43227, 43)
+  (19650, 142),(19630, 51)
+  (2424, 160),(2424, 81)
+  (3449, 171),(3354, 108)
+  (18037, 155),(17941, 109)
+  (28511, 208),(28479, 114)
+  (19946, 217),(19941, 118)
+  (16906, 191),(16816, 139)
+  (759, 187),(662, 163)
+  (22684, 266),(22656, 181)
+  (24423, 255),(24360, 213)
+  (45989, 249),(45910, 222)
+  (11399, 377),(11360, 294)
+  (12162, 389),(12103, 309)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+                c               
+ -------------------------------
+  (50027, 49230),(49951, 49214)
+  (49980, 35004),(49937, 34963)
+  (49985, 6436),(49927, 6338)
+  (49999, 27218),(49908, 27176)
+  (49954, 1340),(49905, 1294)
+  (49944, 25163),(49902, 25153)
+  (49981, 34876),(49898, 34786)
+  (49957, 43390),(49897, 43384)
+  (49853, 18504),(49848, 18503)
+  (49902, 41752),(49818, 41746)
+  (49907, 30225),(49810, 30158)
+  (49843, 5175),(49808, 5145)
+  (49887, 24274),(49805, 24184)
+  (49847, 7128),(49798, 7067)
+  (49820, 7990),(49771, 7967)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+                c               
+ -------------------------------
+  (36311, 50073),(36258, 49987)
+  (30746, 50040),(30727, 49992)
+  (2168, 50012),(2108, 49914)
+  (21551, 49983),(21492, 49885)
+  (17954, 49975),(17865, 49915)
+  (3531, 49962),(3463, 49934)
+  (19128, 49932),(19112, 49849)
+  (31287, 49923),(31236, 49913)
+  (43925, 49912),(43888, 49878)
+  (29261, 49910),(29247, 49818)
+  (14913, 49873),(14849, 49836)
+  (20007, 49858),(19921, 49778)
+  (38266, 49852),(38233, 49844)
+  (37595, 49849),(37581, 49834)
+  (46151, 49848),(46058, 49830)
+ (15 rows)
+ 
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+             c             
+ --------------------------
+  (54, 38679, 3, 38602)
+  (83, 10271, 15, 10265)
+  (122, 46832, 64, 46762)
+  (154, 4019, 138, 3990)
+  (161, 24465, 107, 24374)
+  (162, 26040, 120, 25963)
+  (167, 17214, 92, 17184)
+  (207, 40886, 179, 40879)
+  (259, 1850, 175, 1820)
+  (270, 29508, 264, 29440)
+  (270, 32616, 226, 32607)
+  (288, 49588, 204, 49571)
+  (318, 31489, 235, 31404)
+  (326, 18837, 285, 18817)
+  (337, 455, 240, 359)
+ (15 rows)
+ 
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+               c               
+ ------------------------------
+  (30746, 50040, 30727, 49992)
+  (36311, 50073, 36258, 49987)
+  (3531, 49962, 3463, 49934)
+  (17954, 49975, 17865, 49915)
+  (2168, 50012, 2108, 49914)
+  (31287, 49923, 31236, 49913)
+  (21551, 49983, 21492, 49885)
+  (43925, 49912, 43888, 49878)
+  (19128, 49932, 19112, 49849)
+  (38266, 49852, 38233, 49844)
+  (14913, 49873, 14849, 49836)
+  (37595, 49849, 37581, 49834)
+  (46151, 49848, 46058, 49830)
+  (29261, 49910, 29247, 49818)
+  (19233, 49824, 19185, 49794)
+ (15 rows)
+ 
diff --git a/contrib/cube/expected/cube_1.out b/contrib/cube/expected/cube_1.out
new file mode 100644
index c07d61d..7178088
*** a/contrib/cube/expected/cube_1.out
--- b/contrib/cube/expected/cube_1.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
           0
  (1 row)
  
+ -- Test of distances
+ --
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+  cube_distance 
+ ---------------
+              5
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+  d_e 
+ -----
+    5
+ (1 row)
+ 
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+  distance_chebyshev 
+ --------------------
+                   4
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+  d_c 
+ -----
+    4
+ (1 row)
+ 
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+  distance_taxicab 
+ ------------------
+                 7
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+  d_t 
+ -----
+    7
+ (1 row)
+ 
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  cube_distance 
+ ---------------
+              0
+ (1 row)
+ 
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  distance_chebyshev 
+ --------------------
+                   0
+ (1 row)
+ 
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  distance_taxicab 
+ ------------------
+                 0
+ (1 row)
+ 
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+  ?column? 
+ ----------
+        40
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+  ?column? 
+ ----------
+        60
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[10,20,30])->6;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[10,20,30])->-6;
+ ERROR:  Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+  ?column? 
+ ----------
+        20
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+  ?column? 
+ ----------
+        20
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+  ?column? 
+ ----------
+        40
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR:  Cube index out of bounds
  -- Load some example data and build the index
  --
  CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
   (2424, 160),(2424, 81)
  (5 rows)
  
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            |       dist       
+ -------------------------+------------------
+  (337, 455),(240, 359)   |                0
+  (759, 187),(662, 163)   |              162
+  (948, 1201),(907, 1156) | 772.000647668122
+  (1444, 403),(1346, 344) |              846
+  (369, 1457),(278, 1409) |              909
+ (5 rows)
+ 
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            | dist 
+ -------------------------+------
+  (337, 455),(240, 359)   |    0
+  (759, 187),(662, 163)   |  162
+  (948, 1201),(907, 1156) |  656
+  (1444, 403),(1346, 344) |  846
+  (369, 1457),(278, 1409) |  909
+ (5 rows)
+ 
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            | dist 
+ -------------------------+------
+  (337, 455),(240, 359)   |    0
+  (759, 187),(662, 163)   |  162
+  (1444, 403),(1346, 344) |  846
+  (369, 1457),(278, 1409) |  909
+  (948, 1201),(907, 1156) | 1063
+ (5 rows)
+ 
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+              c             
+ ---------------------------
+  (54, 38679),(3, 38602)
+  (83, 10271),(15, 10265)
+  (122, 46832),(64, 46762)
+  (167, 17214),(92, 17184)
+  (161, 24465),(107, 24374)
+  (162, 26040),(120, 25963)
+  (154, 4019),(138, 3990)
+  (259, 1850),(175, 1820)
+  (207, 40886),(179, 40879)
+  (288, 49588),(204, 49571)
+  (270, 32616),(226, 32607)
+  (318, 31489),(235, 31404)
+  (337, 455),(240, 359)
+  (270, 29508),(264, 29440)
+  (369, 1457),(278, 1409)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+              c             
+ ---------------------------
+  (30333, 50),(30273, 6)
+  (43301, 75),(43227, 43)
+  (19650, 142),(19630, 51)
+  (2424, 160),(2424, 81)
+  (3449, 171),(3354, 108)
+  (18037, 155),(17941, 109)
+  (28511, 208),(28479, 114)
+  (19946, 217),(19941, 118)
+  (16906, 191),(16816, 139)
+  (759, 187),(662, 163)
+  (22684, 266),(22656, 181)
+  (24423, 255),(24360, 213)
+  (45989, 249),(45910, 222)
+  (11399, 377),(11360, 294)
+  (12162, 389),(12103, 309)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+                c               
+ -------------------------------
+  (50027, 49230),(49951, 49214)
+  (49980, 35004),(49937, 34963)
+  (49985, 6436),(49927, 6338)
+  (49999, 27218),(49908, 27176)
+  (49954, 1340),(49905, 1294)
+  (49944, 25163),(49902, 25153)
+  (49981, 34876),(49898, 34786)
+  (49957, 43390),(49897, 43384)
+  (49853, 18504),(49848, 18503)
+  (49902, 41752),(49818, 41746)
+  (49907, 30225),(49810, 30158)
+  (49843, 5175),(49808, 5145)
+  (49887, 24274),(49805, 24184)
+  (49847, 7128),(49798, 7067)
+  (49820, 7990),(49771, 7967)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+                c               
+ -------------------------------
+  (36311, 50073),(36258, 49987)
+  (30746, 50040),(30727, 49992)
+  (2168, 50012),(2108, 49914)
+  (21551, 49983),(21492, 49885)
+  (17954, 49975),(17865, 49915)
+  (3531, 49962),(3463, 49934)
+  (19128, 49932),(19112, 49849)
+  (31287, 49923),(31236, 49913)
+  (43925, 49912),(43888, 49878)
+  (29261, 49910),(29247, 49818)
+  (14913, 49873),(14849, 49836)
+  (20007, 49858),(19921, 49778)
+  (38266, 49852),(38233, 49844)
+  (37595, 49849),(37581, 49834)
+  (46151, 49848),(46058, 49830)
+ (15 rows)
+ 
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+             c             
+ --------------------------
+  (54, 38679, 3, 38602)
+  (83, 10271, 15, 10265)
+  (122, 46832, 64, 46762)
+  (154, 4019, 138, 3990)
+  (161, 24465, 107, 24374)
+  (162, 26040, 120, 25963)
+  (167, 17214, 92, 17184)
+  (207, 40886, 179, 40879)
+  (259, 1850, 175, 1820)
+  (270, 29508, 264, 29440)
+  (270, 32616, 226, 32607)
+  (288, 49588, 204, 49571)
+  (318, 31489, 235, 31404)
+  (326, 18837, 285, 18817)
+  (337, 455, 240, 359)
+ (15 rows)
+ 
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+               c               
+ ------------------------------
+  (30746, 50040, 30727, 49992)
+  (36311, 50073, 36258, 49987)
+  (3531, 49962, 3463, 49934)
+  (17954, 49975, 17865, 49915)
+  (2168, 50012, 2108, 49914)
+  (31287, 49923, 31236, 49913)
+  (21551, 49983, 21492, 49885)
+  (43925, 49912, 43888, 49878)
+  (19128, 49932, 19112, 49849)
+  (38266, 49852, 38233, 49844)
+  (14913, 49873, 14849, 49836)
+  (37595, 49849, 37581, 49834)
+  (46151, 49848, 46058, 49830)
+  (29261, 49910, 29247, 49818)
+  (19233, 49824, 19185, 49794)
+ (15 rows)
+ 
diff --git a/contrib/cube/expected/cube_2.out b/contrib/cube/expected/cube_2.out
new file mode 100644
index 3767d0e..a6c7d36
*** a/contrib/cube/expected/cube_2.out
--- b/contrib/cube/expected/cube_2.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
           0
  (1 row)
  
+ -- Test of distances
+ -- 
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+  cube_distance 
+ ---------------
+              5
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+  d_e 
+ -----
+    5
+ (1 row)
+ 
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+  distance_chebyshev 
+ --------------------
+                   4
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+  d_c 
+ -----
+    4
+ (1 row)
+ 
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+  distance_taxicab 
+ ------------------
+                 7
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+  d_t 
+ -----
+    7
+ (1 row)
+ 
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  cube_distance 
+ ---------------
+              0
+ (1 row)
+ 
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  distance_chebyshev 
+ --------------------
+                   0
+ (1 row)
+ 
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  distance_taxicab 
+ ------------------
+                 0
+ (1 row)
+ 
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+  ?column? 
+ ----------
+        40
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+  ?column? 
+ ----------
+        60
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[10,20,30])->6;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[10,20,30])->-6;
+ ERROR:  Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+  ?column? 
+ ----------
+        20
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+  ?column? 
+ ----------
+        20
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+  ?column? 
+ ----------
+        40
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR:  Cube index out of bounds
  -- Load some example data and build the index
  --
  CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
   (2424, 160),(2424, 81)
  (5 rows)
  
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            |       dist       
+ -------------------------+------------------
+  (337, 455),(240, 359)   |                0
+  (759, 187),(662, 163)   |              162
+  (948, 1201),(907, 1156) | 772.000647668122
+  (1444, 403),(1346, 344) |              846
+  (369, 1457),(278, 1409) |              909
+ (5 rows)
+ 
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            | dist 
+ -------------------------+------
+  (337, 455),(240, 359)   |    0
+  (759, 187),(662, 163)   |  162
+  (948, 1201),(907, 1156) |  656
+  (1444, 403),(1346, 344) |  846
+  (369, 1457),(278, 1409) |  909
+ (5 rows)
+ 
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            | dist 
+ -------------------------+------
+  (337, 455),(240, 359)   |    0
+  (759, 187),(662, 163)   |  162
+  (1444, 403),(1346, 344) |  846
+  (369, 1457),(278, 1409) |  909
+  (948, 1201),(907, 1156) | 1063
+ (5 rows)
+ 
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+              c             
+ ---------------------------
+  (54, 38679),(3, 38602)
+  (83, 10271),(15, 10265)
+  (122, 46832),(64, 46762)
+  (167, 17214),(92, 17184)
+  (161, 24465),(107, 24374)
+  (162, 26040),(120, 25963)
+  (154, 4019),(138, 3990)
+  (259, 1850),(175, 1820)
+  (207, 40886),(179, 40879)
+  (288, 49588),(204, 49571)
+  (270, 32616),(226, 32607)
+  (318, 31489),(235, 31404)
+  (337, 455),(240, 359)
+  (270, 29508),(264, 29440)
+  (369, 1457),(278, 1409)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+              c             
+ ---------------------------
+  (30333, 50),(30273, 6)
+  (43301, 75),(43227, 43)
+  (19650, 142),(19630, 51)
+  (2424, 160),(2424, 81)
+  (3449, 171),(3354, 108)
+  (18037, 155),(17941, 109)
+  (28511, 208),(28479, 114)
+  (19946, 217),(19941, 118)
+  (16906, 191),(16816, 139)
+  (759, 187),(662, 163)
+  (22684, 266),(22656, 181)
+  (24423, 255),(24360, 213)
+  (45989, 249),(45910, 222)
+  (11399, 377),(11360, 294)
+  (12162, 389),(12103, 309)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+                c               
+ -------------------------------
+  (50027, 49230),(49951, 49214)
+  (49980, 35004),(49937, 34963)
+  (49985, 6436),(49927, 6338)
+  (49999, 27218),(49908, 27176)
+  (49954, 1340),(49905, 1294)
+  (49944, 25163),(49902, 25153)
+  (49981, 34876),(49898, 34786)
+  (49957, 43390),(49897, 43384)
+  (49853, 18504),(49848, 18503)
+  (49902, 41752),(49818, 41746)
+  (49907, 30225),(49810, 30158)
+  (49843, 5175),(49808, 5145)
+  (49887, 24274),(49805, 24184)
+  (49847, 7128),(49798, 7067)
+  (49820, 7990),(49771, 7967)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+                c               
+ -------------------------------
+  (36311, 50073),(36258, 49987)
+  (30746, 50040),(30727, 49992)
+  (2168, 50012),(2108, 49914)
+  (21551, 49983),(21492, 49885)
+  (17954, 49975),(17865, 49915)
+  (3531, 49962),(3463, 49934)
+  (19128, 49932),(19112, 49849)
+  (31287, 49923),(31236, 49913)
+  (43925, 49912),(43888, 49878)
+  (29261, 49910),(29247, 49818)
+  (14913, 49873),(14849, 49836)
+  (20007, 49858),(19921, 49778)
+  (38266, 49852),(38233, 49844)
+  (37595, 49849),(37581, 49834)
+  (46151, 49848),(46058, 49830)
+ (15 rows)
+ 
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+             c             
+ --------------------------
+  (54, 38679, 3, 38602)
+  (83, 10271, 15, 10265)
+  (122, 46832, 64, 46762)
+  (154, 4019, 138, 3990)
+  (161, 24465, 107, 24374)
+  (162, 26040, 120, 25963)
+  (167, 17214, 92, 17184)
+  (207, 40886, 179, 40879)
+  (259, 1850, 175, 1820)
+  (270, 29508, 264, 29440)
+  (270, 32616, 226, 32607)
+  (288, 49588, 204, 49571)
+  (318, 31489, 235, 31404)
+  (326, 18837, 285, 18817)
+  (337, 455, 240, 359)
+ (15 rows)
+ 
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+               c               
+ ------------------------------
+  (30746, 50040, 30727, 49992)
+  (36311, 50073, 36258, 49987)
+  (3531, 49962, 3463, 49934)
+  (17954, 49975, 17865, 49915)
+  (2168, 50012, 2108, 49914)
+  (31287, 49923, 31236, 49913)
+  (21551, 49983, 21492, 49885)
+  (43925, 49912, 43888, 49878)
+  (19128, 49932, 19112, 49849)
+  (38266, 49852, 38233, 49844)
+  (14913, 49873, 14849, 49836)
+  (37595, 49849, 37581, 49834)
+  (46151, 49848, 46058, 49830)
+  (29261, 49910, 29247, 49818)
+  (19233, 49824, 19185, 49794)
+ (15 rows)
+ 
diff --git a/contrib/cube/expected/cube_3.out b/contrib/cube/expected/cube_3.out
new file mode 100644
index 2aa42be..f671128
*** a/contrib/cube/expected/cube_3.out
--- b/contrib/cube/expected/cube_3.out
*************** SELECT cube_size('(42,137)'::cube);
*** 1381,1386 ****
--- 1381,1531 ----
           0
  (1 row)
  
+ -- Test of distances
+ -- 
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+  cube_distance 
+ ---------------
+              5
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+  d_e 
+ -----
+    5
+ (1 row)
+ 
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+  distance_chebyshev 
+ --------------------
+                   4
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+  d_c 
+ -----
+    4
+ (1 row)
+ 
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+  distance_taxicab 
+ ------------------
+                 7
+ (1 row)
+ 
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+  d_t 
+ -----
+    7
+ (1 row)
+ 
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  cube_distance 
+ ---------------
+              0
+ (1 row)
+ 
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  distance_chebyshev 
+ --------------------
+                   0
+ (1 row)
+ 
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+  distance_taxicab 
+ ------------------
+                 0
+ (1 row)
+ 
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+  ?column? 
+ ----------
+        40
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+  ?column? 
+ ----------
+        60
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[10,20,30])->3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[10,20,30])->6;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[10,20,30])->-6;
+ ERROR:  Cube index out of bounds
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+  ?column? 
+ ----------
+        10
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+  ?column? 
+ ----------
+        20
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+  ?column? 
+ ----------
+        20
+ (1 row)
+ 
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+  ?column? 
+ ----------
+        30
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ ERROR:  Cube index out of bounds
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+  ?column? 
+ ----------
+        40
+ (1 row)
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ ERROR:  Cube index out of bounds
  -- Load some example data and build the index
  --
  CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 1407,1409 ****
--- 1552,1710 ----
   (2424, 160),(2424, 81)
  (5 rows)
  
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            |       dist       
+ -------------------------+------------------
+  (337, 455),(240, 359)   |                0
+  (759, 187),(662, 163)   |              162
+  (948, 1201),(907, 1156) | 772.000647668122
+  (1444, 403),(1346, 344) |              846
+  (369, 1457),(278, 1409) |              909
+ (5 rows)
+ 
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            | dist 
+ -------------------------+------
+  (337, 455),(240, 359)   |    0
+  (759, 187),(662, 163)   |  162
+  (948, 1201),(907, 1156) |  656
+  (1444, 403),(1346, 344) |  846
+  (369, 1457),(278, 1409) |  909
+ (5 rows)
+ 
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+             c            | dist 
+ -------------------------+------
+  (337, 455),(240, 359)   |    0
+  (759, 187),(662, 163)   |  162
+  (1444, 403),(1346, 344) |  846
+  (369, 1457),(278, 1409) |  909
+  (948, 1201),(907, 1156) | 1063
+ (5 rows)
+ 
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+              c             
+ ---------------------------
+  (54, 38679),(3, 38602)
+  (83, 10271),(15, 10265)
+  (122, 46832),(64, 46762)
+  (167, 17214),(92, 17184)
+  (161, 24465),(107, 24374)
+  (162, 26040),(120, 25963)
+  (154, 4019),(138, 3990)
+  (259, 1850),(175, 1820)
+  (207, 40886),(179, 40879)
+  (288, 49588),(204, 49571)
+  (270, 32616),(226, 32607)
+  (318, 31489),(235, 31404)
+  (337, 455),(240, 359)
+  (270, 29508),(264, 29440)
+  (369, 1457),(278, 1409)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+              c             
+ ---------------------------
+  (30333, 50),(30273, 6)
+  (43301, 75),(43227, 43)
+  (19650, 142),(19630, 51)
+  (2424, 160),(2424, 81)
+  (3449, 171),(3354, 108)
+  (18037, 155),(17941, 109)
+  (28511, 208),(28479, 114)
+  (19946, 217),(19941, 118)
+  (16906, 191),(16816, 139)
+  (759, 187),(662, 163)
+  (22684, 266),(22656, 181)
+  (24423, 255),(24360, 213)
+  (45989, 249),(45910, 222)
+  (11399, 377),(11360, 294)
+  (12162, 389),(12103, 309)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+                c               
+ -------------------------------
+  (50027, 49230),(49951, 49214)
+  (49980, 35004),(49937, 34963)
+  (49985, 6436),(49927, 6338)
+  (49999, 27218),(49908, 27176)
+  (49954, 1340),(49905, 1294)
+  (49944, 25163),(49902, 25153)
+  (49981, 34876),(49898, 34786)
+  (49957, 43390),(49897, 43384)
+  (49853, 18504),(49848, 18503)
+  (49902, 41752),(49818, 41746)
+  (49907, 30225),(49810, 30158)
+  (49843, 5175),(49808, 5145)
+  (49887, 24274),(49805, 24184)
+  (49847, 7128),(49798, 7067)
+  (49820, 7990),(49771, 7967)
+ (15 rows)
+ 
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+                c               
+ -------------------------------
+  (36311, 50073),(36258, 49987)
+  (30746, 50040),(30727, 49992)
+  (2168, 50012),(2108, 49914)
+  (21551, 49983),(21492, 49885)
+  (17954, 49975),(17865, 49915)
+  (3531, 49962),(3463, 49934)
+  (19128, 49932),(19112, 49849)
+  (31287, 49923),(31236, 49913)
+  (43925, 49912),(43888, 49878)
+  (29261, 49910),(29247, 49818)
+  (14913, 49873),(14849, 49836)
+  (20007, 49858),(19921, 49778)
+  (38266, 49852),(38233, 49844)
+  (37595, 49849),(37581, 49834)
+  (46151, 49848),(46058, 49830)
+ (15 rows)
+ 
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+             c             
+ --------------------------
+  (54, 38679, 3, 38602)
+  (83, 10271, 15, 10265)
+  (122, 46832, 64, 46762)
+  (154, 4019, 138, 3990)
+  (161, 24465, 107, 24374)
+  (162, 26040, 120, 25963)
+  (167, 17214, 92, 17184)
+  (207, 40886, 179, 40879)
+  (259, 1850, 175, 1820)
+  (270, 29508, 264, 29440)
+  (270, 32616, 226, 32607)
+  (288, 49588, 204, 49571)
+  (318, 31489, 235, 31404)
+  (326, 18837, 285, 18817)
+  (337, 455, 240, 359)
+ (15 rows)
+ 
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+               c               
+ ------------------------------
+  (30746, 50040, 30727, 49992)
+  (36311, 50073, 36258, 49987)
+  (3531, 49962, 3463, 49934)
+  (17954, 49975, 17865, 49915)
+  (2168, 50012, 2108, 49914)
+  (31287, 49923, 31236, 49913)
+  (21551, 49983, 21492, 49885)
+  (43925, 49912, 43888, 49878)
+  (19128, 49932, 19112, 49849)
+  (38266, 49852, 38233, 49844)
+  (14913, 49873, 14849, 49836)
+  (37595, 49849, 37581, 49834)
+  (46151, 49848, 46058, 49830)
+  (29261, 49910, 29247, 49818)
+  (19233, 49824, 19185, 49794)
+ (15 rows)
+ 
diff --git a/contrib/cube/sql/cube.sql b/contrib/cube/sql/cube.sql
new file mode 100644
index d58974c..c968a3e
*** a/contrib/cube/sql/cube.sql
--- b/contrib/cube/sql/cube.sql
*************** SELECT cube_inter('(1,2,3)'::cube, '(5,6
*** 325,330 ****
--- 325,365 ----
  SELECT cube_size('(4,8),(15,16)'::cube);
  SELECT cube_size('(42,137)'::cube);
  
+ -- Test of distances
+ -- 
+ SELECT cube_distance('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <-> '(4,5)'::cube as d_e;
+ SELECT distance_chebyshev('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <=> '(4,5)'::cube as d_c;
+ SELECT distance_taxicab('(1,1)'::cube, '(4,5)'::cube);
+ SELECT '(1,1)'::cube <#> '(4,5)'::cube as d_t;
+ -- zero for overlapping
+ SELECT cube_distance('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ SELECT distance_chebyshev('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ SELECT distance_taxicab('(2,2),(10,10)'::cube, '(0,0),(5,5)'::cube);
+ -- coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])->1;
+ SELECT cube(array[40,50,60], array[10,20,30])->1;
+ SELECT cube(array[10,20,30], array[40,50,60])->6;
+ SELECT cube(array[10,20,30], array[40,50,60])->0;
+ SELECT cube(array[10,20,30], array[40,50,60])->7;
+ SELECT cube(array[10,20,30], array[40,50,60])->-1;
+ SELECT cube(array[10,20,30], array[40,50,60])->-6;
+ SELECT cube(array[10,20,30])->3;
+ SELECT cube(array[10,20,30])->6;
+ SELECT cube(array[10,20,30])->-6;
+ -- "normalized" coordinate access
+ SELECT cube(array[10,20,30], array[40,50,60])~>1;
+ SELECT cube(array[40,50,60], array[10,20,30])~>1;
+ SELECT cube(array[10,20,30], array[40,50,60])~>2;
+ SELECT cube(array[40,50,60], array[10,20,30])~>2;
+ SELECT cube(array[10,20,30], array[40,50,60])~>3;
+ SELECT cube(array[40,50,60], array[10,20,30])~>3;
+ 
+ SELECT cube(array[40,50,60], array[10,20,30])~>0;
+ SELECT cube(array[40,50,60], array[10,20,30])~>4;
+ SELECT cube(array[40,50,60], array[10,20,30])~>(-1);
+ 
  -- Load some example data and build the index
  --
  CREATE TABLE test_cube (c cube);
*************** SELECT * FROM test_cube WHERE c && '(300
*** 336,338 ****
--- 371,392 ----
  
  -- Test sorting
  SELECT * FROM test_cube WHERE c && '(3000,1000),(0,0)' GROUP BY c ORDER BY c;
+ 
+ -- kNN with index
+ SELECT *, c <-> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <-> '(100, 100),(500, 500)'::cube LIMIT 5;
+ SELECT *, c <=> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <=> '(100, 100),(500, 500)'::cube LIMIT 5;
+ SELECT *, c <#> '(100, 100),(500, 500)'::cube as dist FROM test_cube ORDER BY c <#> '(100, 100),(500, 500)'::cube LIMIT 5;
+ 
+ -- kNN-based sorting
+ SELECT * FROM test_cube ORDER BY c~>1 LIMIT 15; -- ascending by 1st coordinate of lower left corner
+ SELECT * FROM test_cube ORDER BY c~>4 LIMIT 15; -- ascending by 2nd coordinate or upper right corner
+ SELECT * FROM test_cube ORDER BY c~>1 DESC LIMIT 15; -- descending by 1st coordinate of lower left corner
+ SELECT * FROM test_cube ORDER BY c~>4 DESC LIMIT 15; -- descending by 2nd coordinate or upper right corner
+ 
+ -- same thing for index with points
+ CREATE TABLE test_point(c cube);
+ INSERT INTO test_point(SELECT cube(array[c->1,c->2,c->3,c->4]) FROM test_cube);
+ CREATE INDEX ON test_point USING gist(c);
+ SELECT * FROM test_point ORDER BY c~>1, c~>2 LIMIT 15; -- ascending by 1st then by 2nd coordinate
+ SELECT * FROM test_point ORDER BY c~>4 DESC LIMIT 15; -- descending by 1st coordinate
+ 
diff --git a/doc/src/sgml/cube.sgml b/doc/src/sgml/cube.sgml
new file mode 100644
index 0a226ca..5a1cc8b
*** a/doc/src/sgml/cube.sgml
--- b/doc/src/sgml/cube.sgml
***************
*** 71,80 ****
    </table>
  
    <para>
!    It does not matter which order the opposite corners of a cube are
!    entered in.  The <type>cube</> functions
!    automatically swap values if needed to create a uniform
!    <quote>lower left &mdash; upper right</> internal representation.
    </para>
  
    <para>
--- 71,77 ----
    </table>
  
    <para>
!    When corners coincide cube stores only one corner along with a special flag in order to reduce size wasted.
    </para>
  
    <para>
***************
*** 131,136 ****
--- 128,146 ----
        <entry><literal>a &lt;@ b</></entry>
        <entry>The cube a is contained in the cube b.</entry>
       </row>
+ 
+      <row>
+       <entry><literal>a -&gt; n</></entry>
+       <entry>Get n-th coordinate of cube.</entry>
+      </row>
+ 
+      <row>
+       <entry><literal>a ~&gt; n</></entry>
+       <entry>
+         Get n-th coordinate in 'normalized' cube representation. Noramlization
+         means coordinate rearrangement to form (lower left, upper right).
+       </entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
***************
*** 144,149 ****
--- 154,240 ----
    </para>
  
    <para>
+    GiST index can be used to retrieve nearest neighbours via several metric
+    operators. As always any of them can be used as ordinary function.
+   </para>
+ 
+   <table id="cube-gistknn-operators">
+    <title>Cube GiST-kNN Operators</title>
+    <tgroup cols="2">
+     <thead>
+      <row>
+       <entry>Operator</entry>
+       <entry>Description</entry>
+      </row>
+     </thead>
+     <tbody>
+      <row>
+       <entry><literal>a &lt;-&gt; b</></entry>
+       <entry>Euclidean distance between a and b</entry>
+      </row>
+ 
+      <row>
+       <entry><literal>a &lt;#&gt; b</></entry>
+       <entry>Taxicab (L-1 metric) distance between a and b</entry>
+      </row>
+ 
+      <row>
+       <entry><literal>a &lt;=&gt; b</></entry>
+       <entry>Chebyshev (L-inf metric) distance between a and b</entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+ 
+   <para>
+    Selection of nearing neigbours can be done in the following way:
+   </para>
+ <programlisting>
+ SELECT c FROM test 
+ ORDER BY cube(array[0.5,0.5,0.5])<->c 
+ LIMIT 1;
+ </programlisting>
+ 
+ 
+   <para>
+    Also kNN framework allows us to cheat with metrics in order to get results
+    sorted by selected coodinate directly from the index without extra sorting
+    step. That technique significantly faster on small values of LIMIT, however
+    with bigger values of LIMIT planner will switch automatically to standart
+    index scan and sort.
+    That behavior can be achieved using coordinate operator
+    (cube c)~&gt;(int offset).
+   </para>
+ <programlisting>
+ => select cube(array[0.41,0.42,0.43])~>2 as coord;
+  coord 
+ -------
+   0.42
+ (1 row)
+ </programlisting>
+ 
+   <para>
+    So using that operator as kNN metric we can obtain cubes sorted by it's
+    coordinate.
+   </para>
+   <para>
+    To get cubes ordered by first coordinate of lower left corner ascending
+    one can use the following query:
+   </para>
+ <programlisting>
+ SELECT c FROM test ORDER BY c~>1 LIMIT 5;
+ </programlisting>
+   <para>
+    And to get cubes descending by first coordinate of upper right corner
+    of 2d-cube:
+   </para>
+ <programlisting>
+ SELECT c FROM test ORDER BY c~>3 DESC LIMIT 5;
+ </programlisting>
+ 
+ 
+ 
+   <para>
     The standard B-tree operators are also provided, for example
  
     <informaltable>
