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 : }
|