Header And Logo

PostgreSQL
| The world's most advanced open source database.

cube.c

Go to the documentation of this file.
00001 /******************************************************************************
00002   contrib/cube/cube.c
00003 
00004   This file contains routines that can be bound to a Postgres backend and
00005   called by the backend in the process of processing queries.  The calling
00006   format for these routines is dictated by Postgres architecture.
00007 ******************************************************************************/
00008 
00009 #include "postgres.h"
00010 
00011 #include <float.h>
00012 #include <math.h>
00013 
00014 #include "access/gist.h"
00015 #include "access/skey.h"
00016 #include "utils/array.h"
00017 #include "utils/builtins.h"
00018 
00019 #include "cubedata.h"
00020 
00021 PG_MODULE_MAGIC;
00022 
00023 /*
00024  * Taken from the intarray contrib header
00025  */
00026 #define ARRPTR(x)  ( (double *) ARR_DATA_PTR(x) )
00027 #define ARRNELEMS(x)  ArrayGetNItems( ARR_NDIM(x), ARR_DIMS(x))
00028 
00029 extern int  cube_yyparse();
00030 extern void cube_yyerror(const char *message);
00031 extern void cube_scanner_init(const char *str);
00032 extern void cube_scanner_finish(void);
00033 
00034 /*
00035 ** Input/Output routines
00036 */
00037 PG_FUNCTION_INFO_V1(cube_in);
00038 PG_FUNCTION_INFO_V1(cube_a_f8_f8);
00039 PG_FUNCTION_INFO_V1(cube_a_f8);
00040 PG_FUNCTION_INFO_V1(cube_out);
00041 PG_FUNCTION_INFO_V1(cube_f8);
00042 PG_FUNCTION_INFO_V1(cube_f8_f8);
00043 PG_FUNCTION_INFO_V1(cube_c_f8);
00044 PG_FUNCTION_INFO_V1(cube_c_f8_f8);
00045 PG_FUNCTION_INFO_V1(cube_dim);
00046 PG_FUNCTION_INFO_V1(cube_ll_coord);
00047 PG_FUNCTION_INFO_V1(cube_ur_coord);
00048 PG_FUNCTION_INFO_V1(cube_subset);
00049 
00050 Datum       cube_in(PG_FUNCTION_ARGS);
00051 Datum       cube_a_f8_f8(PG_FUNCTION_ARGS);
00052 Datum       cube_a_f8(PG_FUNCTION_ARGS);
00053 Datum       cube_out(PG_FUNCTION_ARGS);
00054 Datum       cube_f8(PG_FUNCTION_ARGS);
00055 Datum       cube_f8_f8(PG_FUNCTION_ARGS);
00056 Datum       cube_c_f8(PG_FUNCTION_ARGS);
00057 Datum       cube_c_f8_f8(PG_FUNCTION_ARGS);
00058 Datum       cube_dim(PG_FUNCTION_ARGS);
00059 Datum       cube_ll_coord(PG_FUNCTION_ARGS);
00060 Datum       cube_ur_coord(PG_FUNCTION_ARGS);
00061 Datum       cube_subset(PG_FUNCTION_ARGS);
00062 
00063 /*
00064 ** GiST support methods
00065 */
00066 
00067 PG_FUNCTION_INFO_V1(g_cube_consistent);
00068 PG_FUNCTION_INFO_V1(g_cube_compress);
00069 PG_FUNCTION_INFO_V1(g_cube_decompress);
00070 PG_FUNCTION_INFO_V1(g_cube_penalty);
00071 PG_FUNCTION_INFO_V1(g_cube_picksplit);
00072 PG_FUNCTION_INFO_V1(g_cube_union);
00073 PG_FUNCTION_INFO_V1(g_cube_same);
00074 
00075 Datum       g_cube_consistent(PG_FUNCTION_ARGS);
00076 Datum       g_cube_compress(PG_FUNCTION_ARGS);
00077 Datum       g_cube_decompress(PG_FUNCTION_ARGS);
00078 Datum       g_cube_penalty(PG_FUNCTION_ARGS);
00079 Datum       g_cube_picksplit(PG_FUNCTION_ARGS);
00080 Datum       g_cube_union(PG_FUNCTION_ARGS);
00081 Datum       g_cube_same(PG_FUNCTION_ARGS);
00082 
00083 /*
00084 ** B-tree support functions
00085 */
00086 PG_FUNCTION_INFO_V1(cube_eq);
00087 PG_FUNCTION_INFO_V1(cube_ne);
00088 PG_FUNCTION_INFO_V1(cube_lt);
00089 PG_FUNCTION_INFO_V1(cube_gt);
00090 PG_FUNCTION_INFO_V1(cube_le);
00091 PG_FUNCTION_INFO_V1(cube_ge);
00092 PG_FUNCTION_INFO_V1(cube_cmp);
00093 
00094 Datum       cube_eq(PG_FUNCTION_ARGS);
00095 Datum       cube_ne(PG_FUNCTION_ARGS);
00096 Datum       cube_lt(PG_FUNCTION_ARGS);
00097 Datum       cube_gt(PG_FUNCTION_ARGS);
00098 Datum       cube_le(PG_FUNCTION_ARGS);
00099 Datum       cube_ge(PG_FUNCTION_ARGS);
00100 Datum       cube_cmp(PG_FUNCTION_ARGS);
00101 
00102 /*
00103 ** R-tree support functions
00104 */
00105 
00106 PG_FUNCTION_INFO_V1(cube_contains);
00107 PG_FUNCTION_INFO_V1(cube_contained);
00108 PG_FUNCTION_INFO_V1(cube_overlap);
00109 PG_FUNCTION_INFO_V1(cube_union);
00110 PG_FUNCTION_INFO_V1(cube_inter);
00111 PG_FUNCTION_INFO_V1(cube_size);
00112 
00113 Datum       cube_contains(PG_FUNCTION_ARGS);
00114 Datum       cube_contained(PG_FUNCTION_ARGS);
00115 Datum       cube_overlap(PG_FUNCTION_ARGS);
00116 Datum       cube_union(PG_FUNCTION_ARGS);
00117 Datum       cube_inter(PG_FUNCTION_ARGS);
00118 Datum       cube_size(PG_FUNCTION_ARGS);
00119 
00120 /*
00121 ** miscellaneous
00122 */
00123 PG_FUNCTION_INFO_V1(cube_distance);
00124 PG_FUNCTION_INFO_V1(cube_is_point);
00125 PG_FUNCTION_INFO_V1(cube_enlarge);
00126 
00127 Datum       cube_distance(PG_FUNCTION_ARGS);
00128 Datum       cube_is_point(PG_FUNCTION_ARGS);
00129 Datum       cube_enlarge(PG_FUNCTION_ARGS);
00130 
00131 /*
00132 ** For internal use only
00133 */
00134 int32       cube_cmp_v0(NDBOX *a, NDBOX *b);
00135 bool        cube_contains_v0(NDBOX *a, NDBOX *b);
00136 bool        cube_overlap_v0(NDBOX *a, NDBOX *b);
00137 NDBOX      *cube_union_v0(NDBOX *a, NDBOX *b);
00138 void        rt_cube_size(NDBOX *a, double *sz);
00139 NDBOX      *g_cube_binary_union(NDBOX *r1, NDBOX *r2, int *sizep);
00140 bool        g_cube_leaf_consistent(NDBOX *key, NDBOX *query, StrategyNumber strategy);
00141 bool        g_cube_internal_consistent(NDBOX *key, NDBOX *query, StrategyNumber strategy);
00142 
00143 /*
00144 ** Auxiliary funxtions
00145 */
00146 static double distance_1D(double a1, double a2, double b1, double b2);
00147 
00148 
00149 /*****************************************************************************
00150  * Input/Output functions
00151  *****************************************************************************/
00152 
00153 /* NdBox = [(lowerleft),(upperright)] */
00154 /* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */
00155 Datum
00156 cube_in(PG_FUNCTION_ARGS)
00157 {
00158     char       *str = PG_GETARG_CSTRING(0);
00159     void       *result;
00160 
00161     cube_scanner_init(str);
00162 
00163     if (cube_yyparse(&result) != 0)
00164         cube_yyerror("bogus input");
00165 
00166     cube_scanner_finish();
00167 
00168     PG_RETURN_NDBOX(result);
00169 }
00170 
00171 
00172 /*
00173 ** Allows the construction of a cube from 2 float[]'s
00174 */
00175 Datum
00176 cube_a_f8_f8(PG_FUNCTION_ARGS)
00177 {
00178     ArrayType  *ur = PG_GETARG_ARRAYTYPE_P(0);
00179     ArrayType  *ll = PG_GETARG_ARRAYTYPE_P(1);
00180     NDBOX      *result;
00181     int         i;
00182     int         dim;
00183     int         size;
00184     double     *dur,
00185                *dll;
00186 
00187     if (array_contains_nulls(ur) || array_contains_nulls(ll))
00188         ereport(ERROR,
00189                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
00190                  errmsg("cannot work with arrays containing NULLs")));
00191 
00192     dim = ARRNELEMS(ur);
00193     if (ARRNELEMS(ll) != dim)
00194         ereport(ERROR,
00195                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
00196                  errmsg("UR and LL arrays must be of same length")));
00197 
00198     dur = ARRPTR(ur);
00199     dll = ARRPTR(ll);
00200 
00201     size = offsetof(NDBOX, x[0]) +sizeof(double) * 2 * dim;
00202     result = (NDBOX *) palloc0(size);
00203     SET_VARSIZE(result, size);
00204     result->dim = dim;
00205 
00206     for (i = 0; i < dim; i++)
00207     {
00208         result->x[i] = dur[i];
00209         result->x[i + dim] = dll[i];
00210     }
00211 
00212     PG_RETURN_NDBOX(result);
00213 }
00214 
00215 /*
00216 ** Allows the construction of a zero-volume cube from a float[]
00217 */
00218 Datum
00219 cube_a_f8(PG_FUNCTION_ARGS)
00220 {
00221     ArrayType  *ur = PG_GETARG_ARRAYTYPE_P(0);
00222     NDBOX      *result;
00223     int         i;
00224     int         dim;
00225     int         size;
00226     double     *dur;
00227 
00228     if (array_contains_nulls(ur))
00229         ereport(ERROR,
00230                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
00231                  errmsg("cannot work with arrays containing NULLs")));
00232 
00233     dim = ARRNELEMS(ur);
00234 
00235     dur = ARRPTR(ur);
00236 
00237     size = offsetof(NDBOX, x[0]) +sizeof(double) * 2 * dim;
00238     result = (NDBOX *) palloc0(size);
00239     SET_VARSIZE(result, size);
00240     result->dim = dim;
00241 
00242     for (i = 0; i < dim; i++)
00243     {
00244         result->x[i] = dur[i];
00245         result->x[i + dim] = dur[i];
00246     }
00247 
00248     PG_RETURN_NDBOX(result);
00249 }
00250 
00251 Datum
00252 cube_subset(PG_FUNCTION_ARGS)
00253 {
00254     NDBOX      *c = PG_GETARG_NDBOX(0);
00255     ArrayType  *idx = PG_GETARG_ARRAYTYPE_P(1);
00256     NDBOX      *result;
00257     int         size,
00258                 dim,
00259                 i;
00260     int        *dx;
00261 
00262     if (array_contains_nulls(idx))
00263         ereport(ERROR,
00264                 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
00265                  errmsg("cannot work with arrays containing NULLs")));
00266 
00267     dx = (int32 *) ARR_DATA_PTR(idx);
00268 
00269     dim = ARRNELEMS(idx);
00270     size = offsetof(NDBOX, x[0]) +sizeof(double) * 2 * dim;
00271     result = (NDBOX *) palloc0(size);
00272     SET_VARSIZE(result, size);
00273     result->dim = dim;
00274 
00275     for (i = 0; i < dim; i++)
00276     {
00277         if ((dx[i] <= 0) || (dx[i] > c->dim))
00278         {
00279             pfree(result);
00280             ereport(ERROR,
00281                     (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
00282                      errmsg("Index out of bounds")));
00283         }
00284         result->x[i] = c->x[dx[i] - 1];
00285         result->x[i + dim] = c->x[dx[i] + c->dim - 1];
00286     }
00287 
00288     PG_FREE_IF_COPY(c, 0);
00289     PG_RETURN_NDBOX(result);
00290 }
00291 
00292 Datum
00293 cube_out(PG_FUNCTION_ARGS)
00294 {
00295     NDBOX      *cube = PG_GETARG_NDBOX(0);
00296     StringInfoData buf;
00297     int         dim = cube->dim;
00298     bool        equal = true;
00299     int         i;
00300     int         ndig;
00301 
00302     initStringInfo(&buf);
00303 
00304     /*
00305      * Get the number of digits to display.
00306      */
00307     ndig = DBL_DIG + extra_float_digits;
00308     if (ndig < 1)
00309         ndig = 1;
00310 
00311     /*
00312      * while printing the first (LL) corner, check if it is equal to the
00313      * second one
00314      */
00315     appendStringInfoChar(&buf, '(');
00316     for (i = 0; i < dim; i++)
00317     {
00318         if (i > 0)
00319             appendStringInfo(&buf, ", ");
00320         appendStringInfo(&buf, "%.*g", ndig, cube->x[i]);
00321         if (cube->x[i] != cube->x[i + dim])
00322             equal = false;
00323     }
00324     appendStringInfoChar(&buf, ')');
00325 
00326     if (!equal)
00327     {
00328         appendStringInfo(&buf, ",(");
00329         for (i = 0; i < dim; i++)
00330         {
00331             if (i > 0)
00332                 appendStringInfo(&buf, ", ");
00333             appendStringInfo(&buf, "%.*g", ndig, cube->x[i + dim]);
00334         }
00335         appendStringInfoChar(&buf, ')');
00336     }
00337 
00338     PG_FREE_IF_COPY(cube, 0);
00339     PG_RETURN_CSTRING(buf.data);
00340 }
00341 
00342 
00343 /*****************************************************************************
00344  *                         GiST functions
00345  *****************************************************************************/
00346 
00347 /*
00348 ** The GiST Consistent method for boxes
00349 ** Should return false if for all data items x below entry,
00350 ** the predicate x op query == FALSE, where op is the oper
00351 ** corresponding to strategy in the pg_amop table.
00352 */
00353 Datum
00354 g_cube_consistent(PG_FUNCTION_ARGS)
00355 {
00356     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
00357     NDBOX      *query = PG_GETARG_NDBOX(1);
00358     StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
00359 
00360     /* Oid      subtype = PG_GETARG_OID(3); */
00361     bool       *recheck = (bool *) PG_GETARG_POINTER(4);
00362     bool        res;
00363 
00364     /* All cases served by this function are exact */
00365     *recheck = false;
00366 
00367     /*
00368      * if entry is not leaf, use g_cube_internal_consistent, else use
00369      * g_cube_leaf_consistent
00370      */
00371     if (GIST_LEAF(entry))
00372         res = g_cube_leaf_consistent(DatumGetNDBOX(entry->key),
00373                                      query, strategy);
00374     else
00375         res = g_cube_internal_consistent(DatumGetNDBOX(entry->key),
00376                                          query, strategy);
00377 
00378     PG_FREE_IF_COPY(query, 1);
00379     PG_RETURN_BOOL(res);
00380 }
00381 
00382 
00383 /*
00384 ** The GiST Union method for boxes
00385 ** returns the minimal bounding box that encloses all the entries in entryvec
00386 */
00387 Datum
00388 g_cube_union(PG_FUNCTION_ARGS)
00389 {
00390     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
00391     int        *sizep = (int *) PG_GETARG_POINTER(1);
00392     NDBOX      *out = (NDBOX *) NULL;
00393     NDBOX      *tmp;
00394     int         i;
00395 
00396     /*
00397      * fprintf(stderr, "union\n");
00398      */
00399     tmp = DatumGetNDBOX(entryvec->vector[0].key);
00400 
00401     /*
00402      * sizep = sizeof(NDBOX); -- NDBOX has variable size
00403      */
00404     *sizep = VARSIZE(tmp);
00405 
00406     for (i = 1; i < entryvec->n; i++)
00407     {
00408         out = g_cube_binary_union(tmp,
00409                                   DatumGetNDBOX(entryvec->vector[i].key),
00410                                   sizep);
00411         tmp = out;
00412     }
00413 
00414     PG_RETURN_POINTER(out);
00415 }
00416 
00417 /*
00418 ** GiST Compress and Decompress methods for boxes
00419 ** do not do anything.
00420 */
00421 
00422 Datum
00423 g_cube_compress(PG_FUNCTION_ARGS)
00424 {
00425     PG_RETURN_DATUM(PG_GETARG_DATUM(0));
00426 }
00427 
00428 Datum
00429 g_cube_decompress(PG_FUNCTION_ARGS)
00430 {
00431     GISTENTRY  *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
00432     NDBOX      *key = DatumGetNDBOX(PG_DETOAST_DATUM(entry->key));
00433 
00434     if (key != DatumGetNDBOX(entry->key))
00435     {
00436         GISTENTRY  *retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
00437 
00438         gistentryinit(*retval, PointerGetDatum(key),
00439                       entry->rel, entry->page,
00440                       entry->offset, FALSE);
00441         PG_RETURN_POINTER(retval);
00442     }
00443     PG_RETURN_POINTER(entry);
00444 }
00445 
00446 
00447 /*
00448 ** The GiST Penalty method for boxes
00449 ** As in the R-tree paper, we use change in area as our penalty metric
00450 */
00451 Datum
00452 g_cube_penalty(PG_FUNCTION_ARGS)
00453 {
00454     GISTENTRY  *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
00455     GISTENTRY  *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
00456     float      *result = (float *) PG_GETARG_POINTER(2);
00457     NDBOX      *ud;
00458     double      tmp1,
00459                 tmp2;
00460 
00461     ud = cube_union_v0(DatumGetNDBOX(origentry->key),
00462                        DatumGetNDBOX(newentry->key));
00463     rt_cube_size(ud, &tmp1);
00464     rt_cube_size(DatumGetNDBOX(origentry->key), &tmp2);
00465     *result = (float) (tmp1 - tmp2);
00466 
00467     /*
00468      * fprintf(stderr, "penalty\n"); fprintf(stderr, "\t%g\n", *result);
00469      */
00470     PG_RETURN_FLOAT8(*result);
00471 }
00472 
00473 
00474 
00475 /*
00476 ** The GiST PickSplit method for boxes
00477 ** We use Guttman's poly time split algorithm
00478 */
00479 Datum
00480 g_cube_picksplit(PG_FUNCTION_ARGS)
00481 {
00482     GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
00483     GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
00484     OffsetNumber i,
00485                 j;
00486     NDBOX      *datum_alpha,
00487                *datum_beta;
00488     NDBOX      *datum_l,
00489                *datum_r;
00490     NDBOX      *union_d,
00491                *union_dl,
00492                *union_dr;
00493     NDBOX      *inter_d;
00494     bool        firsttime;
00495     double      size_alpha,
00496                 size_beta,
00497                 size_union,
00498                 size_inter;
00499     double      size_waste,
00500                 waste;
00501     double      size_l,
00502                 size_r;
00503     int         nbytes;
00504     OffsetNumber seed_1 = 1,
00505                 seed_2 = 2;
00506     OffsetNumber *left,
00507                *right;
00508     OffsetNumber maxoff;
00509 
00510     /*
00511      * fprintf(stderr, "picksplit\n");
00512      */
00513     maxoff = entryvec->n - 2;
00514     nbytes = (maxoff + 2) * sizeof(OffsetNumber);
00515     v->spl_left = (OffsetNumber *) palloc(nbytes);
00516     v->spl_right = (OffsetNumber *) palloc(nbytes);
00517 
00518     firsttime = true;
00519     waste = 0.0;
00520 
00521     for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i))
00522     {
00523         datum_alpha = DatumGetNDBOX(entryvec->vector[i].key);
00524         for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j))
00525         {
00526             datum_beta = DatumGetNDBOX(entryvec->vector[j].key);
00527 
00528             /* compute the wasted space by unioning these guys */
00529             /* size_waste = size_union - size_inter; */
00530             union_d = cube_union_v0(datum_alpha, datum_beta);
00531             rt_cube_size(union_d, &size_union);
00532             inter_d = DatumGetNDBOX(DirectFunctionCall2(cube_inter,
00533                           entryvec->vector[i].key, entryvec->vector[j].key));
00534             rt_cube_size(inter_d, &size_inter);
00535             size_waste = size_union - size_inter;
00536 
00537             /*
00538              * are these a more promising split than what we've already seen?
00539              */
00540 
00541             if (size_waste > waste || firsttime)
00542             {
00543                 waste = size_waste;
00544                 seed_1 = i;
00545                 seed_2 = j;
00546                 firsttime = false;
00547             }
00548         }
00549     }
00550 
00551     left = v->spl_left;
00552     v->spl_nleft = 0;
00553     right = v->spl_right;
00554     v->spl_nright = 0;
00555 
00556     datum_alpha = DatumGetNDBOX(entryvec->vector[seed_1].key);
00557     datum_l = cube_union_v0(datum_alpha, datum_alpha);
00558     rt_cube_size(datum_l, &size_l);
00559     datum_beta = DatumGetNDBOX(entryvec->vector[seed_2].key);
00560     datum_r = cube_union_v0(datum_beta, datum_beta);
00561     rt_cube_size(datum_r, &size_r);
00562 
00563     /*
00564      * Now split up the regions between the two seeds.  An important property
00565      * of this split algorithm is that the split vector v has the indices of
00566      * items to be split in order in its left and right vectors.  We exploit
00567      * this property by doing a merge in the code that actually splits the
00568      * page.
00569      *
00570      * For efficiency, we also place the new index tuple in this loop. This is
00571      * handled at the very end, when we have placed all the existing tuples
00572      * and i == maxoff + 1.
00573      */
00574 
00575     maxoff = OffsetNumberNext(maxoff);
00576     for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
00577     {
00578         /*
00579          * If we've already decided where to place this item, just put it on
00580          * the right list.  Otherwise, we need to figure out which page needs
00581          * the least enlargement in order to store the item.
00582          */
00583 
00584         if (i == seed_1)
00585         {
00586             *left++ = i;
00587             v->spl_nleft++;
00588             continue;
00589         }
00590         else if (i == seed_2)
00591         {
00592             *right++ = i;
00593             v->spl_nright++;
00594             continue;
00595         }
00596 
00597         /* okay, which page needs least enlargement? */
00598         datum_alpha = DatumGetNDBOX(entryvec->vector[i].key);
00599         union_dl = cube_union_v0(datum_l, datum_alpha);
00600         union_dr = cube_union_v0(datum_r, datum_alpha);
00601         rt_cube_size(union_dl, &size_alpha);
00602         rt_cube_size(union_dr, &size_beta);
00603 
00604         /* pick which page to add it to */
00605         if (size_alpha - size_l < size_beta - size_r)
00606         {
00607             datum_l = union_dl;
00608             size_l = size_alpha;
00609             *left++ = i;
00610             v->spl_nleft++;
00611         }
00612         else
00613         {
00614             datum_r = union_dr;
00615             size_r = size_beta;
00616             *right++ = i;
00617             v->spl_nright++;
00618         }
00619     }
00620     *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */
00621 
00622     v->spl_ldatum = PointerGetDatum(datum_l);
00623     v->spl_rdatum = PointerGetDatum(datum_r);
00624 
00625     PG_RETURN_POINTER(v);
00626 }
00627 
00628 /*
00629 ** Equality method
00630 */
00631 Datum
00632 g_cube_same(PG_FUNCTION_ARGS)
00633 {
00634     NDBOX      *b1 = PG_GETARG_NDBOX(0);
00635     NDBOX      *b2 = PG_GETARG_NDBOX(1);
00636     bool       *result = (bool *) PG_GETARG_POINTER(2);
00637 
00638     if (cube_cmp_v0(b1, b2) == 0)
00639         *result = TRUE;
00640     else
00641         *result = FALSE;
00642 
00643     /*
00644      * fprintf(stderr, "same: %s\n", (*result ? "TRUE" : "FALSE" ));
00645      */
00646     PG_RETURN_NDBOX(result);
00647 }
00648 
00649 /*
00650 ** SUPPORT ROUTINES
00651 */
00652 bool
00653 g_cube_leaf_consistent(NDBOX *key,
00654                        NDBOX *query,
00655                        StrategyNumber strategy)
00656 {
00657     bool        retval;
00658 
00659     /*
00660      * fprintf(stderr, "leaf_consistent, %d\n", strategy);
00661      */
00662     switch (strategy)
00663     {
00664         case RTOverlapStrategyNumber:
00665             retval = (bool) cube_overlap_v0(key, query);
00666             break;
00667         case RTSameStrategyNumber:
00668             retval = (bool) (cube_cmp_v0(key, query) == 0);
00669             break;
00670         case RTContainsStrategyNumber:
00671         case RTOldContainsStrategyNumber:
00672             retval = (bool) cube_contains_v0(key, query);
00673             break;
00674         case RTContainedByStrategyNumber:
00675         case RTOldContainedByStrategyNumber:
00676             retval = (bool) cube_contains_v0(query, key);
00677             break;
00678         default:
00679             retval = FALSE;
00680     }
00681     return (retval);
00682 }
00683 
00684 bool
00685 g_cube_internal_consistent(NDBOX *key,
00686                            NDBOX *query,
00687                            StrategyNumber strategy)
00688 {
00689     bool        retval;
00690 
00691     /*
00692      * fprintf(stderr, "internal_consistent, %d\n", strategy);
00693      */
00694     switch (strategy)
00695     {
00696         case RTOverlapStrategyNumber:
00697             retval = (bool) cube_overlap_v0(key, query);
00698             break;
00699         case RTSameStrategyNumber:
00700         case RTContainsStrategyNumber:
00701         case RTOldContainsStrategyNumber:
00702             retval = (bool) cube_contains_v0(key, query);
00703             break;
00704         case RTContainedByStrategyNumber:
00705         case RTOldContainedByStrategyNumber:
00706             retval = (bool) cube_overlap_v0(key, query);
00707             break;
00708         default:
00709             retval = FALSE;
00710     }
00711     return (retval);
00712 }
00713 
00714 NDBOX *
00715 g_cube_binary_union(NDBOX *r1, NDBOX *r2, int *sizep)
00716 {
00717     NDBOX      *retval;
00718 
00719     retval = cube_union_v0(r1, r2);
00720     *sizep = VARSIZE(retval);
00721 
00722     return (retval);
00723 }
00724 
00725 
00726 /* cube_union_v0 */
00727 NDBOX *
00728 cube_union_v0(NDBOX *a, NDBOX *b)
00729 {
00730     int         i;
00731     NDBOX      *result;
00732 
00733     if (a->dim >= b->dim)
00734     {
00735         result = palloc0(VARSIZE(a));
00736         SET_VARSIZE(result, VARSIZE(a));
00737         result->dim = a->dim;
00738     }
00739     else
00740     {
00741         result = palloc0(VARSIZE(b));
00742         SET_VARSIZE(result, VARSIZE(b));
00743         result->dim = b->dim;
00744     }
00745 
00746     /* swap the box pointers if needed */
00747     if (a->dim < b->dim)
00748     {
00749         NDBOX      *tmp = b;
00750 
00751         b = a;
00752         a = tmp;
00753     }
00754 
00755     /*
00756      * use the potentially smaller of the two boxes (b) to fill in the result,
00757      * padding absent dimensions with zeroes
00758      */
00759     for (i = 0; i < b->dim; i++)
00760     {
00761         result->x[i] = Min(b->x[i], b->x[i + b->dim]);
00762         result->x[i + a->dim] = Max(b->x[i], b->x[i + b->dim]);
00763     }
00764     for (i = b->dim; i < a->dim; i++)
00765     {
00766         result->x[i] = 0;
00767         result->x[i + a->dim] = 0;
00768     }
00769 
00770     /* compute the union */
00771     for (i = 0; i < a->dim; i++)
00772     {
00773         result->x[i] =
00774             Min(Min(a->x[i], a->x[i + a->dim]), result->x[i]);
00775         result->x[i + a->dim] = Max(Max(a->x[i],
00776                                    a->x[i + a->dim]), result->x[i + a->dim]);
00777     }
00778 
00779     return (result);
00780 }
00781 
00782 Datum
00783 cube_union(PG_FUNCTION_ARGS)
00784 {
00785     NDBOX      *a = PG_GETARG_NDBOX(0);
00786     NDBOX      *b = PG_GETARG_NDBOX(1);
00787     NDBOX      *res;
00788 
00789     res = cube_union_v0(a, b);
00790 
00791     PG_FREE_IF_COPY(a, 0);
00792     PG_FREE_IF_COPY(b, 1);
00793     PG_RETURN_NDBOX(res);
00794 }
00795 
00796 /* cube_inter */
00797 Datum
00798 cube_inter(PG_FUNCTION_ARGS)
00799 {
00800     NDBOX      *a = PG_GETARG_NDBOX(0);
00801     NDBOX      *b = PG_GETARG_NDBOX(1);
00802     NDBOX      *result;
00803     bool        swapped = false;
00804     int         i;
00805 
00806     if (a->dim >= b->dim)
00807     {
00808         result = palloc0(VARSIZE(a));
00809         SET_VARSIZE(result, VARSIZE(a));
00810         result->dim = a->dim;
00811     }
00812     else
00813     {
00814         result = palloc0(VARSIZE(b));
00815         SET_VARSIZE(result, VARSIZE(b));
00816         result->dim = b->dim;
00817     }
00818 
00819     /* swap the box pointers if needed */
00820     if (a->dim < b->dim)
00821     {
00822         NDBOX      *tmp = b;
00823 
00824         b = a;
00825         a = tmp;
00826         swapped = true;
00827     }
00828 
00829     /*
00830      * use the potentially  smaller of the two boxes (b) to fill in the
00831      * result, padding absent dimensions with zeroes
00832      */
00833     for (i = 0; i < b->dim; i++)
00834     {
00835         result->x[i] = Min(b->x[i], b->x[i + b->dim]);
00836         result->x[i + a->dim] = Max(b->x[i], b->x[i + b->dim]);
00837     }
00838     for (i = b->dim; i < a->dim; i++)
00839     {
00840         result->x[i] = 0;
00841         result->x[i + a->dim] = 0;
00842     }
00843 
00844     /* compute the intersection */
00845     for (i = 0; i < a->dim; i++)
00846     {
00847         result->x[i] =
00848             Max(Min(a->x[i], a->x[i + a->dim]), result->x[i]);
00849         result->x[i + a->dim] = Min(Max(a->x[i],
00850                                    a->x[i + a->dim]), result->x[i + a->dim]);
00851     }
00852 
00853     if (swapped)
00854     {
00855         PG_FREE_IF_COPY(b, 0);
00856         PG_FREE_IF_COPY(a, 1);
00857     }
00858     else
00859     {
00860         PG_FREE_IF_COPY(a, 0);
00861         PG_FREE_IF_COPY(b, 1);
00862     }
00863 
00864     /*
00865      * Is it OK to return a non-null intersection for non-overlapping boxes?
00866      */
00867     PG_RETURN_NDBOX(result);
00868 }
00869 
00870 /* cube_size */
00871 Datum
00872 cube_size(PG_FUNCTION_ARGS)
00873 {
00874     NDBOX      *a = PG_GETARG_NDBOX(0);
00875     double      result;
00876     int         i,
00877                 j;
00878 
00879     result = 1.0;
00880     for (i = 0, j = a->dim; i < a->dim; i++, j++)
00881         result = result * Abs((a->x[j] - a->x[i]));
00882 
00883     PG_FREE_IF_COPY(a, 0);
00884     PG_RETURN_FLOAT8(result);
00885 }
00886 
00887 void
00888 rt_cube_size(NDBOX *a, double *size)
00889 {
00890     int         i,
00891                 j;
00892 
00893     if (a == (NDBOX *) NULL)
00894         *size = 0.0;
00895     else
00896     {
00897         *size = 1.0;
00898         for (i = 0, j = a->dim; i < a->dim; i++, j++)
00899             *size = (*size) * Abs((a->x[j] - a->x[i]));
00900     }
00901     return;
00902 }
00903 
00904 /* make up a metric in which one box will be 'lower' than the other
00905    -- this can be useful for sorting and to determine uniqueness */
00906 int32
00907 cube_cmp_v0(NDBOX *a, NDBOX *b)
00908 {
00909     int         i;
00910     int         dim;
00911 
00912     dim = Min(a->dim, b->dim);
00913 
00914     /* compare the common dimensions */
00915     for (i = 0; i < dim; i++)
00916     {
00917         if (Min(a->x[i], a->x[a->dim + i]) >
00918             Min(b->x[i], b->x[b->dim + i]))
00919             return 1;
00920         if (Min(a->x[i], a->x[a->dim + i]) <
00921             Min(b->x[i], b->x[b->dim + i]))
00922             return -1;
00923     }
00924     for (i = 0; i < dim; i++)
00925     {
00926         if (Max(a->x[i], a->x[a->dim + i]) >
00927             Max(b->x[i], b->x[b->dim + i]))
00928             return 1;
00929         if (Max(a->x[i], a->x[a->dim + i]) <
00930             Max(b->x[i], b->x[b->dim + i]))
00931             return -1;
00932     }
00933 
00934     /* compare extra dimensions to zero */
00935     if (a->dim > b->dim)
00936     {
00937         for (i = dim; i < a->dim; i++)
00938         {
00939             if (Min(a->x[i], a->x[a->dim + i]) > 0)
00940                 return 1;
00941             if (Min(a->x[i], a->x[a->dim + i]) < 0)
00942                 return -1;
00943         }
00944         for (i = dim; i < a->dim; i++)
00945         {
00946             if (Max(a->x[i], a->x[a->dim + i]) > 0)
00947                 return 1;
00948             if (Max(a->x[i], a->x[a->dim + i]) < 0)
00949                 return -1;
00950         }
00951 
00952         /*
00953          * if all common dimensions are equal, the cube with more dimensions
00954          * wins
00955          */
00956         return 1;
00957     }
00958     if (a->dim < b->dim)
00959     {
00960         for (i = dim; i < b->dim; i++)
00961         {
00962             if (Min(b->x[i], b->x[b->dim + i]) > 0)
00963                 return -1;
00964             if (Min(b->x[i], b->x[b->dim + i]) < 0)
00965                 return 1;
00966         }
00967         for (i = dim; i < b->dim; i++)
00968         {
00969             if (Max(b->x[i], b->x[b->dim + i]) > 0)
00970                 return -1;
00971             if (Max(b->x[i], b->x[b->dim + i]) < 0)
00972                 return 1;
00973         }
00974 
00975         /*
00976          * if all common dimensions are equal, the cube with more dimensions
00977          * wins
00978          */
00979         return -1;
00980     }
00981 
00982     /* They're really equal */
00983     return 0;
00984 }
00985 
00986 Datum
00987 cube_cmp(PG_FUNCTION_ARGS)
00988 {
00989     NDBOX      *a = PG_GETARG_NDBOX(0),
00990                *b = PG_GETARG_NDBOX(1);
00991     int32       res;
00992 
00993     res = cube_cmp_v0(a, b);
00994 
00995     PG_FREE_IF_COPY(a, 0);
00996     PG_FREE_IF_COPY(b, 1);
00997     PG_RETURN_INT32(res);
00998 }
00999 
01000 
01001 Datum
01002 cube_eq(PG_FUNCTION_ARGS)
01003 {
01004     NDBOX      *a = PG_GETARG_NDBOX(0),
01005                *b = PG_GETARG_NDBOX(1);
01006     int32       res;
01007 
01008     res = cube_cmp_v0(a, b);
01009 
01010     PG_FREE_IF_COPY(a, 0);
01011     PG_FREE_IF_COPY(b, 1);
01012     PG_RETURN_BOOL(res == 0);
01013 }
01014 
01015 
01016 Datum
01017 cube_ne(PG_FUNCTION_ARGS)
01018 {
01019     NDBOX      *a = PG_GETARG_NDBOX(0),
01020                *b = PG_GETARG_NDBOX(1);
01021     int32       res;
01022 
01023     res = cube_cmp_v0(a, b);
01024 
01025     PG_FREE_IF_COPY(a, 0);
01026     PG_FREE_IF_COPY(b, 1);
01027     PG_RETURN_BOOL(res != 0);
01028 }
01029 
01030 
01031 Datum
01032 cube_lt(PG_FUNCTION_ARGS)
01033 {
01034     NDBOX      *a = PG_GETARG_NDBOX(0),
01035                *b = PG_GETARG_NDBOX(1);
01036     int32       res;
01037 
01038     res = cube_cmp_v0(a, b);
01039 
01040     PG_FREE_IF_COPY(a, 0);
01041     PG_FREE_IF_COPY(b, 1);
01042     PG_RETURN_BOOL(res < 0);
01043 }
01044 
01045 
01046 Datum
01047 cube_gt(PG_FUNCTION_ARGS)
01048 {
01049     NDBOX      *a = PG_GETARG_NDBOX(0),
01050                *b = PG_GETARG_NDBOX(1);
01051     int32       res;
01052 
01053     res = cube_cmp_v0(a, b);
01054 
01055     PG_FREE_IF_COPY(a, 0);
01056     PG_FREE_IF_COPY(b, 1);
01057     PG_RETURN_BOOL(res > 0);
01058 }
01059 
01060 
01061 Datum
01062 cube_le(PG_FUNCTION_ARGS)
01063 {
01064     NDBOX      *a = PG_GETARG_NDBOX(0),
01065                *b = PG_GETARG_NDBOX(1);
01066     int32       res;
01067 
01068     res = cube_cmp_v0(a, b);
01069 
01070     PG_FREE_IF_COPY(a, 0);
01071     PG_FREE_IF_COPY(b, 1);
01072     PG_RETURN_BOOL(res <= 0);
01073 }
01074 
01075 
01076 Datum
01077 cube_ge(PG_FUNCTION_ARGS)
01078 {
01079     NDBOX      *a = PG_GETARG_NDBOX(0),
01080                *b = PG_GETARG_NDBOX(1);
01081     int32       res;
01082 
01083     res = cube_cmp_v0(a, b);
01084 
01085     PG_FREE_IF_COPY(a, 0);
01086     PG_FREE_IF_COPY(b, 1);
01087     PG_RETURN_BOOL(res >= 0);
01088 }
01089 
01090 
01091 /* Contains */
01092 /* Box(A) CONTAINS Box(B) IFF pt(A) < pt(B) */
01093 bool
01094 cube_contains_v0(NDBOX *a, NDBOX *b)
01095 {
01096     int         i;
01097 
01098     if ((a == NULL) || (b == NULL))
01099         return (FALSE);
01100 
01101     if (a->dim < b->dim)
01102     {
01103         /*
01104          * the further comparisons will make sense if the excess dimensions of
01105          * (b) were zeroes Since both UL and UR coordinates must be zero, we
01106          * can check them all without worrying about which is which.
01107          */
01108         for (i = a->dim; i < b->dim; i++)
01109         {
01110             if (b->x[i] != 0)
01111                 return (FALSE);
01112             if (b->x[i + b->dim] != 0)
01113                 return (FALSE);
01114         }
01115     }
01116 
01117     /* Can't care less about the excess dimensions of (a), if any */
01118     for (i = 0; i < Min(a->dim, b->dim); i++)
01119     {
01120         if (Min(a->x[i], a->x[a->dim + i]) >
01121             Min(b->x[i], b->x[b->dim + i]))
01122             return (FALSE);
01123         if (Max(a->x[i], a->x[a->dim + i]) <
01124             Max(b->x[i], b->x[b->dim + i]))
01125             return (FALSE);
01126     }
01127 
01128     return (TRUE);
01129 }
01130 
01131 Datum
01132 cube_contains(PG_FUNCTION_ARGS)
01133 {
01134     NDBOX      *a = PG_GETARG_NDBOX(0),
01135                *b = PG_GETARG_NDBOX(1);
01136     bool        res;
01137 
01138     res = cube_contains_v0(a, b);
01139 
01140     PG_FREE_IF_COPY(a, 0);
01141     PG_FREE_IF_COPY(b, 1);
01142     PG_RETURN_BOOL(res);
01143 }
01144 
01145 /* Contained */
01146 /* Box(A) Contained by Box(B) IFF Box(B) Contains Box(A) */
01147 Datum
01148 cube_contained(PG_FUNCTION_ARGS)
01149 {
01150     NDBOX      *a = PG_GETARG_NDBOX(0),
01151                *b = PG_GETARG_NDBOX(1);
01152     bool        res;
01153 
01154     res = cube_contains_v0(b, a);
01155 
01156     PG_FREE_IF_COPY(a, 0);
01157     PG_FREE_IF_COPY(b, 1);
01158     PG_RETURN_BOOL(res);
01159 }
01160 
01161 /* Overlap */
01162 /* Box(A) Overlap Box(B) IFF (pt(a)LL < pt(B)UR) && (pt(b)LL < pt(a)UR) */
01163 bool
01164 cube_overlap_v0(NDBOX *a, NDBOX *b)
01165 {
01166     int         i;
01167 
01168     /*
01169      * This *very bad* error was found in the source: if ( (a==NULL) ||
01170      * (b=NULL) ) return(FALSE);
01171      */
01172     if ((a == NULL) || (b == NULL))
01173         return (FALSE);
01174 
01175     /* swap the box pointers if needed */
01176     if (a->dim < b->dim)
01177     {
01178         NDBOX      *tmp = b;
01179 
01180         b = a;
01181         a = tmp;
01182     }
01183 
01184     /* compare within the dimensions of (b) */
01185     for (i = 0; i < b->dim; i++)
01186     {
01187         if (Min(a->x[i], a->x[a->dim + i]) >
01188             Max(b->x[i], b->x[b->dim + i]))
01189             return (FALSE);
01190         if (Max(a->x[i], a->x[a->dim + i]) <
01191             Min(b->x[i], b->x[b->dim + i]))
01192             return (FALSE);
01193     }
01194 
01195     /* compare to zero those dimensions in (a) absent in (b) */
01196     for (i = b->dim; i < a->dim; i++)
01197     {
01198         if (Min(a->x[i], a->x[a->dim + i]) > 0)
01199             return (FALSE);
01200         if (Max(a->x[i], a->x[a->dim + i]) < 0)
01201             return (FALSE);
01202     }
01203 
01204     return (TRUE);
01205 }
01206 
01207 
01208 Datum
01209 cube_overlap(PG_FUNCTION_ARGS)
01210 {
01211     NDBOX      *a = PG_GETARG_NDBOX(0),
01212                *b = PG_GETARG_NDBOX(1);
01213     bool        res;
01214 
01215     res = cube_overlap_v0(a, b);
01216 
01217     PG_FREE_IF_COPY(a, 0);
01218     PG_FREE_IF_COPY(b, 1);
01219     PG_RETURN_BOOL(res);
01220 }
01221 
01222 
01223 /* Distance */
01224 /* The distance is computed as a per axis sum of the squared distances
01225    between 1D projections of the boxes onto Cartesian axes. Assuming zero
01226    distance between overlapping projections, this metric coincides with the
01227    "common sense" geometric distance */
01228 Datum
01229 cube_distance(PG_FUNCTION_ARGS)
01230 {
01231     NDBOX      *a = PG_GETARG_NDBOX(0),
01232                *b = PG_GETARG_NDBOX(1);
01233     bool        swapped = false;
01234     double      d,
01235                 distance;
01236     int         i;
01237 
01238     /* swap the box pointers if needed */
01239     if (a->dim < b->dim)
01240     {
01241         NDBOX      *tmp = b;
01242 
01243         b = a;
01244         a = tmp;
01245         swapped = true;
01246     }
01247 
01248     distance = 0.0;
01249     /* compute within the dimensions of (b) */
01250     for (i = 0; i < b->dim; i++)
01251     {
01252         d = distance_1D(a->x[i], a->x[i + a->dim], b->x[i], b->x[i + b->dim]);
01253         distance += d * d;
01254     }
01255 
01256     /* compute distance to zero for those dimensions in (a) absent in (b) */
01257     for (i = b->dim; i < a->dim; i++)
01258     {
01259         d = distance_1D(a->x[i], a->x[i + a->dim], 0.0, 0.0);
01260         distance += d * d;
01261     }
01262 
01263     if (swapped)
01264     {
01265         PG_FREE_IF_COPY(b, 0);
01266         PG_FREE_IF_COPY(a, 1);
01267     }
01268     else
01269     {
01270         PG_FREE_IF_COPY(a, 0);
01271         PG_FREE_IF_COPY(b, 1);
01272     }
01273 
01274     PG_RETURN_FLOAT8(sqrt(distance));
01275 }
01276 
01277 static double
01278 distance_1D(double a1, double a2, double b1, double b2)
01279 {
01280     /* interval (a) is entirely on the left of (b) */
01281     if ((a1 <= b1) && (a2 <= b1) && (a1 <= b2) && (a2 <= b2))
01282         return (Min(b1, b2) - Max(a1, a2));
01283 
01284     /* interval (a) is entirely on the right of (b) */
01285     if ((a1 > b1) && (a2 > b1) && (a1 > b2) && (a2 > b2))
01286         return (Min(a1, a2) - Max(b1, b2));
01287 
01288     /* the rest are all sorts of intersections */
01289     return (0.0);
01290 }
01291 
01292 /* Test if a box is also a point */
01293 Datum
01294 cube_is_point(PG_FUNCTION_ARGS)
01295 {
01296     NDBOX      *a = PG_GETARG_NDBOX(0);
01297     int         i,
01298                 j;
01299 
01300     for (i = 0, j = a->dim; i < a->dim; i++, j++)
01301     {
01302         if (a->x[i] != a->x[j])
01303             PG_RETURN_BOOL(FALSE);
01304     }
01305 
01306     PG_FREE_IF_COPY(a, 0);
01307     PG_RETURN_BOOL(TRUE);
01308 }
01309 
01310 /* Return dimensions in use in the data structure */
01311 Datum
01312 cube_dim(PG_FUNCTION_ARGS)
01313 {
01314     NDBOX      *c = PG_GETARG_NDBOX(0);
01315     int         dim = c->dim;
01316 
01317     PG_FREE_IF_COPY(c, 0);
01318     PG_RETURN_INT32(dim);
01319 }
01320 
01321 /* Return a specific normalized LL coordinate */
01322 Datum
01323 cube_ll_coord(PG_FUNCTION_ARGS)
01324 {
01325     NDBOX      *c = PG_GETARG_NDBOX(0);
01326     int         n = PG_GETARG_INT16(1);
01327     double      result;
01328 
01329     if (c->dim >= n && n > 0)
01330         result = Min(c->x[n - 1], c->x[c->dim + n - 1]);
01331     else
01332         result = 0;
01333 
01334     PG_FREE_IF_COPY(c, 0);
01335     PG_RETURN_FLOAT8(result);
01336 }
01337 
01338 /* Return a specific normalized UR coordinate */
01339 Datum
01340 cube_ur_coord(PG_FUNCTION_ARGS)
01341 {
01342     NDBOX      *c = PG_GETARG_NDBOX(0);
01343     int         n = PG_GETARG_INT16(1);
01344     double      result;
01345 
01346     if (c->dim >= n && n > 0)
01347         result = Max(c->x[n - 1], c->x[c->dim + n - 1]);
01348     else
01349         result = 0;
01350 
01351     PG_FREE_IF_COPY(c, 0);
01352     PG_RETURN_FLOAT8(result);
01353 }
01354 
01355 /* Increase or decrease box size by a radius in at least n dimensions. */
01356 Datum
01357 cube_enlarge(PG_FUNCTION_ARGS)
01358 {
01359     NDBOX      *a = PG_GETARG_NDBOX(0);
01360     double      r = PG_GETARG_FLOAT8(1);
01361     int32       n = PG_GETARG_INT32(2);
01362     NDBOX      *result;
01363     int         dim = 0;
01364     int         size;
01365     int         i,
01366                 j,
01367                 k;
01368 
01369     if (n > CUBE_MAX_DIM)
01370         n = CUBE_MAX_DIM;
01371     if (r > 0 && n > 0)
01372         dim = n;
01373     if (a->dim > dim)
01374         dim = a->dim;
01375     size = offsetof(NDBOX, x[0]) +sizeof(double) * dim * 2;
01376     result = (NDBOX *) palloc0(size);
01377     SET_VARSIZE(result, size);
01378     result->dim = dim;
01379     for (i = 0, j = dim, k = a->dim; i < a->dim; i++, j++, k++)
01380     {
01381         if (a->x[i] >= a->x[k])
01382         {
01383             result->x[i] = a->x[k] - r;
01384             result->x[j] = a->x[i] + r;
01385         }
01386         else
01387         {
01388             result->x[i] = a->x[i] - r;
01389             result->x[j] = a->x[k] + r;
01390         }
01391         if (result->x[i] > result->x[j])
01392         {
01393             result->x[i] = (result->x[i] + result->x[j]) / 2;
01394             result->x[j] = result->x[i];
01395         }
01396     }
01397     /* dim > a->dim only if r > 0 */
01398     for (; i < dim; i++, j++)
01399     {
01400         result->x[i] = -r;
01401         result->x[j] = r;
01402     }
01403 
01404     PG_FREE_IF_COPY(a, 0);
01405     PG_RETURN_NDBOX(result);
01406 }
01407 
01408 /* Create a one dimensional box with identical upper and lower coordinates */
01409 Datum
01410 cube_f8(PG_FUNCTION_ARGS)
01411 {
01412     double      x = PG_GETARG_FLOAT8(0);
01413     NDBOX      *result;
01414     int         size;
01415 
01416     size = offsetof(NDBOX, x[0]) +sizeof(double) * 2;
01417     result = (NDBOX *) palloc0(size);
01418     SET_VARSIZE(result, size);
01419     result->dim = 1;
01420     result->x[0] = result->x[1] = x;
01421 
01422     PG_RETURN_NDBOX(result);
01423 }
01424 
01425 /* Create a one dimensional box */
01426 Datum
01427 cube_f8_f8(PG_FUNCTION_ARGS)
01428 {
01429     double      x0 = PG_GETARG_FLOAT8(0);
01430     double      x1 = PG_GETARG_FLOAT8(1);
01431     NDBOX      *result;
01432     int         size;
01433 
01434     size = offsetof(NDBOX, x[0]) +sizeof(double) * 2;
01435     result = (NDBOX *) palloc0(size);
01436     SET_VARSIZE(result, size);
01437     result->dim = 1;
01438     result->x[0] = x0;
01439     result->x[1] = x1;
01440 
01441     PG_RETURN_NDBOX(result);
01442 }
01443 
01444 /* Add a dimension to an existing cube with the same values for the new
01445    coordinate */
01446 Datum
01447 cube_c_f8(PG_FUNCTION_ARGS)
01448 {
01449     NDBOX      *c = PG_GETARG_NDBOX(0);
01450     double      x = PG_GETARG_FLOAT8(1);
01451     NDBOX      *result;
01452     int         size;
01453     int         i;
01454 
01455     size = offsetof(NDBOX, x[0]) +sizeof(double) * (c->dim + 1) *2;
01456     result = (NDBOX *) palloc0(size);
01457     SET_VARSIZE(result, size);
01458     result->dim = c->dim + 1;
01459     for (i = 0; i < c->dim; i++)
01460     {
01461         result->x[i] = c->x[i];
01462         result->x[result->dim + i] = c->x[c->dim + i];
01463     }
01464     result->x[result->dim - 1] = x;
01465     result->x[2 * result->dim - 1] = x;
01466 
01467     PG_FREE_IF_COPY(c, 0);
01468     PG_RETURN_NDBOX(result);
01469 }
01470 
01471 /* Add a dimension to an existing cube */
01472 Datum
01473 cube_c_f8_f8(PG_FUNCTION_ARGS)
01474 {
01475     NDBOX      *c = PG_GETARG_NDBOX(0);
01476     double      x1 = PG_GETARG_FLOAT8(1);
01477     double      x2 = PG_GETARG_FLOAT8(2);
01478     NDBOX      *result;
01479     int         size;
01480     int         i;
01481 
01482     size = offsetof(NDBOX, x[0]) +sizeof(double) * (c->dim + 1) *2;
01483     result = (NDBOX *) palloc0(size);
01484     SET_VARSIZE(result, size);
01485     result->dim = c->dim + 1;
01486     for (i = 0; i < c->dim; i++)
01487     {
01488         result->x[i] = c->x[i];
01489         result->x[result->dim + i] = c->x[c->dim + i];
01490     }
01491     result->x[result->dim - 1] = x1;
01492     result->x[2 * result->dim - 1] = x2;
01493 
01494     PG_FREE_IF_COPY(c, 0);
01495     PG_RETURN_NDBOX(result);
01496 }