LCOV - code coverage report
Current view: top level - contrib/cube - cube.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 1022 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 95 0
Legend: Lines:     hit not hit

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

Generated by: LCOV version 2.3.2-1