LCOV - differential code coverage report
Current view: top level - contrib/cube - cube.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 88.1 % 817 720 22 40 35 7 375 3 335 55 361 2
Current Date: 2023-04-08 15:15:32 Functions: 93.7 % 95 89 6 45 1 43 6 45
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /******************************************************************************
       2                 :   contrib/cube/cube.c
       3                 : 
       4                 :   This file contains routines that can be bound to a Postgres backend and
       5                 :   called by the backend in the process of processing queries.  The calling
       6                 :   format for these routines is dictated by Postgres architecture.
       7                 : ******************************************************************************/
       8                 : 
       9                 : #include "postgres.h"
      10                 : 
      11                 : #include <math.h>
      12                 : 
      13                 : #include "access/gist.h"
      14                 : #include "access/stratnum.h"
      15                 : #include "cubedata.h"
      16                 : #include "libpq/pqformat.h"
      17                 : #include "utils/array.h"
      18                 : #include "utils/float.h"
      19                 : 
      20 CBC           3 : PG_MODULE_MAGIC;
      21                 : 
      22                 : /*
      23                 :  * Taken from the intarray contrib header
      24                 :  */
      25                 : #define ARRPTR(x)  ( (double *) ARR_DATA_PTR(x) )
      26                 : #define ARRNELEMS(x)  ArrayGetNItems( ARR_NDIM(x), ARR_DIMS(x))
      27                 : 
      28                 : /*
      29                 : ** Input/Output routines
      30                 : */
      31               6 : PG_FUNCTION_INFO_V1(cube_in);
      32               4 : PG_FUNCTION_INFO_V1(cube_a_f8_f8);
      33               4 : PG_FUNCTION_INFO_V1(cube_a_f8);
      34               5 : PG_FUNCTION_INFO_V1(cube_out);
      35               3 : PG_FUNCTION_INFO_V1(cube_send);
      36               3 : PG_FUNCTION_INFO_V1(cube_recv);
      37               5 : PG_FUNCTION_INFO_V1(cube_f8);
      38               4 : PG_FUNCTION_INFO_V1(cube_f8_f8);
      39               5 : PG_FUNCTION_INFO_V1(cube_c_f8);
      40               4 : PG_FUNCTION_INFO_V1(cube_c_f8_f8);
      41               5 : PG_FUNCTION_INFO_V1(cube_dim);
      42               5 : PG_FUNCTION_INFO_V1(cube_ll_coord);
      43               5 : PG_FUNCTION_INFO_V1(cube_ur_coord);
      44               4 : PG_FUNCTION_INFO_V1(cube_coord);
      45               4 : PG_FUNCTION_INFO_V1(cube_coord_llur);
      46               4 : PG_FUNCTION_INFO_V1(cube_subset);
      47                 : 
      48                 : /*
      49                 : ** GiST support methods
      50                 : */
      51                 : 
      52               4 : PG_FUNCTION_INFO_V1(g_cube_consistent);
      53               3 : PG_FUNCTION_INFO_V1(g_cube_compress);
      54               3 : PG_FUNCTION_INFO_V1(g_cube_decompress);
      55               4 : PG_FUNCTION_INFO_V1(g_cube_penalty);
      56               4 : PG_FUNCTION_INFO_V1(g_cube_picksplit);
      57               4 : PG_FUNCTION_INFO_V1(g_cube_union);
      58               4 : PG_FUNCTION_INFO_V1(g_cube_same);
      59               4 : PG_FUNCTION_INFO_V1(g_cube_distance);
      60                 : 
      61                 : /*
      62                 : ** B-tree support functions
      63                 : */
      64               4 : PG_FUNCTION_INFO_V1(cube_eq);
      65               4 : PG_FUNCTION_INFO_V1(cube_ne);
      66               4 : PG_FUNCTION_INFO_V1(cube_lt);
      67               4 : PG_FUNCTION_INFO_V1(cube_gt);
      68               3 : PG_FUNCTION_INFO_V1(cube_le);
      69               3 : PG_FUNCTION_INFO_V1(cube_ge);
      70               4 : PG_FUNCTION_INFO_V1(cube_cmp);
      71                 : 
      72                 : /*
      73                 : ** R-tree support functions
      74                 : */
      75                 : 
      76               5 : PG_FUNCTION_INFO_V1(cube_contains);
      77               4 : PG_FUNCTION_INFO_V1(cube_contained);
      78               4 : PG_FUNCTION_INFO_V1(cube_overlap);
      79               4 : PG_FUNCTION_INFO_V1(cube_union);
      80               4 : PG_FUNCTION_INFO_V1(cube_inter);
      81               4 : PG_FUNCTION_INFO_V1(cube_size);
      82                 : 
      83                 : /*
      84                 : ** miscellaneous
      85                 : */
      86               4 : PG_FUNCTION_INFO_V1(distance_taxicab);
      87               5 : PG_FUNCTION_INFO_V1(cube_distance);
      88               4 : PG_FUNCTION_INFO_V1(distance_chebyshev);
      89               5 : PG_FUNCTION_INFO_V1(cube_is_point);
      90               5 : PG_FUNCTION_INFO_V1(cube_enlarge);
      91                 : 
      92                 : /*
      93                 : ** For internal use only
      94                 : */
      95                 : int32       cube_cmp_v0(NDBOX *a, NDBOX *b);
      96                 : bool        cube_contains_v0(NDBOX *a, NDBOX *b);
      97                 : bool        cube_overlap_v0(NDBOX *a, NDBOX *b);
      98                 : NDBOX      *cube_union_v0(NDBOX *a, NDBOX *b);
      99                 : void        rt_cube_size(NDBOX *a, double *size);
     100                 : NDBOX      *g_cube_binary_union(NDBOX *r1, NDBOX *r2, int *sizep);
     101                 : bool        g_cube_leaf_consistent(NDBOX *key, NDBOX *query, StrategyNumber strategy);
     102                 : bool        g_cube_internal_consistent(NDBOX *key, NDBOX *query, StrategyNumber strategy);
     103                 : 
     104                 : /*
     105                 : ** Auxiliary functions
     106                 : */
     107                 : static double distance_1D(double a1, double a2, double b1, double b2);
     108                 : static bool cube_is_point_internal(NDBOX *cube);
     109                 : 
     110                 : 
     111                 : /*****************************************************************************
     112                 :  * Input/Output functions
     113                 :  *****************************************************************************/
     114                 : 
     115                 : /* NdBox = [(lowerleft),(upperright)] */
     116                 : /* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */
     117                 : Datum
     118            3435 : cube_in(PG_FUNCTION_ARGS)
     119                 : {
     120            3435 :     char       *str = PG_GETARG_CSTRING(0);
     121                 :     NDBOX      *result;
     122                 :     Size        scanbuflen;
     123                 : 
     124 GNC        3435 :     cube_scanner_init(str, &scanbuflen);
     125 ECB             : 
     126 GNC        3435 :     cube_yyparse(&result, scanbuflen, fcinfo->context);
     127                 : 
     128                 :     /* We might as well run this even on failure. */
     129 GIC        3405 :     cube_scanner_finish();
     130 ECB             : 
     131 GIC        3405 :     PG_RETURN_NDBOX_P(result);
     132 ECB             : }
     133                 : 
     134                 : 
     135                 : /*
     136                 : ** Allows the construction of a cube from 2 float[]'s
     137                 : */
     138                 : Datum
     139 GIC          22 : cube_a_f8_f8(PG_FUNCTION_ARGS)
     140 ECB             : {
     141 GIC          22 :     ArrayType  *ur = PG_GETARG_ARRAYTYPE_P(0);
     142 CBC          22 :     ArrayType  *ll = PG_GETARG_ARRAYTYPE_P(1);
     143 ECB             :     NDBOX      *result;
     144                 :     int         i;
     145                 :     int         dim;
     146                 :     int         size;
     147                 :     bool        point;
     148                 :     double     *dur,
     149                 :                *dll;
     150                 : 
     151 GIC          22 :     if (array_contains_nulls(ur) || array_contains_nulls(ll))
     152 LBC           0 :         ereport(ERROR,
     153 EUB             :                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
     154                 :                  errmsg("cannot work with arrays containing NULLs")));
     155                 : 
     156 GIC          22 :     dim = ARRNELEMS(ur);
     157 CBC          22 :     if (dim > CUBE_MAX_DIM)
     158               1 :         ereport(ERROR,
     159 ECB             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     160                 :                  errmsg("can't extend cube"),
     161                 :                  errdetail("A cube cannot have more than %d dimensions.",
     162                 :                            CUBE_MAX_DIM)));
     163                 : 
     164 GIC          21 :     if (ARRNELEMS(ll) != dim)
     165 CBC           1 :         ereport(ERROR,
     166 ECB             :                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
     167                 :                  errmsg("UR and LL arrays must be of same length")));
     168                 : 
     169 GIC          20 :     dur = ARRPTR(ur);
     170 CBC          20 :     dll = ARRPTR(ll);
     171 ECB             : 
     172                 :     /* Check if it's a point */
     173 GIC          20 :     point = true;
     174 CBC         223 :     for (i = 0; i < dim; i++)
     175 ECB             :     {
     176 GIC         220 :         if (dur[i] != dll[i])
     177 ECB             :         {
     178 GIC          17 :             point = false;
     179 CBC          17 :             break;
     180 ECB             :         }
     181                 :     }
     182                 : 
     183 GIC          20 :     size = point ? POINT_SIZE(dim) : CUBE_SIZE(dim);
     184 CBC          20 :     result = (NDBOX *) palloc0(size);
     185              20 :     SET_VARSIZE(result, size);
     186              20 :     SET_DIM(result, dim);
     187 ECB             : 
     188 GIC         274 :     for (i = 0; i < dim; i++)
     189 CBC         254 :         result->x[i] = dur[i];
     190 ECB             : 
     191 GIC          20 :     if (!point)
     192 ECB             :     {
     193 GIC          68 :         for (i = 0; i < dim; i++)
     194 CBC          51 :             result->x[i + dim] = dll[i];
     195 ECB             :     }
     196                 :     else
     197 GIC           3 :         SET_POINT_BIT(result);
     198 ECB             : 
     199 GIC          20 :     PG_RETURN_NDBOX_P(result);
     200 ECB             : }
     201                 : 
     202                 : /*
     203                 : ** Allows the construction of a zero-volume cube from a float[]
     204                 : */
     205                 : Datum
     206 GIC           8 : cube_a_f8(PG_FUNCTION_ARGS)
     207 ECB             : {
     208 GIC           8 :     ArrayType  *ur = PG_GETARG_ARRAYTYPE_P(0);
     209 ECB             :     NDBOX      *result;
     210                 :     int         i;
     211                 :     int         dim;
     212                 :     int         size;
     213                 :     double     *dur;
     214                 : 
     215 GIC           8 :     if (array_contains_nulls(ur))
     216 LBC           0 :         ereport(ERROR,
     217 EUB             :                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
     218                 :                  errmsg("cannot work with arrays containing NULLs")));
     219                 : 
     220 GIC           8 :     dim = ARRNELEMS(ur);
     221 CBC           8 :     if (dim > CUBE_MAX_DIM)
     222               1 :         ereport(ERROR,
     223 ECB             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     224                 :                  errmsg("array is too long"),
     225                 :                  errdetail("A cube cannot have more than %d dimensions.",
     226                 :                            CUBE_MAX_DIM)));
     227                 : 
     228 GIC           7 :     dur = ARRPTR(ur);
     229 ECB             : 
     230 GIC           7 :     size = POINT_SIZE(dim);
     231 CBC           7 :     result = (NDBOX *) palloc0(size);
     232               7 :     SET_VARSIZE(result, size);
     233               7 :     SET_DIM(result, dim);
     234               7 :     SET_POINT_BIT(result);
     235 ECB             : 
     236 GIC         223 :     for (i = 0; i < dim; i++)
     237 CBC         216 :         result->x[i] = dur[i];
     238 ECB             : 
     239 GIC           7 :     PG_RETURN_NDBOX_P(result);
     240 ECB             : }
     241                 : 
     242                 : Datum
     243 GIC           6 : cube_subset(PG_FUNCTION_ARGS)
     244 ECB             : {
     245 GIC           6 :     NDBOX      *c = PG_GETARG_NDBOX_P(0);
     246 CBC           6 :     ArrayType  *idx = PG_GETARG_ARRAYTYPE_P(1);
     247 ECB             :     NDBOX      *result;
     248                 :     int         size,
     249                 :                 dim,
     250                 :                 i;
     251                 :     int        *dx;
     252                 : 
     253 GIC           6 :     if (array_contains_nulls(idx))
     254 LBC           0 :         ereport(ERROR,
     255 EUB             :                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
     256                 :                  errmsg("cannot work with arrays containing NULLs")));
     257                 : 
     258 GIC           6 :     dx = (int32 *) ARR_DATA_PTR(idx);
     259 ECB             : 
     260 GIC           6 :     dim = ARRNELEMS(idx);
     261 CBC           6 :     if (dim > CUBE_MAX_DIM)
     262               1 :         ereport(ERROR,
     263 ECB             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     264                 :                  errmsg("array is too long"),
     265                 :                  errdetail("A cube cannot have more than %d dimensions.",
     266                 :                            CUBE_MAX_DIM)));
     267                 : 
     268 GIC           5 :     size = IS_POINT(c) ? POINT_SIZE(dim) : CUBE_SIZE(dim);
     269 CBC           5 :     result = (NDBOX *) palloc0(size);
     270               5 :     SET_VARSIZE(result, size);
     271               5 :     SET_DIM(result, dim);
     272 ECB             : 
     273 GIC           5 :     if (IS_POINT(c))
     274 CBC           3 :         SET_POINT_BIT(result);
     275 ECB             : 
     276 GIC         113 :     for (i = 0; i < dim; i++)
     277 ECB             :     {
     278 GIC         110 :         if ((dx[i] <= 0) || (dx[i] > DIM(c)))
     279 CBC           2 :             ereport(ERROR,
     280 ECB             :                     (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
     281                 :                      errmsg("Index out of bounds")));
     282 GIC         108 :         result->x[i] = c->x[dx[i] - 1];
     283 CBC         108 :         if (!IS_POINT(c))
     284               4 :             result->x[i + dim] = c->x[dx[i] + DIM(c) - 1];
     285 ECB             :     }
     286                 : 
     287 GIC           3 :     PG_FREE_IF_COPY(c, 0);
     288 CBC           3 :     PG_RETURN_NDBOX_P(result);
     289 ECB             : }
     290                 : 
     291                 : Datum
     292 GIC         389 : cube_out(PG_FUNCTION_ARGS)
     293 ECB             : {
     294 GIC         389 :     NDBOX      *cube = PG_GETARG_NDBOX_P(0);
     295 ECB             :     StringInfoData buf;
     296 GIC         389 :     int         dim = DIM(cube);
     297 ECB             :     int         i;
     298                 : 
     299 GIC         389 :     initStringInfo(&buf);
     300 ECB             : 
     301 GIC         389 :     appendStringInfoChar(&buf, '(');
     302 CBC        1435 :     for (i = 0; i < dim; i++)
     303 ECB             :     {
     304 GIC        1046 :         if (i > 0)
     305 CBC         659 :             appendStringInfoString(&buf, ", ");
     306            1046 :         appendStringInfoString(&buf, float8out_internal(LL_COORD(cube, i)));
     307 ECB             :     }
     308 GIC         389 :     appendStringInfoChar(&buf, ')');
     309 ECB             : 
     310 GIC         389 :     if (!cube_is_point_internal(cube))
     311 ECB             :     {
     312 GIC         290 :         appendStringInfoString(&buf, ",(");
     313 CBC         880 :         for (i = 0; i < dim; i++)
     314 ECB             :         {
     315 GIC         590 :             if (i > 0)
     316 CBC         300 :                 appendStringInfoString(&buf, ", ");
     317             590 :             appendStringInfoString(&buf, float8out_internal(UR_COORD(cube, i)));
     318 ECB             :         }
     319 GIC         290 :         appendStringInfoChar(&buf, ')');
     320 ECB             :     }
     321                 : 
     322 GIC         389 :     PG_FREE_IF_COPY(cube, 0);
     323 CBC         389 :     PG_RETURN_CSTRING(buf.data);
     324 ECB             : }
     325                 : 
     326                 : /*
     327                 :  * cube_send - a binary output handler for cube type
     328                 :  */
     329                 : Datum
     330 UIC           0 : cube_send(PG_FUNCTION_ARGS)
     331 EUB             : {
     332 UIC           0 :     NDBOX      *cube = PG_GETARG_NDBOX_P(0);
     333 EUB             :     StringInfoData buf;
     334                 :     int32       i,
     335 UIC           0 :                 nitems = DIM(cube);
     336 EUB             : 
     337 UIC           0 :     pq_begintypsend(&buf);
     338 UBC           0 :     pq_sendint32(&buf, cube->header);
     339               0 :     if (!IS_POINT(cube))
     340               0 :         nitems += nitems;
     341 EUB             :     /* for symmetry with cube_recv, we don't use LL_COORD/UR_COORD here */
     342 UIC           0 :     for (i = 0; i < nitems; i++)
     343 UBC           0 :         pq_sendfloat8(&buf, cube->x[i]);
     344 EUB             : 
     345 UIC           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     346 EUB             : }
     347                 : 
     348                 : /*
     349                 :  * cube_recv - a binary input handler for cube type
     350                 :  */
     351                 : Datum
     352 UIC           0 : cube_recv(PG_FUNCTION_ARGS)
     353 EUB             : {
     354 UIC           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     355 EUB             :     int32       header;
     356                 :     int32       i,
     357                 :                 nitems;
     358                 :     NDBOX      *cube;
     359                 : 
     360 UIC           0 :     header = pq_getmsgint(buf, sizeof(int32));
     361 UBC           0 :     nitems = (header & DIM_MASK);
     362               0 :     if (nitems > CUBE_MAX_DIM)
     363               0 :         ereport(ERROR,
     364 EUB             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
     365                 :                  errmsg("cube dimension is too large"),
     366                 :                  errdetail("A cube cannot have more than %d dimensions.",
     367                 :                            CUBE_MAX_DIM)));
     368 UIC           0 :     if ((header & POINT_BIT) == 0)
     369 UBC           0 :         nitems += nitems;
     370               0 :     cube = palloc(offsetof(NDBOX, x) + sizeof(double) * nitems);
     371               0 :     SET_VARSIZE(cube, offsetof(NDBOX, x) + sizeof(double) * nitems);
     372               0 :     cube->header = header;
     373               0 :     for (i = 0; i < nitems; i++)
     374               0 :         cube->x[i] = pq_getmsgfloat8(buf);
     375 EUB             : 
     376 UIC           0 :     PG_RETURN_NDBOX_P(cube);
     377 EUB             : }
     378                 : 
     379                 : 
     380                 : /*****************************************************************************
     381                 :  *                         GiST functions
     382                 :  *****************************************************************************/
     383                 : 
     384                 : /*
     385                 : ** The GiST Consistent method for boxes
     386                 : ** Should return false if for all data items x below entry,
     387                 : ** the predicate x op query == false, where op is the oper
     388                 : ** corresponding to strategy in the pg_amop table.
     389                 : */
     390                 : Datum
     391 GIC         252 : g_cube_consistent(PG_FUNCTION_ARGS)
     392 ECB             : {
     393 GIC         252 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     394 CBC         252 :     NDBOX      *query = PG_GETARG_NDBOX_P(1);
     395             252 :     StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
     396 ECB             : 
     397                 :     /* Oid      subtype = PG_GETARG_OID(3); */
     398 GIC         252 :     bool       *recheck = (bool *) PG_GETARG_POINTER(4);
     399 ECB             :     bool        res;
     400                 : 
     401                 :     /* All cases served by this function are exact */
     402 GIC         252 :     *recheck = false;
     403 ECB             : 
     404                 :     /*
     405                 :      * if entry is not leaf, use g_cube_internal_consistent, else use
     406                 :      * g_cube_leaf_consistent
     407                 :      */
     408 GIC         252 :     if (GIST_LEAF(entry))
     409 CBC         144 :         res = g_cube_leaf_consistent(DatumGetNDBOXP(entry->key),
     410 ECB             :                                      query, strategy);
     411                 :     else
     412 GIC         108 :         res = g_cube_internal_consistent(DatumGetNDBOXP(entry->key),
     413 ECB             :                                          query, strategy);
     414                 : 
     415 GIC         252 :     PG_FREE_IF_COPY(query, 1);
     416 CBC         252 :     PG_RETURN_BOOL(res);
     417 ECB             : }
     418                 : 
     419                 : 
     420                 : /*
     421                 : ** The GiST Union method for boxes
     422                 : ** returns the minimal bounding box that encloses all the entries in entryvec
     423                 : */
     424                 : Datum
     425 GIC        2962 : g_cube_union(PG_FUNCTION_ARGS)
     426 ECB             : {
     427 GIC        2962 :     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     428 CBC        2962 :     int        *sizep = (int *) PG_GETARG_POINTER(1);
     429            2962 :     NDBOX      *out = (NDBOX *) NULL;
     430 ECB             :     NDBOX      *tmp;
     431                 :     int         i;
     432                 : 
     433 GIC        2962 :     tmp = DatumGetNDBOXP(entryvec->vector[0].key);
     434 ECB             : 
     435                 :     /*
     436                 :      * sizep = sizeof(NDBOX); -- NDBOX has variable size
     437                 :      */
     438 GIC        2962 :     *sizep = VARSIZE(tmp);
     439 ECB             : 
     440 GIC        5924 :     for (i = 1; i < entryvec->n; i++)
     441 ECB             :     {
     442 GIC        2962 :         out = g_cube_binary_union(tmp,
     443 CBC        2962 :                                   DatumGetNDBOXP(entryvec->vector[i].key),
     444 ECB             :                                   sizep);
     445 GIC        2962 :         tmp = out;
     446 ECB             :     }
     447                 : 
     448 GIC        2962 :     PG_RETURN_POINTER(out);
     449 ECB             : }
     450                 : 
     451                 : /*
     452                 : ** GiST Compress and Decompress methods for boxes
     453                 : ** do not do anything.
     454                 : */
     455                 : 
     456                 : Datum
     457 UIC           0 : g_cube_compress(PG_FUNCTION_ARGS)
     458 EUB             : {
     459 UIC           0 :     PG_RETURN_DATUM(PG_GETARG_DATUM(0));
     460 EUB             : }
     461                 : 
     462                 : Datum
     463 UIC           0 : g_cube_decompress(PG_FUNCTION_ARGS)
     464 EUB             : {
     465 UIC           0 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
     466 UBC           0 :     NDBOX      *key = DatumGetNDBOXP(entry->key);
     467 EUB             : 
     468 UIC           0 :     if (key != DatumGetNDBOXP(entry->key))
     469 EUB             :     {
     470 UIC           0 :         GISTENTRY  *retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
     471 EUB             : 
     472 UIC           0 :         gistentryinit(*retval, PointerGetDatum(key),
     473 EUB             :                       entry->rel, entry->page,
     474                 :                       entry->offset, false);
     475 UIC           0 :         PG_RETURN_POINTER(retval);
     476 EUB             :     }
     477 UIC           0 :     PG_RETURN_POINTER(entry);
     478 EUB             : }
     479                 : 
     480                 : 
     481                 : /*
     482                 : ** The GiST Penalty method for boxes
     483                 : ** As in the R-tree paper, we use change in area as our penalty metric
     484                 : */
     485                 : Datum
     486 GIC       43363 : g_cube_penalty(PG_FUNCTION_ARGS)
     487 ECB             : {
     488 GIC       43363 :     GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
     489 CBC       43363 :     GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
     490           43363 :     float      *result = (float *) PG_GETARG_POINTER(2);
     491 ECB             :     NDBOX      *ud;
     492                 :     double      tmp1,
     493                 :                 tmp2;
     494                 : 
     495 GIC       43363 :     ud = cube_union_v0(DatumGetNDBOXP(origentry->key),
     496 CBC       43363 :                        DatumGetNDBOXP(newentry->key));
     497           43363 :     rt_cube_size(ud, &tmp1);
     498           43363 :     rt_cube_size(DatumGetNDBOXP(origentry->key), &tmp2);
     499           43363 :     *result = (float) (tmp1 - tmp2);
     500 ECB             : 
     501 GIC       43363 :     PG_RETURN_FLOAT8(*result);
     502 ECB             : }
     503                 : 
     504                 : 
     505                 : 
     506                 : /*
     507                 : ** The GiST PickSplit method for boxes
     508                 : ** We use Guttman's poly time split algorithm
     509                 : */
     510                 : Datum
     511 GIC          35 : g_cube_picksplit(PG_FUNCTION_ARGS)
     512 ECB             : {
     513 GIC          35 :     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
     514 CBC          35 :     GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
     515 ECB             :     OffsetNumber i,
     516                 :                 j;
     517                 :     NDBOX      *datum_alpha,
     518                 :                *datum_beta;
     519                 :     NDBOX      *datum_l,
     520                 :                *datum_r;
     521                 :     NDBOX      *union_d,
     522                 :                *union_dl,
     523                 :                *union_dr;
     524                 :     NDBOX      *inter_d;
     525                 :     bool        firsttime;
     526                 :     double      size_alpha,
     527                 :                 size_beta,
     528                 :                 size_union,
     529                 :                 size_inter;
     530                 :     double      size_waste,
     531                 :                 waste;
     532                 :     double      size_l,
     533                 :                 size_r;
     534                 :     int         nbytes;
     535 GIC          35 :     OffsetNumber seed_1 = 1,
     536 CBC          35 :                 seed_2 = 2;
     537 ECB             :     OffsetNumber *left,
     538                 :                *right;
     539                 :     OffsetNumber maxoff;
     540                 : 
     541 GIC          35 :     maxoff = entryvec->n - 2;
     542 CBC          35 :     nbytes = (maxoff + 2) * sizeof(OffsetNumber);
     543              35 :     v->spl_left = (OffsetNumber *) palloc(nbytes);
     544              35 :     v->spl_right = (OffsetNumber *) palloc(nbytes);
     545 ECB             : 
     546 GIC          35 :     firsttime = true;
     547 CBC          35 :     waste = 0.0;
     548 ECB             : 
     549 GIC        4900 :     for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i))
     550 ECB             :     {
     551 GIC        4865 :         datum_alpha = DatumGetNDBOXP(entryvec->vector[i].key);
     552 CBC      345415 :         for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j))
     553 ECB             :         {
     554 GIC      340550 :             datum_beta = DatumGetNDBOXP(entryvec->vector[j].key);
     555 ECB             : 
     556                 :             /* compute the wasted space by unioning these guys */
     557                 :             /* size_waste = size_union - size_inter; */
     558 GIC      340550 :             union_d = cube_union_v0(datum_alpha, datum_beta);
     559 CBC      340550 :             rt_cube_size(union_d, &size_union);
     560          340550 :             inter_d = DatumGetNDBOXP(DirectFunctionCall2(cube_inter,
     561 ECB             :                                                          entryvec->vector[i].key,
     562                 :                                                          entryvec->vector[j].key));
     563 GIC      340550 :             rt_cube_size(inter_d, &size_inter);
     564 CBC      340550 :             size_waste = size_union - size_inter;
     565 ECB             : 
     566                 :             /*
     567                 :              * are these a more promising split than what we've already seen?
     568                 :              */
     569                 : 
     570 GIC      340550 :             if (size_waste > waste || firsttime)
     571 ECB             :             {
     572 GIC         473 :                 waste = size_waste;
     573 CBC         473 :                 seed_1 = i;
     574             473 :                 seed_2 = j;
     575             473 :                 firsttime = false;
     576 ECB             :             }
     577                 :         }
     578                 :     }
     579                 : 
     580 GIC          35 :     left = v->spl_left;
     581 CBC          35 :     v->spl_nleft = 0;
     582              35 :     right = v->spl_right;
     583              35 :     v->spl_nright = 0;
     584 ECB             : 
     585 GIC          35 :     datum_alpha = DatumGetNDBOXP(entryvec->vector[seed_1].key);
     586 CBC          35 :     datum_l = cube_union_v0(datum_alpha, datum_alpha);
     587              35 :     rt_cube_size(datum_l, &size_l);
     588              35 :     datum_beta = DatumGetNDBOXP(entryvec->vector[seed_2].key);
     589              35 :     datum_r = cube_union_v0(datum_beta, datum_beta);
     590              35 :     rt_cube_size(datum_r, &size_r);
     591 ECB             : 
     592                 :     /*
     593                 :      * Now split up the regions between the two seeds.  An important property
     594                 :      * of this split algorithm is that the split vector v has the indices of
     595                 :      * items to be split in order in its left and right vectors.  We exploit
     596                 :      * this property by doing a merge in the code that actually splits the
     597                 :      * page.
     598                 :      *
     599                 :      * For efficiency, we also place the new index tuple in this loop. This is
     600                 :      * handled at the very end, when we have placed all the existing tuples
     601                 :      * and i == maxoff + 1.
     602                 :      */
     603                 : 
     604 GIC          35 :     maxoff = OffsetNumberNext(maxoff);
     605 CBC        4970 :     for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
     606 ECB             :     {
     607                 :         /*
     608                 :          * If we've already decided where to place this item, just put it on
     609                 :          * the right list.  Otherwise, we need to figure out which page needs
     610                 :          * the least enlargement in order to store the item.
     611                 :          */
     612                 : 
     613 GIC        4935 :         if (i == seed_1)
     614 ECB             :         {
     615 GIC          35 :             *left++ = i;
     616 CBC          35 :             v->spl_nleft++;
     617              35 :             continue;
     618 ECB             :         }
     619 GIC        4900 :         else if (i == seed_2)
     620 ECB             :         {
     621 GIC          35 :             *right++ = i;
     622 CBC          35 :             v->spl_nright++;
     623              35 :             continue;
     624 ECB             :         }
     625                 : 
     626                 :         /* okay, which page needs least enlargement? */
     627 GIC        4865 :         datum_alpha = DatumGetNDBOXP(entryvec->vector[i].key);
     628 CBC        4865 :         union_dl = cube_union_v0(datum_l, datum_alpha);
     629            4865 :         union_dr = cube_union_v0(datum_r, datum_alpha);
     630            4865 :         rt_cube_size(union_dl, &size_alpha);
     631            4865 :         rt_cube_size(union_dr, &size_beta);
     632 ECB             : 
     633                 :         /* pick which page to add it to */
     634 GIC        4865 :         if (size_alpha - size_l < size_beta - size_r)
     635 ECB             :         {
     636 GIC        2358 :             datum_l = union_dl;
     637 CBC        2358 :             size_l = size_alpha;
     638            2358 :             *left++ = i;
     639            2358 :             v->spl_nleft++;
     640 ECB             :         }
     641                 :         else
     642                 :         {
     643 GIC        2507 :             datum_r = union_dr;
     644 CBC        2507 :             size_r = size_beta;
     645            2507 :             *right++ = i;
     646            2507 :             v->spl_nright++;
     647 ECB             :         }
     648                 :     }
     649 GIC          35 :     *left = *right = FirstOffsetNumber; /* sentinel value */
     650 ECB             : 
     651 GIC          35 :     v->spl_ldatum = PointerGetDatum(datum_l);
     652 CBC          35 :     v->spl_rdatum = PointerGetDatum(datum_r);
     653 ECB             : 
     654 GIC          35 :     PG_RETURN_POINTER(v);
     655 ECB             : }
     656                 : 
     657                 : /*
     658                 : ** Equality method
     659                 : */
     660                 : Datum
     661 GIC        2962 : g_cube_same(PG_FUNCTION_ARGS)
     662 ECB             : {
     663 GIC        2962 :     NDBOX      *b1 = PG_GETARG_NDBOX_P(0);
     664 CBC        2962 :     NDBOX      *b2 = PG_GETARG_NDBOX_P(1);
     665            2962 :     bool       *result = (bool *) PG_GETARG_POINTER(2);
     666 ECB             : 
     667 GIC        2962 :     if (cube_cmp_v0(b1, b2) == 0)
     668 CBC        2808 :         *result = true;
     669 ECB             :     else
     670 GIC         154 :         *result = false;
     671 ECB             : 
     672 GIC        2962 :     PG_RETURN_NDBOX_P(result);
     673 ECB             : }
     674                 : 
     675                 : /*
     676                 : ** SUPPORT ROUTINES
     677                 : */
     678                 : bool
     679 GIC         144 : g_cube_leaf_consistent(NDBOX *key,
     680 ECB             :                        NDBOX *query,
     681                 :                        StrategyNumber strategy)
     682                 : {
     683                 :     bool        retval;
     684                 : 
     685 GIC         144 :     switch (strategy)
     686 ECB             :     {
     687 GIC          96 :         case RTOverlapStrategyNumber:
     688 CBC          96 :             retval = cube_overlap_v0(key, query);
     689              96 :             break;
     690 LBC           0 :         case RTSameStrategyNumber:
     691 UBC           0 :             retval = (cube_cmp_v0(key, query) == 0);
     692               0 :             break;
     693               0 :         case RTContainsStrategyNumber:
     694 EUB             :         case RTOldContainsStrategyNumber:
     695 UIC           0 :             retval = cube_contains_v0(key, query);
     696 UBC           0 :             break;
     697 GBC          48 :         case RTContainedByStrategyNumber:
     698 ECB             :         case RTOldContainedByStrategyNumber:
     699 GIC          48 :             retval = cube_contains_v0(query, key);
     700 CBC          48 :             break;
     701 LBC           0 :         default:
     702 UBC           0 :             retval = false;
     703 EUB             :     }
     704 GIC         144 :     return retval;
     705 ECB             : }
     706                 : 
     707                 : bool
     708 GIC         108 : g_cube_internal_consistent(NDBOX *key,
     709 ECB             :                            NDBOX *query,
     710                 :                            StrategyNumber strategy)
     711                 : {
     712                 :     bool        retval;
     713                 : 
     714 GIC         108 :     switch (strategy)
     715 ECB             :     {
     716 GIC          72 :         case RTOverlapStrategyNumber:
     717 CBC          72 :             retval = (bool) cube_overlap_v0(key, query);
     718              72 :             break;
     719 LBC           0 :         case RTSameStrategyNumber:
     720 EUB             :         case RTContainsStrategyNumber:
     721                 :         case RTOldContainsStrategyNumber:
     722 UIC           0 :             retval = (bool) cube_contains_v0(key, query);
     723 UBC           0 :             break;
     724 GBC          36 :         case RTContainedByStrategyNumber:
     725 ECB             :         case RTOldContainedByStrategyNumber:
     726 GIC          36 :             retval = (bool) cube_overlap_v0(key, query);
     727 CBC          36 :             break;
     728 LBC           0 :         default:
     729 UBC           0 :             retval = false;
     730 EUB             :     }
     731 GIC         108 :     return retval;
     732 ECB             : }
     733                 : 
     734                 : NDBOX *
     735 GIC        2962 : g_cube_binary_union(NDBOX *r1, NDBOX *r2, int *sizep)
     736 ECB             : {
     737                 :     NDBOX      *retval;
     738                 : 
     739 GIC        2962 :     retval = cube_union_v0(r1, r2);
     740 CBC        2962 :     *sizep = VARSIZE(retval);
     741 ECB             : 
     742 GIC        2962 :     return retval;
     743 ECB             : }
     744                 : 
     745                 : 
     746                 : /* cube_union_v0 */
     747                 : NDBOX *
     748 GIC      396680 : cube_union_v0(NDBOX *a, NDBOX *b)
     749 ECB             : {
     750                 :     int         i;
     751                 :     NDBOX      *result;
     752                 :     int         dim;
     753                 :     int         size;
     754                 : 
     755                 :     /* trivial case */
     756 GIC      396680 :     if (a == b)
     757 CBC          70 :         return a;
     758 ECB             : 
     759                 :     /* swap the arguments if needed, so that 'a' is always larger than 'b' */
     760 GIC      396610 :     if (DIM(a) < DIM(b))
     761 ECB             :     {
     762 GIC           3 :         NDBOX      *tmp = b;
     763 ECB             : 
     764 GIC           3 :         b = a;
     765 CBC           3 :         a = tmp;
     766 ECB             :     }
     767 GIC      396610 :     dim = DIM(a);
     768 ECB             : 
     769 GIC      396610 :     size = CUBE_SIZE(dim);
     770 CBC      396610 :     result = palloc0(size);
     771          396610 :     SET_VARSIZE(result, size);
     772          396610 :     SET_DIM(result, dim);
     773 ECB             : 
     774                 :     /* First compute the union of the dimensions present in both args */
     775 GIC     1189793 :     for (i = 0; i < DIM(b); i++)
     776 ECB             :     {
     777 GIC      793183 :         result->x[i] = Min(Min(LL_COORD(a, i), UR_COORD(a, i)),
     778 ECB             :                            Min(LL_COORD(b, i), UR_COORD(b, i)));
     779 GIC      793183 :         result->x[i + DIM(a)] = Max(Max(LL_COORD(a, i), UR_COORD(a, i)),
     780 ECB             :                                     Max(LL_COORD(b, i), UR_COORD(b, i)));
     781                 :     }
     782                 :     /* continue on the higher dimensions only present in 'a' */
     783 GIC      396651 :     for (; i < DIM(a); i++)
     784 ECB             :     {
     785 GIC          41 :         result->x[i] = Min(0,
     786 ECB             :                            Min(LL_COORD(a, i), UR_COORD(a, i))
     787                 :             );
     788 GIC          41 :         result->x[i + dim] = Max(0,
     789 ECB             :                                  Max(LL_COORD(a, i), UR_COORD(a, i))
     790                 :             );
     791                 :     }
     792                 : 
     793                 :     /*
     794                 :      * Check if the result was in fact a point, and set the flag in the datum
     795                 :      * accordingly. (we don't bother to repalloc it smaller)
     796                 :      */
     797 GIC      396610 :     if (cube_is_point_internal(result))
     798 ECB             :     {
     799 GIC           2 :         size = POINT_SIZE(dim);
     800 CBC           2 :         SET_VARSIZE(result, size);
     801               2 :         SET_POINT_BIT(result);
     802 ECB             :     }
     803                 : 
     804 GIC      396610 :     return result;
     805 ECB             : }
     806                 : 
     807                 : Datum
     808 GIC           5 : cube_union(PG_FUNCTION_ARGS)
     809 ECB             : {
     810 GIC           5 :     NDBOX      *a = PG_GETARG_NDBOX_P(0);
     811 CBC           5 :     NDBOX      *b = PG_GETARG_NDBOX_P(1);
     812 ECB             :     NDBOX      *res;
     813                 : 
     814 GIC           5 :     res = cube_union_v0(a, b);
     815 ECB             : 
     816 GIC           5 :     PG_FREE_IF_COPY(a, 0);
     817 CBC           5 :     PG_FREE_IF_COPY(b, 1);
     818               5 :     PG_RETURN_NDBOX_P(res);
     819 ECB             : }
     820                 : 
     821                 : /* cube_inter */
     822                 : Datum
     823 GIC      340557 : cube_inter(PG_FUNCTION_ARGS)
     824 ECB             : {
     825 GIC      340557 :     NDBOX      *a = PG_GETARG_NDBOX_P(0);
     826 CBC      340557 :     NDBOX      *b = PG_GETARG_NDBOX_P(1);
     827 ECB             :     NDBOX      *result;
     828 GIC      340557 :     bool        swapped = false;
     829 ECB             :     int         i;
     830                 :     int         dim;
     831                 :     int         size;
     832                 : 
     833                 :     /* swap the arguments if needed, so that 'a' is always larger than 'b' */
     834 GIC      340557 :     if (DIM(a) < DIM(b))
     835 ECB             :     {
     836 UIC           0 :         NDBOX      *tmp = b;
     837 EUB             : 
     838 UIC           0 :         b = a;
     839 UBC           0 :         a = tmp;
     840               0 :         swapped = true;
     841 EUB             :     }
     842 GIC      340557 :     dim = DIM(a);
     843 ECB             : 
     844 GIC      340557 :     size = CUBE_SIZE(dim);
     845 CBC      340557 :     result = (NDBOX *) palloc0(size);
     846          340557 :     SET_VARSIZE(result, size);
     847          340557 :     SET_DIM(result, dim);
     848 ECB             : 
     849                 :     /* First compute intersection of the dimensions present in both args */
     850 GIC     1021673 :     for (i = 0; i < DIM(b); i++)
     851 ECB             :     {
     852 GIC      681116 :         result->x[i] = Max(Min(LL_COORD(a, i), UR_COORD(a, i)),
     853 ECB             :                            Min(LL_COORD(b, i), UR_COORD(b, i)));
     854 GIC      681116 :         result->x[i + DIM(a)] = Min(Max(LL_COORD(a, i), UR_COORD(a, i)),
     855 ECB             :                                     Max(LL_COORD(b, i), UR_COORD(b, i)));
     856                 :     }
     857                 :     /* continue on the higher dimensions only present in 'a' */
     858 GIC      340557 :     for (; i < DIM(a); i++)
     859 ECB             :     {
     860 UIC           0 :         result->x[i] = Max(0,
     861 EUB             :                            Min(LL_COORD(a, i), UR_COORD(a, i))
     862                 :             );
     863 UIC           0 :         result->x[i + DIM(a)] = Min(0,
     864 EUB             :                                     Max(LL_COORD(a, i), UR_COORD(a, i))
     865                 :             );
     866                 :     }
     867                 : 
     868                 :     /*
     869                 :      * Check if the result was in fact a point, and set the flag in the datum
     870                 :      * accordingly. (we don't bother to repalloc it smaller)
     871                 :      */
     872 GIC      340557 :     if (cube_is_point_internal(result))
     873 ECB             :     {
     874 GIC           2 :         size = POINT_SIZE(dim);
     875 CBC           2 :         result = repalloc(result, size);
     876               2 :         SET_VARSIZE(result, size);
     877               2 :         SET_POINT_BIT(result);
     878 ECB             :     }
     879                 : 
     880 GIC      340557 :     if (swapped)
     881 ECB             :     {
     882 UIC           0 :         PG_FREE_IF_COPY(b, 0);
     883 UBC           0 :         PG_FREE_IF_COPY(a, 1);
     884 EUB             :     }
     885                 :     else
     886                 :     {
     887 GIC      340557 :         PG_FREE_IF_COPY(a, 0);
     888 CBC      340557 :         PG_FREE_IF_COPY(b, 1);
     889 ECB             :     }
     890                 : 
     891                 :     /*
     892                 :      * Is it OK to return a non-null intersection for non-overlapping boxes?
     893                 :      */
     894 GIC      340557 :     PG_RETURN_NDBOX_P(result);
     895 ECB             : }
     896                 : 
     897                 : /* cube_size */
     898                 : Datum
     899 GIC           2 : cube_size(PG_FUNCTION_ARGS)
     900 ECB             : {
     901 GIC           2 :     NDBOX      *a = PG_GETARG_NDBOX_P(0);
     902 ECB             :     double      result;
     903                 : 
     904 GIC           2 :     rt_cube_size(a, &result);
     905 CBC           2 :     PG_FREE_IF_COPY(a, 0);
     906               2 :     PG_RETURN_FLOAT8(result);
     907 ECB             : }
     908                 : 
     909                 : void
     910 GIC      777628 : rt_cube_size(NDBOX *a, double *size)
     911 ECB             : {
     912                 :     double      result;
     913                 :     int         i;
     914                 : 
     915 GIC      777628 :     if (a == (NDBOX *) NULL)
     916 ECB             :     {
     917                 :         /* special case for GiST */
     918 UIC           0 :         result = 0.0;
     919 EUB             :     }
     920 GIC      777628 :     else if (IS_POINT(a) || DIM(a) == 0)
     921 ECB             :     {
     922                 :         /* necessarily has zero size */
     923 GIC           1 :         result = 0.0;
     924 ECB             :     }
     925                 :     else
     926                 :     {
     927 GIC      777627 :         result = 1.0;
     928 CBC     2332881 :         for (i = 0; i < DIM(a); i++)
     929 GNC     1555254 :             result *= fabs(UR_COORD(a, i) - LL_COORD(a, i));
     930 ECB             :     }
     931 GIC      777628 :     *size = result;
     932 CBC      777628 : }
     933 ECB             : 
     934                 : /* make up a metric in which one box will be 'lower' than the other
     935                 :    -- this can be useful for sorting and to determine uniqueness */
     936                 : int32
     937 GIC        3010 : cube_cmp_v0(NDBOX *a, NDBOX *b)
     938 ECB             : {
     939                 :     int         i;
     940                 :     int         dim;
     941                 : 
     942 GIC        3010 :     dim = Min(DIM(a), DIM(b));
     943 ECB             : 
     944                 :     /* compare the common dimensions */
     945 GIC        8859 :     for (i = 0; i < dim; i++)
     946 ECB             :     {
     947 GIC       11912 :         if (Min(LL_COORD(a, i), UR_COORD(a, i)) >
     948 CBC        5956 :             Min(LL_COORD(b, i), UR_COORD(b, i)))
     949              92 :             return 1;
     950           11728 :         if (Min(LL_COORD(a, i), UR_COORD(a, i)) <
     951            5864 :             Min(LL_COORD(b, i), UR_COORD(b, i)))
     952              15 :             return -1;
     953 ECB             :     }
     954 GIC        8591 :     for (i = 0; i < dim; i++)
     955 ECB             :     {
     956 GIC       11534 :         if (Max(LL_COORD(a, i), UR_COORD(a, i)) >
     957 CBC        5767 :             Max(LL_COORD(b, i), UR_COORD(b, i)))
     958 LBC           0 :             return 1;
     959 GBC       11534 :         if (Max(LL_COORD(a, i), UR_COORD(a, i)) <
     960 CBC        5767 :             Max(LL_COORD(b, i), UR_COORD(b, i)))
     961              79 :             return -1;
     962 ECB             :     }
     963                 : 
     964                 :     /* compare extra dimensions to zero */
     965 GIC        2824 :     if (DIM(a) > DIM(b))
     966 ECB             :     {
     967 GIC          24 :         for (i = dim; i < DIM(a); i++)
     968 ECB             :         {
     969 GIC          18 :             if (Min(LL_COORD(a, i), UR_COORD(a, i)) > 0)
     970 LBC           0 :                 return 1;
     971 GBC          18 :             if (Min(LL_COORD(a, i), UR_COORD(a, i)) < 0)
     972 LBC           0 :                 return -1;
     973 EUB             :         }
     974 GIC          20 :         for (i = dim; i < DIM(a); i++)
     975 ECB             :         {
     976 GIC          18 :             if (Max(LL_COORD(a, i), UR_COORD(a, i)) > 0)
     977 CBC           4 :                 return 1;
     978              14 :             if (Max(LL_COORD(a, i), UR_COORD(a, i)) < 0)
     979 LBC           0 :                 return -1;
     980 EUB             :         }
     981                 : 
     982                 :         /*
     983                 :          * if all common dimensions are equal, the cube with more dimensions
     984                 :          * wins
     985                 :          */
     986 GIC           2 :         return 1;
     987 ECB             :     }
     988 GIC        2818 :     if (DIM(a) < DIM(b))
     989 ECB             :     {
     990 GIC          32 :         for (i = dim; i < DIM(b); i++)
     991 ECB             :         {
     992 GIC          24 :             if (Min(LL_COORD(b, i), UR_COORD(b, i)) > 0)
     993 LBC           0 :                 return -1;
     994 GBC          24 :             if (Min(LL_COORD(b, i), UR_COORD(b, i)) < 0)
     995 LBC           0 :                 return 1;
     996 EUB             :         }
     997 GIC          27 :         for (i = dim; i < DIM(b); i++)
     998 ECB             :         {
     999 GIC          24 :             if (Max(LL_COORD(b, i), UR_COORD(b, i)) > 0)
    1000 CBC           5 :                 return -1;
    1001              19 :             if (Max(LL_COORD(b, i), UR_COORD(b, i)) < 0)
    1002 LBC           0 :                 return 1;
    1003 EUB             :         }
    1004                 : 
    1005                 :         /*
    1006                 :          * if all common dimensions are equal, the cube with more dimensions
    1007                 :          * wins
    1008                 :          */
    1009 GIC           3 :         return -1;
    1010 ECB             :     }
    1011                 : 
    1012                 :     /* They're really equal */
    1013 GIC        2810 :     return 0;
    1014 ECB             : }
    1015                 : 
    1016                 : Datum
    1017 GIC          22 : cube_cmp(PG_FUNCTION_ARGS)
    1018 ECB             : {
    1019 GIC          22 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1020 CBC          22 :                *b = PG_GETARG_NDBOX_P(1);
    1021 ECB             :     int32       res;
    1022                 : 
    1023 GIC          22 :     res = cube_cmp_v0(a, b);
    1024 ECB             : 
    1025 GIC          22 :     PG_FREE_IF_COPY(a, 0);
    1026 CBC          22 :     PG_FREE_IF_COPY(b, 1);
    1027              22 :     PG_RETURN_INT32(res);
    1028 ECB             : }
    1029                 : 
    1030                 : 
    1031                 : Datum
    1032 GIC           8 : cube_eq(PG_FUNCTION_ARGS)
    1033 ECB             : {
    1034 GIC           8 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1035 CBC           8 :                *b = PG_GETARG_NDBOX_P(1);
    1036 ECB             :     int32       res;
    1037                 : 
    1038 GIC           8 :     res = cube_cmp_v0(a, b);
    1039 ECB             : 
    1040 GIC           8 :     PG_FREE_IF_COPY(a, 0);
    1041 CBC           8 :     PG_FREE_IF_COPY(b, 1);
    1042               8 :     PG_RETURN_BOOL(res == 0);
    1043 ECB             : }
    1044                 : 
    1045                 : 
    1046                 : Datum
    1047 GIC           2 : cube_ne(PG_FUNCTION_ARGS)
    1048 ECB             : {
    1049 GIC           2 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1050 CBC           2 :                *b = PG_GETARG_NDBOX_P(1);
    1051 ECB             :     int32       res;
    1052                 : 
    1053 GIC           2 :     res = cube_cmp_v0(a, b);
    1054 ECB             : 
    1055 GIC           2 :     PG_FREE_IF_COPY(a, 0);
    1056 CBC           2 :     PG_FREE_IF_COPY(b, 1);
    1057               2 :     PG_RETURN_BOOL(res != 0);
    1058 ECB             : }
    1059                 : 
    1060                 : 
    1061                 : Datum
    1062 GIC           8 : cube_lt(PG_FUNCTION_ARGS)
    1063 ECB             : {
    1064 GIC           8 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1065 CBC           8 :                *b = PG_GETARG_NDBOX_P(1);
    1066 ECB             :     int32       res;
    1067                 : 
    1068 GIC           8 :     res = cube_cmp_v0(a, b);
    1069 ECB             : 
    1070 GIC           8 :     PG_FREE_IF_COPY(a, 0);
    1071 CBC           8 :     PG_FREE_IF_COPY(b, 1);
    1072               8 :     PG_RETURN_BOOL(res < 0);
    1073 ECB             : }
    1074                 : 
    1075                 : 
    1076                 : Datum
    1077 GIC           8 : cube_gt(PG_FUNCTION_ARGS)
    1078 ECB             : {
    1079 GIC           8 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1080 CBC           8 :                *b = PG_GETARG_NDBOX_P(1);
    1081 ECB             :     int32       res;
    1082                 : 
    1083 GIC           8 :     res = cube_cmp_v0(a, b);
    1084 ECB             : 
    1085 GIC           8 :     PG_FREE_IF_COPY(a, 0);
    1086 CBC           8 :     PG_FREE_IF_COPY(b, 1);
    1087               8 :     PG_RETURN_BOOL(res > 0);
    1088 ECB             : }
    1089                 : 
    1090                 : 
    1091                 : Datum
    1092 UIC           0 : cube_le(PG_FUNCTION_ARGS)
    1093 EUB             : {
    1094 UIC           0 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1095 UBC           0 :                *b = PG_GETARG_NDBOX_P(1);
    1096 EUB             :     int32       res;
    1097                 : 
    1098 UIC           0 :     res = cube_cmp_v0(a, b);
    1099 EUB             : 
    1100 UIC           0 :     PG_FREE_IF_COPY(a, 0);
    1101 UBC           0 :     PG_FREE_IF_COPY(b, 1);
    1102               0 :     PG_RETURN_BOOL(res <= 0);
    1103 EUB             : }
    1104                 : 
    1105                 : 
    1106                 : Datum
    1107 UIC           0 : cube_ge(PG_FUNCTION_ARGS)
    1108 EUB             : {
    1109 UIC           0 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1110 UBC           0 :                *b = PG_GETARG_NDBOX_P(1);
    1111 EUB             :     int32       res;
    1112                 : 
    1113 UIC           0 :     res = cube_cmp_v0(a, b);
    1114 EUB             : 
    1115 UIC           0 :     PG_FREE_IF_COPY(a, 0);
    1116 UBC           0 :     PG_FREE_IF_COPY(b, 1);
    1117               0 :     PG_RETURN_BOOL(res >= 0);
    1118 EUB             : }
    1119                 : 
    1120                 : 
    1121                 : /* Contains */
    1122                 : /* Box(A) CONTAINS Box(B) IFF pt(A) < pt(B) */
    1123                 : bool
    1124 GIC          94 : cube_contains_v0(NDBOX *a, NDBOX *b)
    1125 ECB             : {
    1126                 :     int         i;
    1127                 : 
    1128 GIC          94 :     if ((a == NULL) || (b == NULL))
    1129 LBC           0 :         return false;
    1130 EUB             : 
    1131 GIC          94 :     if (DIM(a) < DIM(b))
    1132 ECB             :     {
    1133                 :         /*
    1134                 :          * the further comparisons will make sense if the excess dimensions of
    1135                 :          * (b) were zeroes Since both UL and UR coordinates must be zero, we
    1136                 :          * can check them all without worrying about which is which.
    1137                 :          */
    1138 UIC           0 :         for (i = DIM(a); i < DIM(b); i++)
    1139 EUB             :         {
    1140 UIC           0 :             if (LL_COORD(b, i) != 0)
    1141 UBC           0 :                 return false;
    1142               0 :             if (UR_COORD(b, i) != 0)
    1143               0 :                 return false;
    1144 EUB             :         }
    1145                 :     }
    1146                 : 
    1147                 :     /* Can't care less about the excess dimensions of (a), if any */
    1148 GIC         190 :     for (i = 0; i < Min(DIM(a), DIM(b)); i++)
    1149 ECB             :     {
    1150 GIC         312 :         if (Min(LL_COORD(a, i), UR_COORD(a, i)) >
    1151 CBC         156 :             Min(LL_COORD(b, i), UR_COORD(b, i)))
    1152               7 :             return false;
    1153             298 :         if (Max(LL_COORD(a, i), UR_COORD(a, i)) <
    1154             149 :             Max(LL_COORD(b, i), UR_COORD(b, i)))
    1155              53 :             return false;
    1156 ECB             :     }
    1157                 : 
    1158 GIC          34 :     return true;
    1159 ECB             : }
    1160                 : 
    1161                 : Datum
    1162 GIC          31 : cube_contains(PG_FUNCTION_ARGS)
    1163 ECB             : {
    1164 GIC          31 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1165 CBC          31 :                *b = PG_GETARG_NDBOX_P(1);
    1166 ECB             :     bool        res;
    1167                 : 
    1168 GIC          31 :     res = cube_contains_v0(a, b);
    1169 ECB             : 
    1170 GIC          31 :     PG_FREE_IF_COPY(a, 0);
    1171 CBC          31 :     PG_FREE_IF_COPY(b, 1);
    1172              31 :     PG_RETURN_BOOL(res);
    1173 ECB             : }
    1174                 : 
    1175                 : /* Contained */
    1176                 : /* Box(A) Contained by Box(B) IFF Box(B) Contains Box(A) */
    1177                 : Datum
    1178 GIC          15 : cube_contained(PG_FUNCTION_ARGS)
    1179 ECB             : {
    1180 GIC          15 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1181 CBC          15 :                *b = PG_GETARG_NDBOX_P(1);
    1182 ECB             :     bool        res;
    1183                 : 
    1184 GIC          15 :     res = cube_contains_v0(b, a);
    1185 ECB             : 
    1186 GIC          15 :     PG_FREE_IF_COPY(a, 0);
    1187 CBC          15 :     PG_FREE_IF_COPY(b, 1);
    1188              15 :     PG_RETURN_BOOL(res);
    1189 ECB             : }
    1190                 : 
    1191                 : /* Overlap */
    1192                 : /* Box(A) Overlap Box(B) IFF (pt(a)LL < pt(B)UR) && (pt(b)LL < pt(a)UR) */
    1193                 : bool
    1194 GIC         212 : cube_overlap_v0(NDBOX *a, NDBOX *b)
    1195 ECB             : {
    1196                 :     int         i;
    1197                 : 
    1198 GIC         212 :     if ((a == NULL) || (b == NULL))
    1199 LBC           0 :         return false;
    1200 EUB             : 
    1201                 :     /* swap the box pointers if needed */
    1202 GIC         212 :     if (DIM(a) < DIM(b))
    1203 ECB             :     {
    1204 UIC           0 :         NDBOX      *tmp = b;
    1205 EUB             : 
    1206 UIC           0 :         b = a;
    1207 UBC           0 :         a = tmp;
    1208 EUB             :     }
    1209                 : 
    1210                 :     /* compare within the dimensions of (b) */
    1211 GIC         290 :     for (i = 0; i < DIM(b); i++)
    1212 ECB             :     {
    1213 GIC         271 :         if (Min(LL_COORD(a, i), UR_COORD(a, i)) > Max(LL_COORD(b, i), UR_COORD(b, i)))
    1214 CBC         191 :             return false;
    1215              80 :         if (Max(LL_COORD(a, i), UR_COORD(a, i)) < Min(LL_COORD(b, i), UR_COORD(b, i)))
    1216               2 :             return false;
    1217 ECB             :     }
    1218                 : 
    1219                 :     /* compare to zero those dimensions in (a) absent in (b) */
    1220 GIC          24 :     for (i = DIM(b); i < DIM(a); i++)
    1221 ECB             :     {
    1222 GIC           5 :         if (Min(LL_COORD(a, i), UR_COORD(a, i)) > 0)
    1223 LBC           0 :             return false;
    1224 GBC           5 :         if (Max(LL_COORD(a, i), UR_COORD(a, i)) < 0)
    1225 LBC           0 :             return false;
    1226 EUB             :     }
    1227                 : 
    1228 GIC          19 :     return true;
    1229 ECB             : }
    1230                 : 
    1231                 : 
    1232                 : Datum
    1233 GIC           8 : cube_overlap(PG_FUNCTION_ARGS)
    1234 ECB             : {
    1235 GIC           8 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1236 CBC           8 :                *b = PG_GETARG_NDBOX_P(1);
    1237 ECB             :     bool        res;
    1238                 : 
    1239 GIC           8 :     res = cube_overlap_v0(a, b);
    1240 ECB             : 
    1241 GIC           8 :     PG_FREE_IF_COPY(a, 0);
    1242 CBC           8 :     PG_FREE_IF_COPY(b, 1);
    1243               8 :     PG_RETURN_BOOL(res);
    1244 ECB             : }
    1245                 : 
    1246                 : 
    1247                 : /* Distance */
    1248                 : /* The distance is computed as a per axis sum of the squared distances
    1249                 :    between 1D projections of the boxes onto Cartesian axes. Assuming zero
    1250                 :    distance between overlapping projections, this metric coincides with the
    1251                 :    "common sense" geometric distance */
    1252                 : Datum
    1253 GIC        3424 : cube_distance(PG_FUNCTION_ARGS)
    1254 ECB             : {
    1255 GIC        3424 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1256 CBC        3424 :                *b = PG_GETARG_NDBOX_P(1);
    1257            3424 :     bool        swapped = false;
    1258 ECB             :     double      d,
    1259                 :                 distance;
    1260                 :     int         i;
    1261                 : 
    1262                 :     /* swap the box pointers if needed */
    1263 GIC        3424 :     if (DIM(a) < DIM(b))
    1264 ECB             :     {
    1265 GIC           3 :         NDBOX      *tmp = b;
    1266 ECB             : 
    1267 GIC           3 :         b = a;
    1268 CBC           3 :         a = tmp;
    1269               3 :         swapped = true;
    1270 ECB             :     }
    1271                 : 
    1272 GIC        3424 :     distance = 0.0;
    1273 ECB             :     /* compute within the dimensions of (b) */
    1274 GIC       10105 :     for (i = 0; i < DIM(b); i++)
    1275 ECB             :     {
    1276 GIC        6681 :         d = distance_1D(LL_COORD(a, i), UR_COORD(a, i), LL_COORD(b, i), UR_COORD(b, i));
    1277 CBC        6681 :         distance += d * d;
    1278 ECB             :     }
    1279                 : 
    1280                 :     /* compute distance to zero for those dimensions in (a) absent in (b) */
    1281 GIC        3820 :     for (i = DIM(b); i < DIM(a); i++)
    1282 ECB             :     {
    1283 GIC         396 :         d = distance_1D(LL_COORD(a, i), UR_COORD(a, i), 0.0, 0.0);
    1284 CBC         396 :         distance += d * d;
    1285 ECB             :     }
    1286                 : 
    1287 GIC        3424 :     if (swapped)
    1288 ECB             :     {
    1289 GIC           3 :         PG_FREE_IF_COPY(b, 0);
    1290 CBC           3 :         PG_FREE_IF_COPY(a, 1);
    1291 ECB             :     }
    1292                 :     else
    1293                 :     {
    1294 GIC        3421 :         PG_FREE_IF_COPY(a, 0);
    1295 CBC        3421 :         PG_FREE_IF_COPY(b, 1);
    1296 ECB             :     }
    1297                 : 
    1298 GIC        3424 :     PG_RETURN_FLOAT8(sqrt(distance));
    1299 ECB             : }
    1300                 : 
    1301                 : Datum
    1302 GIC        3196 : distance_taxicab(PG_FUNCTION_ARGS)
    1303 ECB             : {
    1304 GIC        3196 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1305 CBC        3196 :                *b = PG_GETARG_NDBOX_P(1);
    1306            3196 :     bool        swapped = false;
    1307 ECB             :     double      distance;
    1308                 :     int         i;
    1309                 : 
    1310                 :     /* swap the box pointers if needed */
    1311 GIC        3196 :     if (DIM(a) < DIM(b))
    1312 ECB             :     {
    1313 GIC           1 :         NDBOX      *tmp = b;
    1314 ECB             : 
    1315 GIC           1 :         b = a;
    1316 CBC           1 :         a = tmp;
    1317               1 :         swapped = true;
    1318 ECB             :     }
    1319                 : 
    1320 GIC        3196 :     distance = 0.0;
    1321 ECB             :     /* compute within the dimensions of (b) */
    1322 GIC        9587 :     for (i = 0; i < DIM(b); i++)
    1323 CBC        6391 :         distance += fabs(distance_1D(LL_COORD(a, i), UR_COORD(a, i),
    1324            6391 :                                      LL_COORD(b, i), UR_COORD(b, i)));
    1325 ECB             : 
    1326                 :     /* compute distance to zero for those dimensions in (a) absent in (b) */
    1327 GIC        3197 :     for (i = DIM(b); i < DIM(a); i++)
    1328 CBC           1 :         distance += fabs(distance_1D(LL_COORD(a, i), UR_COORD(a, i),
    1329 ECB             :                                      0.0, 0.0));
    1330                 : 
    1331 GIC        3196 :     if (swapped)
    1332 ECB             :     {
    1333 GIC           1 :         PG_FREE_IF_COPY(b, 0);
    1334 CBC           1 :         PG_FREE_IF_COPY(a, 1);
    1335 ECB             :     }
    1336                 :     else
    1337                 :     {
    1338 GIC        3195 :         PG_FREE_IF_COPY(a, 0);
    1339 CBC        3195 :         PG_FREE_IF_COPY(b, 1);
    1340 ECB             :     }
    1341                 : 
    1342 GIC        3196 :     PG_RETURN_FLOAT8(distance);
    1343 ECB             : }
    1344                 : 
    1345                 : Datum
    1346 GIC        3196 : distance_chebyshev(PG_FUNCTION_ARGS)
    1347 ECB             : {
    1348 GIC        3196 :     NDBOX      *a = PG_GETARG_NDBOX_P(0),
    1349 CBC        3196 :                *b = PG_GETARG_NDBOX_P(1);
    1350            3196 :     bool        swapped = false;
    1351 ECB             :     double      d,
    1352                 :                 distance;
    1353                 :     int         i;
    1354                 : 
    1355                 :     /* swap the box pointers if needed */
    1356 GIC        3196 :     if (DIM(a) < DIM(b))
    1357 ECB             :     {
    1358 GIC           1 :         NDBOX      *tmp = b;
    1359 ECB             : 
    1360 GIC           1 :         b = a;
    1361 CBC           1 :         a = tmp;
    1362               1 :         swapped = true;
    1363 ECB             :     }
    1364                 : 
    1365 GIC        3196 :     distance = 0.0;
    1366 ECB             :     /* compute within the dimensions of (b) */
    1367 GIC        9587 :     for (i = 0; i < DIM(b); i++)
    1368 ECB             :     {
    1369 GIC        6391 :         d = fabs(distance_1D(LL_COORD(a, i), UR_COORD(a, i),
    1370 CBC        6391 :                              LL_COORD(b, i), UR_COORD(b, i)));
    1371            6391 :         if (d > distance)
    1372            4721 :             distance = d;
    1373 ECB             :     }
    1374                 : 
    1375                 :     /* compute distance to zero for those dimensions in (a) absent in (b) */
    1376 GIC        3197 :     for (i = DIM(b); i < DIM(a); i++)
    1377 ECB             :     {
    1378 GIC           1 :         d = fabs(distance_1D(LL_COORD(a, i), UR_COORD(a, i), 0.0, 0.0));
    1379 CBC           1 :         if (d > distance)
    1380 LBC           0 :             distance = d;
    1381 EUB             :     }
    1382                 : 
    1383 GIC        3196 :     if (swapped)
    1384 ECB             :     {
    1385 GIC           1 :         PG_FREE_IF_COPY(b, 0);
    1386 CBC           1 :         PG_FREE_IF_COPY(a, 1);
    1387 ECB             :     }
    1388                 :     else
    1389                 :     {
    1390 GIC        3195 :         PG_FREE_IF_COPY(a, 0);
    1391 CBC        3195 :         PG_FREE_IF_COPY(b, 1);
    1392 ECB             :     }
    1393                 : 
    1394 GIC        3196 :     PG_RETURN_FLOAT8(distance);
    1395 ECB             : }
    1396                 : 
    1397                 : Datum
    1398 GIC        4092 : g_cube_distance(PG_FUNCTION_ARGS)
    1399 ECB             : {
    1400 GIC        4092 :     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
    1401 CBC        4092 :     StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
    1402            4092 :     NDBOX      *cube = DatumGetNDBOXP(entry->key);
    1403 ECB             :     double      retval;
    1404                 : 
    1405 GIC        4092 :     if (strategy == CubeKNNDistanceCoord)
    1406 ECB             :     {
    1407                 :         /*
    1408                 :          * Handle ordering by ~> operator.  See comments of cube_coord_llur()
    1409                 :          * for details
    1410                 :          */
    1411 GIC        3837 :         int         coord = PG_GETARG_INT32(1);
    1412 CBC        3837 :         bool        isLeaf = GistPageIsLeaf(entry->page);
    1413            3837 :         bool        inverse = false;
    1414 ECB             : 
    1415                 :         /* 0 is the only unsupported coordinate value */
    1416 GIC        3837 :         if (coord == 0)
    1417 LBC           0 :             ereport(ERROR,
    1418 EUB             :                     (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
    1419                 :                      errmsg("zero cube index is not defined")));
    1420                 : 
    1421                 :         /* Return inversed value for negative coordinate */
    1422 GIC        3837 :         if (coord < 0)
    1423 ECB             :         {
    1424 GIC        2339 :             coord = -coord;
    1425 CBC        2339 :             inverse = true;
    1426 ECB             :         }
    1427                 : 
    1428 GIC        3837 :         if (coord <= 2 * DIM(cube))
    1429 ECB             :         {
    1430                 :             /* dimension index */
    1431 GIC        3835 :             int         index = (coord - 1) / 2;
    1432 ECB             : 
    1433                 :             /* whether this is upper bound (lower bound otherwise) */
    1434 GIC        3835 :             bool        upper = ((coord - 1) % 2 == 1);
    1435 ECB             : 
    1436 GIC        3835 :             if (IS_POINT(cube))
    1437 ECB             :             {
    1438 GIC          10 :                 retval = cube->x[index];
    1439 ECB             :             }
    1440                 :             else
    1441                 :             {
    1442 GIC        3825 :                 if (isLeaf)
    1443 ECB             :                 {
    1444                 :                     /* For leaf just return required upper/lower bound */
    1445 GIC        3537 :                     if (upper)
    1446 CBC        1702 :                         retval = Max(cube->x[index], cube->x[index + DIM(cube)]);
    1447 ECB             :                     else
    1448 GIC        1835 :                         retval = Min(cube->x[index], cube->x[index + DIM(cube)]);
    1449 ECB             :                 }
    1450                 :                 else
    1451                 :                 {
    1452                 :                     /*
    1453                 :                      * For non-leaf we should always return lower bound,
    1454                 :                      * because even upper bound of a child in the subtree can
    1455                 :                      * be as small as our lower bound.  For inversed case we
    1456                 :                      * return upper bound because it becomes lower bound for
    1457                 :                      * inversed value.
    1458                 :                      */
    1459 GIC         288 :                     if (!inverse)
    1460 CBC         144 :                         retval = Min(cube->x[index], cube->x[index + DIM(cube)]);
    1461 ECB             :                     else
    1462 GIC         144 :                         retval = Max(cube->x[index], cube->x[index + DIM(cube)]);
    1463 ECB             :                 }
    1464                 :             }
    1465                 :         }
    1466                 :         else
    1467                 :         {
    1468 GIC           2 :             retval = 0.0;
    1469 ECB             :         }
    1470                 : 
    1471                 :         /* Inverse return value if needed */
    1472 GIC        3837 :         if (inverse)
    1473 CBC        2339 :             retval = -retval;
    1474 ECB             :     }
    1475                 :     else
    1476                 :     {
    1477 GIC         255 :         NDBOX      *query = PG_GETARG_NDBOX_P(1);
    1478 ECB             : 
    1479 GIC         255 :         switch (strategy)
    1480 ECB             :         {
    1481 GIC          85 :             case CubeKNNDistanceTaxicab:
    1482 CBC          85 :                 retval = DatumGetFloat8(DirectFunctionCall2(distance_taxicab,
    1483 ECB             :                                                             PointerGetDatum(cube), PointerGetDatum(query)));
    1484 GIC          85 :                 break;
    1485 CBC          85 :             case CubeKNNDistanceEuclid:
    1486              85 :                 retval = DatumGetFloat8(DirectFunctionCall2(cube_distance,
    1487 ECB             :                                                             PointerGetDatum(cube), PointerGetDatum(query)));
    1488 GIC          85 :                 break;
    1489 CBC          85 :             case CubeKNNDistanceChebyshev:
    1490              85 :                 retval = DatumGetFloat8(DirectFunctionCall2(distance_chebyshev,
    1491 ECB             :                                                             PointerGetDatum(cube), PointerGetDatum(query)));
    1492 GIC          85 :                 break;
    1493 LBC           0 :             default:
    1494 UBC           0 :                 elog(ERROR, "unrecognized cube strategy number: %d", strategy);
    1495 EUB             :                 retval = 0;     /* keep compiler quiet */
    1496                 :                 break;
    1497                 :         }
    1498                 :     }
    1499 GIC        4092 :     PG_RETURN_FLOAT8(retval);
    1500 ECB             : }
    1501                 : 
    1502                 : static double
    1503 GIC       19861 : distance_1D(double a1, double a2, double b1, double b2)
    1504 ECB             : {
    1505                 :     /* interval (a) is entirely on the left of (b) */
    1506 GIC       19861 :     if ((a1 <= b1) && (a2 <= b1) && (a1 <= b2) && (a2 <= b2))
    1507 CBC         376 :         return (Min(b1, b2) - Max(a1, a2));
    1508 ECB             : 
    1509                 :     /* interval (a) is entirely on the right of (b) */
    1510 GIC       19485 :     if ((a1 > b1) && (a2 > b1) && (a1 > b2) && (a2 > b2))
    1511 CBC       19250 :         return (Min(a1, a2) - Max(b1, b2));
    1512 ECB             : 
    1513                 :     /* the rest are all sorts of intersections */
    1514 GIC         235 :     return 0.0;
    1515 ECB             : }
    1516                 : 
    1517                 : /* Test if a box is also a point */
    1518                 : Datum
    1519 GIC         201 : cube_is_point(PG_FUNCTION_ARGS)
    1520 ECB             : {
    1521 GIC         201 :     NDBOX      *cube = PG_GETARG_NDBOX_P(0);
    1522 ECB             :     bool        result;
    1523                 : 
    1524 GIC         201 :     result = cube_is_point_internal(cube);
    1525 CBC         201 :     PG_FREE_IF_COPY(cube, 0);
    1526             201 :     PG_RETURN_BOOL(result);
    1527 ECB             : }
    1528                 : 
    1529                 : static bool
    1530 GIC      737811 : cube_is_point_internal(NDBOX *cube)
    1531 ECB             : {
    1532                 :     int         i;
    1533                 : 
    1534 GIC      737811 :     if (IS_POINT(cube))
    1535 CBC         297 :         return true;
    1536 ECB             : 
    1537                 :     /*
    1538                 :      * Even if the point-flag is not set, all the lower-left coordinates might
    1539                 :      * match the upper-right coordinates, so that the value is in fact a
    1540                 :      * point. Such values don't arise with current code - the point flag is
    1541                 :      * always set if appropriate - but they might be present on-disk in
    1542                 :      * clusters upgraded from pre-9.4 versions.
    1543                 :      */
    1544 GIC      737657 :     for (i = 0; i < DIM(cube); i++)
    1545 ECB             :     {
    1546 GIC      737645 :         if (LL_COORD(cube, i) != UR_COORD(cube, i))
    1547 CBC      737502 :             return false;
    1548 ECB             :     }
    1549 GIC          12 :     return true;
    1550 ECB             : }
    1551                 : 
    1552                 : /* Return dimensions in use in the data structure */
    1553                 : Datum
    1554 GIC         200 : cube_dim(PG_FUNCTION_ARGS)
    1555 ECB             : {
    1556 GIC         200 :     NDBOX      *c = PG_GETARG_NDBOX_P(0);
    1557 CBC         200 :     int         dim = DIM(c);
    1558 ECB             : 
    1559 GIC         200 :     PG_FREE_IF_COPY(c, 0);
    1560 CBC         200 :     PG_RETURN_INT32(dim);
    1561 ECB             : }
    1562                 : 
    1563                 : /* Return a specific normalized LL coordinate */
    1564                 : Datum
    1565 GIC         151 : cube_ll_coord(PG_FUNCTION_ARGS)
    1566 ECB             : {
    1567 GIC         151 :     NDBOX      *c = PG_GETARG_NDBOX_P(0);
    1568 CBC         151 :     int         n = PG_GETARG_INT32(1);
    1569 ECB             :     double      result;
    1570                 : 
    1571 GIC         151 :     if (DIM(c) >= n && n > 0)
    1572 CBC         148 :         result = Min(LL_COORD(c, n - 1), UR_COORD(c, n - 1));
    1573 ECB             :     else
    1574 GIC           3 :         result = 0;
    1575 ECB             : 
    1576 GIC         151 :     PG_FREE_IF_COPY(c, 0);
    1577 CBC         151 :     PG_RETURN_FLOAT8(result);
    1578 ECB             : }
    1579                 : 
    1580                 : /* Return a specific normalized UR coordinate */
    1581                 : Datum
    1582 GIC          18 : cube_ur_coord(PG_FUNCTION_ARGS)
    1583 ECB             : {
    1584 GIC          18 :     NDBOX      *c = PG_GETARG_NDBOX_P(0);
    1585 CBC          18 :     int         n = PG_GETARG_INT32(1);
    1586 ECB             :     double      result;
    1587                 : 
    1588 GIC          18 :     if (DIM(c) >= n && n > 0)
    1589 CBC          15 :         result = Max(LL_COORD(c, n - 1), UR_COORD(c, n - 1));
    1590 ECB             :     else
    1591 GIC           3 :         result = 0;
    1592 ECB             : 
    1593 GIC          18 :     PG_FREE_IF_COPY(c, 0);
    1594 CBC          18 :     PG_RETURN_FLOAT8(result);
    1595 ECB             : }
    1596                 : 
    1597                 : /*
    1598                 :  * Function returns cube coordinate.
    1599                 :  * Numbers from 1 to DIM denotes first corner coordinates.
    1600                 :  * Numbers from DIM+1 to 2*DIM denotes second corner coordinates.
    1601                 :  */
    1602                 : Datum
    1603 GIC          10 : cube_coord(PG_FUNCTION_ARGS)
    1604 ECB             : {
    1605 GIC          10 :     NDBOX      *cube = PG_GETARG_NDBOX_P(0);
    1606 CBC          10 :     int         coord = PG_GETARG_INT32(1);
    1607 ECB             : 
    1608 GIC          10 :     if (coord <= 0 || coord > 2 * DIM(cube))
    1609 CBC           5 :         ereport(ERROR,
    1610 ECB             :                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
    1611                 :                  errmsg("cube index %d is out of bounds", coord)));
    1612                 : 
    1613 GIC           5 :     if (IS_POINT(cube))
    1614 CBC           2 :         PG_RETURN_FLOAT8(cube->x[(coord - 1) % DIM(cube)]);
    1615 ECB             :     else
    1616 GIC           3 :         PG_RETURN_FLOAT8(cube->x[coord - 1]);
    1617 ECB             : }
    1618                 : 
    1619                 : 
    1620                 : /*----
    1621                 :  * This function works like cube_coord(), but rearranges coordinates in the
    1622                 :  * way suitable to support coordinate ordering using KNN-GiST.  For historical
    1623                 :  * reasons this extension allows us to create cubes in form ((2,1),(1,2)) and
    1624                 :  * instead of normalizing such cube to ((1,1),(2,2)) it stores cube in original
    1625                 :  * way.  But in order to get cubes ordered by one of dimensions from the index
    1626                 :  * without explicit sort step we need this representation-independent coordinate
    1627                 :  * getter.  Moreover, indexed dataset may contain cubes of different dimensions
    1628                 :  * number.  Accordingly, this coordinate getter should be able to return
    1629                 :  * lower/upper bound for particular dimension independently on number of cube
    1630                 :  * dimensions.  Also, KNN-GiST supports only ascending sorting.  In order to
    1631                 :  * support descending sorting, this function returns inverse of value when
    1632                 :  * negative coordinate is given.
    1633                 :  *
    1634                 :  * Long story short, this function uses following meaning of coordinates:
    1635                 :  * # (2 * N - 1) -- lower bound of Nth dimension,
    1636                 :  * # (2 * N) -- upper bound of Nth dimension,
    1637                 :  * # - (2 * N - 1) -- negative of lower bound of Nth dimension,
    1638                 :  * # - (2 * N) -- negative of upper bound of Nth dimension.
    1639                 :  *
    1640                 :  * When given coordinate exceeds number of cube dimensions, then 0 returned
    1641                 :  * (reproducing logic of GiST indexing of variable-length cubes).
    1642                 :  */
    1643                 : Datum
    1644 GIC       24953 : cube_coord_llur(PG_FUNCTION_ARGS)
    1645 ECB             : {
    1646 GIC       24953 :     NDBOX      *cube = PG_GETARG_NDBOX_P(0);
    1647 CBC       24953 :     int         coord = PG_GETARG_INT32(1);
    1648           24953 :     bool        inverse = false;
    1649 ECB             :     float8      result;
    1650                 : 
    1651                 :     /* 0 is the only unsupported coordinate value */
    1652 GIC       24953 :     if (coord == 0)
    1653 CBC           1 :         ereport(ERROR,
    1654 ECB             :                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
    1655                 :                  errmsg("zero cube index is not defined")));
    1656                 : 
    1657                 :     /* Return inversed value for negative coordinate */
    1658 GIC       24952 :     if (coord < 0)
    1659 ECB             :     {
    1660 GIC       12473 :         coord = -coord;
    1661 CBC       12473 :         inverse = true;
    1662 ECB             :     }
    1663                 : 
    1664 GIC       24952 :     if (coord <= 2 * DIM(cube))
    1665 ECB             :     {
    1666                 :         /* dimension index */
    1667 GIC       24946 :         int         index = (coord - 1) / 2;
    1668 ECB             : 
    1669                 :         /* whether this is upper bound (lower bound otherwise) */
    1670 GIC       24946 :         bool        upper = ((coord - 1) % 2 == 1);
    1671 ECB             : 
    1672 GIC       24946 :         if (IS_POINT(cube))
    1673 ECB             :         {
    1674 GIC          30 :             result = cube->x[index];
    1675 ECB             :         }
    1676                 :         else
    1677                 :         {
    1678 GIC       24916 :             if (upper)
    1679 CBC       12457 :                 result = Max(cube->x[index], cube->x[index + DIM(cube)]);
    1680 ECB             :             else
    1681 GIC       12459 :                 result = Min(cube->x[index], cube->x[index + DIM(cube)]);
    1682 ECB             :         }
    1683                 :     }
    1684                 :     else
    1685                 :     {
    1686                 :         /*
    1687                 :          * Return zero if coordinate is out of bound.  That reproduces logic
    1688                 :          * of how cubes with low dimension number are expanded during GiST
    1689                 :          * indexing.
    1690                 :          */
    1691 GIC           6 :         result = 0.0;
    1692 ECB             :     }
    1693                 : 
    1694                 :     /* Inverse value if needed */
    1695 GIC       24952 :     if (inverse)
    1696 CBC       12473 :         result = -result;
    1697 ECB             : 
    1698 GIC       24952 :     PG_RETURN_FLOAT8(result);
    1699 ECB             : }
    1700                 : 
    1701                 : /* Increase or decrease box size by a radius in at least n dimensions. */
    1702                 : Datum
    1703 GIC          54 : cube_enlarge(PG_FUNCTION_ARGS)
    1704 ECB             : {
    1705 GIC          54 :     NDBOX      *a = PG_GETARG_NDBOX_P(0);
    1706 CBC          54 :     double      r = PG_GETARG_FLOAT8(1);
    1707              54 :     int32       n = PG_GETARG_INT32(2);
    1708 ECB             :     NDBOX      *result;
    1709 GIC          54 :     int         dim = 0;
    1710 ECB             :     int         size;
    1711                 :     int         i,
    1712                 :                 j;
    1713                 : 
    1714 GIC          54 :     if (n > CUBE_MAX_DIM)
    1715 LBC           0 :         n = CUBE_MAX_DIM;
    1716 GBC          54 :     if (r > 0 && n > 0)
    1717 CBC          40 :         dim = n;
    1718              54 :     if (DIM(a) > dim)
    1719              15 :         dim = DIM(a);
    1720 ECB             : 
    1721 GIC          54 :     size = CUBE_SIZE(dim);
    1722 CBC          54 :     result = (NDBOX *) palloc0(size);
    1723              54 :     SET_VARSIZE(result, size);
    1724              54 :     SET_DIM(result, dim);
    1725 ECB             : 
    1726 GIC         188 :     for (i = 0, j = dim; i < DIM(a); i++, j++)
    1727 ECB             :     {
    1728 GIC         134 :         if (LL_COORD(a, i) >= UR_COORD(a, i))
    1729 ECB             :         {
    1730 GIC         126 :             result->x[i] = UR_COORD(a, i) - r;
    1731 CBC         126 :             result->x[j] = LL_COORD(a, i) + r;
    1732 ECB             :         }
    1733                 :         else
    1734                 :         {
    1735 GIC           8 :             result->x[i] = LL_COORD(a, i) - r;
    1736 CBC           8 :             result->x[j] = UR_COORD(a, i) + r;
    1737 ECB             :         }
    1738 GIC         134 :         if (result->x[i] > result->x[j])
    1739 ECB             :         {
    1740 GIC           8 :             result->x[i] = (result->x[i] + result->x[j]) / 2;
    1741 CBC           8 :             result->x[j] = result->x[i];
    1742 ECB             :         }
    1743                 :     }
    1744                 :     /* dim > a->dim only if r > 0 */
    1745 GIC          58 :     for (; i < dim; i++, j++)
    1746 ECB             :     {
    1747 GIC           4 :         result->x[i] = -r;
    1748 CBC           4 :         result->x[j] = r;
    1749 ECB             :     }
    1750                 : 
    1751                 :     /*
    1752                 :      * Check if the result was in fact a point, and set the flag in the datum
    1753                 :      * accordingly. (we don't bother to repalloc it smaller)
    1754                 :      */
    1755 GIC          54 :     if (cube_is_point_internal(result))
    1756 ECB             :     {
    1757 GIC           8 :         size = POINT_SIZE(dim);
    1758 CBC           8 :         SET_VARSIZE(result, size);
    1759               8 :         SET_POINT_BIT(result);
    1760 ECB             :     }
    1761                 : 
    1762 GIC          54 :     PG_FREE_IF_COPY(a, 0);
    1763 CBC          54 :     PG_RETURN_NDBOX_P(result);
    1764 ECB             : }
    1765                 : 
    1766                 : /* Create a one dimensional box with identical upper and lower coordinates */
    1767                 : Datum
    1768 GIC         194 : cube_f8(PG_FUNCTION_ARGS)
    1769 ECB             : {
    1770 GIC         194 :     double      x = PG_GETARG_FLOAT8(0);
    1771 ECB             :     NDBOX      *result;
    1772                 :     int         size;
    1773                 : 
    1774 GIC         194 :     size = POINT_SIZE(1);
    1775 CBC         194 :     result = (NDBOX *) palloc0(size);
    1776             194 :     SET_VARSIZE(result, size);
    1777             194 :     SET_DIM(result, 1);
    1778             194 :     SET_POINT_BIT(result);
    1779             194 :     result->x[0] = x;
    1780 ECB             : 
    1781 GIC         194 :     PG_RETURN_NDBOX_P(result);
    1782 ECB             : }
    1783                 : 
    1784                 : /* Create a one dimensional box */
    1785                 : Datum
    1786 GIC          12 : cube_f8_f8(PG_FUNCTION_ARGS)
    1787 ECB             : {
    1788 GIC          12 :     double      x0 = PG_GETARG_FLOAT8(0);
    1789 CBC          12 :     double      x1 = PG_GETARG_FLOAT8(1);
    1790 ECB             :     NDBOX      *result;
    1791                 :     int         size;
    1792                 : 
    1793 GIC          12 :     if (x0 == x1)
    1794 ECB             :     {
    1795 GIC           4 :         size = POINT_SIZE(1);
    1796 CBC           4 :         result = (NDBOX *) palloc0(size);
    1797               4 :         SET_VARSIZE(result, size);
    1798               4 :         SET_DIM(result, 1);
    1799               4 :         SET_POINT_BIT(result);
    1800               4 :         result->x[0] = x0;
    1801 ECB             :     }
    1802                 :     else
    1803                 :     {
    1804 GIC           8 :         size = CUBE_SIZE(1);
    1805 CBC           8 :         result = (NDBOX *) palloc0(size);
    1806               8 :         SET_VARSIZE(result, size);
    1807               8 :         SET_DIM(result, 1);
    1808               8 :         result->x[0] = x0;
    1809               8 :         result->x[1] = x1;
    1810 ECB             :     }
    1811                 : 
    1812 GIC          12 :     PG_RETURN_NDBOX_P(result);
    1813 ECB             : }
    1814                 : 
    1815                 : /* Add a dimension to an existing cube with the same values for the new
    1816                 :    coordinate */
    1817                 : Datum
    1818 GIC         387 : cube_c_f8(PG_FUNCTION_ARGS)
    1819 ECB             : {
    1820 GIC         387 :     NDBOX      *cube = PG_GETARG_NDBOX_P(0);
    1821 CBC         387 :     double      x = PG_GETARG_FLOAT8(1);
    1822 ECB             :     NDBOX      *result;
    1823                 :     int         size;
    1824                 :     int         i;
    1825                 : 
    1826 GIC         387 :     if (DIM(cube) + 1 > CUBE_MAX_DIM)
    1827 CBC           1 :         ereport(ERROR,
    1828 ECB             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1829                 :                  errmsg("can't extend cube"),
    1830                 :                  errdetail("A cube cannot have more than %d dimensions.",
    1831                 :                            CUBE_MAX_DIM)));
    1832                 : 
    1833 GIC         386 :     if (IS_POINT(cube))
    1834 ECB             :     {
    1835 GIC         383 :         size = POINT_SIZE((DIM(cube) + 1));
    1836 CBC         383 :         result = (NDBOX *) palloc0(size);
    1837             383 :         SET_VARSIZE(result, size);
    1838             383 :         SET_DIM(result, DIM(cube) + 1);
    1839             383 :         SET_POINT_BIT(result);
    1840             957 :         for (i = 0; i < DIM(cube); i++)
    1841             574 :             result->x[i] = cube->x[i];
    1842             383 :         result->x[DIM(result) - 1] = x;
    1843 ECB             :     }
    1844                 :     else
    1845                 :     {
    1846 GIC           3 :         size = CUBE_SIZE((DIM(cube) + 1));
    1847 CBC           3 :         result = (NDBOX *) palloc0(size);
    1848               3 :         SET_VARSIZE(result, size);
    1849               3 :         SET_DIM(result, DIM(cube) + 1);
    1850               7 :         for (i = 0; i < DIM(cube); i++)
    1851 ECB             :         {
    1852 GIC           4 :             result->x[i] = cube->x[i];
    1853 CBC           4 :             result->x[DIM(result) + i] = cube->x[DIM(cube) + i];
    1854 ECB             :         }
    1855 GIC           3 :         result->x[DIM(result) - 1] = x;
    1856 CBC           3 :         result->x[2 * DIM(result) - 1] = x;
    1857 ECB             :     }
    1858                 : 
    1859 GIC         386 :     PG_FREE_IF_COPY(cube, 0);
    1860 CBC         386 :     PG_RETURN_NDBOX_P(result);
    1861 ECB             : }
    1862                 : 
    1863                 : /* Add a dimension to an existing cube */
    1864                 : Datum
    1865 GIC           9 : cube_c_f8_f8(PG_FUNCTION_ARGS)
    1866 ECB             : {
    1867 GIC           9 :     NDBOX      *cube = PG_GETARG_NDBOX_P(0);
    1868 CBC           9 :     double      x1 = PG_GETARG_FLOAT8(1);
    1869               9 :     double      x2 = PG_GETARG_FLOAT8(2);
    1870 ECB             :     NDBOX      *result;
    1871                 :     int         size;
    1872                 :     int         i;
    1873                 : 
    1874 GIC           9 :     if (DIM(cube) + 1 > CUBE_MAX_DIM)
    1875 CBC           1 :         ereport(ERROR,
    1876 ECB             :                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
    1877                 :                  errmsg("can't extend cube"),
    1878                 :                  errdetail("A cube cannot have more than %d dimensions.",
    1879                 :                            CUBE_MAX_DIM)));
    1880                 : 
    1881 GIC           8 :     if (IS_POINT(cube) && (x1 == x2))
    1882 ECB             :     {
    1883 GIC           1 :         size = POINT_SIZE((DIM(cube) + 1));
    1884 CBC           1 :         result = (NDBOX *) palloc0(size);
    1885               1 :         SET_VARSIZE(result, size);
    1886               1 :         SET_DIM(result, DIM(cube) + 1);
    1887               1 :         SET_POINT_BIT(result);
    1888               2 :         for (i = 0; i < DIM(cube); i++)
    1889               1 :             result->x[i] = cube->x[i];
    1890               1 :         result->x[DIM(result) - 1] = x1;
    1891 ECB             :     }
    1892                 :     else
    1893                 :     {
    1894 GIC           7 :         size = CUBE_SIZE((DIM(cube) + 1));
    1895 CBC           7 :         result = (NDBOX *) palloc0(size);
    1896               7 :         SET_VARSIZE(result, size);
    1897               7 :         SET_DIM(result, DIM(cube) + 1);
    1898              15 :         for (i = 0; i < DIM(cube); i++)
    1899 ECB             :         {
    1900 GIC           8 :             result->x[i] = LL_COORD(cube, i);
    1901 CBC           8 :             result->x[DIM(result) + i] = UR_COORD(cube, i);
    1902 ECB             :         }
    1903 GIC           7 :         result->x[DIM(result) - 1] = x1;
    1904 CBC           7 :         result->x[2 * DIM(result) - 1] = x2;
    1905 ECB             :     }
    1906                 : 
    1907 GIC           8 :     PG_FREE_IF_COPY(cube, 0);
    1908 CBC           8 :     PG_RETURN_NDBOX_P(result);
    1909 ECB             : }
        

Generated by: LCOV version v1.16-55-g56c0a2a