Check nsl library for gethostbyname_r() on all platforms (HP-UX uses it
[PostgreSQL.git] / contrib / cube / cube.c
blob97149950ae61a3e2759f788b0f0b3719fa02f963
1 /******************************************************************************
2 $PostgreSQL$
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 ******************************************************************************/
9 #include "postgres.h"
11 #include <float.h>
12 #include <math.h>
14 #include "access/gist.h"
15 #include "access/skey.h"
16 #include "lib/stringinfo.h"
17 #include "utils/array.h"
18 #include "utils/builtins.h"
20 #include "cubedata.h"
22 PG_MODULE_MAGIC;
25 * Taken from the intarray contrib header
27 #define ARRPTR(x) ( (double *) ARR_DATA_PTR(x) )
28 #define ARRNELEMS(x) ArrayGetNItems( ARR_NDIM(x), ARR_DIMS(x))
30 extern int cube_yyparse();
31 extern void cube_yyerror(const char *message);
32 extern void cube_scanner_init(const char *str);
33 extern void cube_scanner_finish(void);
36 ** Input/Output routines
38 PG_FUNCTION_INFO_V1(cube_in);
39 PG_FUNCTION_INFO_V1(cube);
40 PG_FUNCTION_INFO_V1(cube_a_f8_f8);
41 PG_FUNCTION_INFO_V1(cube_a_f8);
42 PG_FUNCTION_INFO_V1(cube_out);
43 PG_FUNCTION_INFO_V1(cube_f8);
44 PG_FUNCTION_INFO_V1(cube_f8_f8);
45 PG_FUNCTION_INFO_V1(cube_c_f8);
46 PG_FUNCTION_INFO_V1(cube_c_f8_f8);
47 PG_FUNCTION_INFO_V1(cube_dim);
48 PG_FUNCTION_INFO_V1(cube_ll_coord);
49 PG_FUNCTION_INFO_V1(cube_ur_coord);
50 PG_FUNCTION_INFO_V1(cube_subset);
52 Datum cube_in(PG_FUNCTION_ARGS);
53 Datum cube(PG_FUNCTION_ARGS);
54 Datum cube_a_f8_f8(PG_FUNCTION_ARGS);
55 Datum cube_a_f8(PG_FUNCTION_ARGS);
56 Datum cube_out(PG_FUNCTION_ARGS);
57 Datum cube_f8(PG_FUNCTION_ARGS);
58 Datum cube_f8_f8(PG_FUNCTION_ARGS);
59 Datum cube_c_f8(PG_FUNCTION_ARGS);
60 Datum cube_c_f8_f8(PG_FUNCTION_ARGS);
61 Datum cube_dim(PG_FUNCTION_ARGS);
62 Datum cube_ll_coord(PG_FUNCTION_ARGS);
63 Datum cube_ur_coord(PG_FUNCTION_ARGS);
64 Datum cube_subset(PG_FUNCTION_ARGS);
67 ** GiST support methods
70 PG_FUNCTION_INFO_V1(g_cube_consistent);
71 PG_FUNCTION_INFO_V1(g_cube_compress);
72 PG_FUNCTION_INFO_V1(g_cube_decompress);
73 PG_FUNCTION_INFO_V1(g_cube_penalty);
74 PG_FUNCTION_INFO_V1(g_cube_picksplit);
75 PG_FUNCTION_INFO_V1(g_cube_union);
76 PG_FUNCTION_INFO_V1(g_cube_same);
78 Datum g_cube_consistent(PG_FUNCTION_ARGS);
79 Datum g_cube_compress(PG_FUNCTION_ARGS);
80 Datum g_cube_decompress(PG_FUNCTION_ARGS);
81 Datum g_cube_penalty(PG_FUNCTION_ARGS);
82 Datum g_cube_picksplit(PG_FUNCTION_ARGS);
83 Datum g_cube_union(PG_FUNCTION_ARGS);
84 Datum g_cube_same(PG_FUNCTION_ARGS);
87 ** B-tree support functions
89 PG_FUNCTION_INFO_V1(cube_eq);
90 PG_FUNCTION_INFO_V1(cube_ne);
91 PG_FUNCTION_INFO_V1(cube_lt);
92 PG_FUNCTION_INFO_V1(cube_gt);
93 PG_FUNCTION_INFO_V1(cube_le);
94 PG_FUNCTION_INFO_V1(cube_ge);
95 PG_FUNCTION_INFO_V1(cube_cmp);
97 Datum cube_eq(PG_FUNCTION_ARGS);
98 Datum cube_ne(PG_FUNCTION_ARGS);
99 Datum cube_lt(PG_FUNCTION_ARGS);
100 Datum cube_gt(PG_FUNCTION_ARGS);
101 Datum cube_le(PG_FUNCTION_ARGS);
102 Datum cube_ge(PG_FUNCTION_ARGS);
103 Datum cube_cmp(PG_FUNCTION_ARGS);
106 ** R-tree support functions
109 PG_FUNCTION_INFO_V1(cube_contains);
110 PG_FUNCTION_INFO_V1(cube_contained);
111 PG_FUNCTION_INFO_V1(cube_overlap);
112 PG_FUNCTION_INFO_V1(cube_union);
113 PG_FUNCTION_INFO_V1(cube_inter);
114 PG_FUNCTION_INFO_V1(cube_size);
116 Datum cube_contains(PG_FUNCTION_ARGS);
117 Datum cube_contained(PG_FUNCTION_ARGS);
118 Datum cube_overlap(PG_FUNCTION_ARGS);
119 Datum cube_union(PG_FUNCTION_ARGS);
120 Datum cube_inter(PG_FUNCTION_ARGS);
121 Datum cube_size(PG_FUNCTION_ARGS);
124 ** miscellaneous
126 PG_FUNCTION_INFO_V1(cube_distance);
127 PG_FUNCTION_INFO_V1(cube_is_point);
128 PG_FUNCTION_INFO_V1(cube_enlarge);
130 Datum cube_distance(PG_FUNCTION_ARGS);
131 Datum cube_is_point(PG_FUNCTION_ARGS);
132 Datum cube_enlarge(PG_FUNCTION_ARGS);
135 ** For internal use only
137 int32 cube_cmp_v0(NDBOX * a, NDBOX * b);
138 bool cube_contains_v0(NDBOX * a, NDBOX * b);
139 bool cube_overlap_v0(NDBOX * a, NDBOX * b);
140 NDBOX *cube_union_v0(NDBOX * a, NDBOX * b);
141 void rt_cube_size(NDBOX * a, double *sz);
142 NDBOX *g_cube_binary_union(NDBOX * r1, NDBOX * r2, int *sizep);
143 bool g_cube_leaf_consistent(NDBOX * key, NDBOX * query, StrategyNumber strategy);
144 bool g_cube_internal_consistent(NDBOX * key, NDBOX * query, StrategyNumber strategy);
147 ** Auxiliary funxtions
149 static double distance_1D(double a1, double a2, double b1, double b2);
152 /*****************************************************************************
153 * Input/Output functions
154 *****************************************************************************/
156 /* NdBox = [(lowerleft),(upperright)] */
157 /* [(xLL(1)...xLL(N)),(xUR(1)...xUR(n))] */
158 Datum
159 cube_in(PG_FUNCTION_ARGS)
161 char *str = PG_GETARG_CSTRING(0);
162 void *result;
164 cube_scanner_init(str);
166 if (cube_yyparse(&result) != 0)
167 cube_yyerror("bogus input");
169 cube_scanner_finish();
171 PG_RETURN_NDBOX(result);
176 ** Allows the construction of a cube from 2 float[]'s
178 Datum
179 cube_a_f8_f8(PG_FUNCTION_ARGS)
181 ArrayType *ur = PG_GETARG_ARRAYTYPE_P(0);
182 ArrayType *ll = PG_GETARG_ARRAYTYPE_P(1);
183 NDBOX *result;
184 int i;
185 int dim;
186 int size;
187 double *dur,
188 *dll;
190 if (ARR_HASNULL(ur) || ARR_HASNULL(ll))
191 ereport(ERROR,
192 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
193 errmsg("cannot work with arrays containing NULLs")));
195 dim = ARRNELEMS(ur);
196 if (ARRNELEMS(ll) != dim)
197 ereport(ERROR,
198 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
199 errmsg("UR and LL arrays must be of same length")));
201 dur = ARRPTR(ur);
202 dll = ARRPTR(ll);
204 size = offsetof(NDBOX, x[0]) + sizeof(double) * 2 * dim;
205 result = (NDBOX *) palloc0(size);
206 SET_VARSIZE(result, size);
207 result->dim = dim;
209 for (i = 0; i < dim; i++)
211 result->x[i] = dur[i];
212 result->x[i + dim] = dll[i];
215 PG_RETURN_NDBOX(result);
219 ** Allows the construction of a zero-volume cube from a float[]
221 Datum
222 cube_a_f8(PG_FUNCTION_ARGS)
224 ArrayType *ur = PG_GETARG_ARRAYTYPE_P(0);
225 NDBOX *result;
226 int i;
227 int dim;
228 int size;
229 double *dur;
231 if (ARR_HASNULL(ur))
232 ereport(ERROR,
233 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
234 errmsg("cannot work with arrays containing NULLs")));
236 dim = ARRNELEMS(ur);
238 dur = ARRPTR(ur);
240 size = offsetof(NDBOX, x[0]) + sizeof(double) * 2 * dim;
241 result = (NDBOX *) palloc0(size);
242 SET_VARSIZE(result, size);
243 result->dim = dim;
245 for (i = 0; i < dim; i++)
247 result->x[i] = dur[i];
248 result->x[i + dim] = dur[i];
251 PG_RETURN_NDBOX(result);
254 Datum
255 cube_subset(PG_FUNCTION_ARGS)
257 NDBOX *c = PG_GETARG_NDBOX(0);
258 ArrayType *idx = PG_GETARG_ARRAYTYPE_P(1);
259 NDBOX *result;
260 int size,
261 dim,
263 int *dx;
265 if (ARR_HASNULL(idx))
266 ereport(ERROR,
267 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
268 errmsg("cannot work with arrays containing NULLs")));
270 dx = (int4 *) ARR_DATA_PTR(idx);
272 dim = ARRNELEMS(idx);
273 size = offsetof(NDBOX, x[0]) + sizeof(double) * 2 * dim;
274 result = (NDBOX *) palloc0(size);
275 SET_VARSIZE(result, size);
276 result->dim = dim;
278 for (i = 0; i < dim; i++)
280 if ((dx[i] <= 0) || (dx[i] > c->dim))
282 pfree(result);
283 ereport(ERROR,
284 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
285 errmsg("Index out of bounds")));
287 result->x[i] = c->x[dx[i] - 1];
288 result->x[i + dim] = c->x[dx[i] + c->dim - 1];
291 PG_FREE_IF_COPY(c, 0);
292 PG_RETURN_NDBOX(result);
295 Datum
296 cube_out(PG_FUNCTION_ARGS)
298 NDBOX *cube = PG_GETARG_NDBOX(0);
299 StringInfoData buf;
300 int dim = cube->dim;
301 bool equal = true;
302 int i;
303 int ndig;
305 initStringInfo(&buf);
308 * Get the number of digits to display.
310 ndig = DBL_DIG + extra_float_digits;
311 if (ndig < 1)
312 ndig = 1;
315 * while printing the first (LL) corner, check if it is equal to the
316 * second one
318 appendStringInfoChar(&buf, '(');
319 for (i = 0; i < dim; i++)
321 if (i > 0)
322 appendStringInfo(&buf, ", ");
323 appendStringInfo(&buf, "%.*g", ndig, cube->x[i]);
324 if (cube->x[i] != cube->x[i + dim])
325 equal = false;
327 appendStringInfoChar(&buf, ')');
329 if (!equal)
331 appendStringInfo(&buf, ",(");
332 for (i = 0; i < dim; i++)
334 if (i > 0)
335 appendStringInfo(&buf, ", ");
336 appendStringInfo(&buf, "%.*g", ndig, cube->x[i + dim]);
338 appendStringInfoChar(&buf, ')');
341 PG_FREE_IF_COPY(cube, 0);
342 PG_RETURN_CSTRING(buf.data);
346 /*****************************************************************************
347 * GiST functions
348 *****************************************************************************/
351 ** The GiST Consistent method for boxes
352 ** Should return false if for all data items x below entry,
353 ** the predicate x op query == FALSE, where op is the oper
354 ** corresponding to strategy in the pg_amop table.
356 Datum
357 g_cube_consistent(PG_FUNCTION_ARGS)
359 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
360 NDBOX *query = PG_GETARG_NDBOX(1);
361 StrategyNumber strategy = (StrategyNumber) PG_GETARG_UINT16(2);
362 /* Oid subtype = PG_GETARG_OID(3); */
363 bool *recheck = (bool *) PG_GETARG_POINTER(4);
364 bool res;
366 /* All cases served by this function are exact */
367 *recheck = false;
370 * if entry is not leaf, use g_cube_internal_consistent, else use
371 * g_cube_leaf_consistent
373 if (GIST_LEAF(entry))
374 res = g_cube_leaf_consistent(DatumGetNDBOX(entry->key),
375 query, strategy);
376 else
377 res = g_cube_internal_consistent(DatumGetNDBOX(entry->key),
378 query, strategy);
380 PG_FREE_IF_COPY(query, 1);
381 PG_RETURN_BOOL(res);
386 ** The GiST Union method for boxes
387 ** returns the minimal bounding box that encloses all the entries in entryvec
389 Datum
390 g_cube_union(PG_FUNCTION_ARGS)
392 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
393 int *sizep = (int *) PG_GETARG_POINTER(1);
394 NDBOX *out = (NDBOX *) NULL;
395 NDBOX *tmp;
396 int i;
399 * fprintf(stderr, "union\n");
401 tmp = DatumGetNDBOX(entryvec->vector[0].key);
404 * sizep = sizeof(NDBOX); -- NDBOX has variable size
406 *sizep = VARSIZE(tmp);
408 for (i = 1; i < entryvec->n; i++)
410 out = g_cube_binary_union(tmp,
411 DatumGetNDBOX(entryvec->vector[i].key),
412 sizep);
413 tmp = out;
416 PG_RETURN_POINTER(out);
420 ** GiST Compress and Decompress methods for boxes
421 ** do not do anything.
424 Datum
425 g_cube_compress(PG_FUNCTION_ARGS)
427 PG_RETURN_DATUM(PG_GETARG_DATUM(0));
430 Datum
431 g_cube_decompress(PG_FUNCTION_ARGS)
433 GISTENTRY *entry = (GISTENTRY *) PG_GETARG_POINTER(0);
434 NDBOX *key = DatumGetNDBOX(PG_DETOAST_DATUM(entry->key));
436 if (key != DatumGetNDBOX(entry->key))
438 GISTENTRY *retval = (GISTENTRY *) palloc(sizeof(GISTENTRY));
440 gistentryinit(*retval, PointerGetDatum(key),
441 entry->rel, entry->page,
442 entry->offset, FALSE);
443 PG_RETURN_POINTER(retval);
445 PG_RETURN_POINTER(entry);
450 ** The GiST Penalty method for boxes
451 ** As in the R-tree paper, we use change in area as our penalty metric
453 Datum
454 g_cube_penalty(PG_FUNCTION_ARGS)
456 GISTENTRY *origentry = (GISTENTRY *) PG_GETARG_POINTER(0);
457 GISTENTRY *newentry = (GISTENTRY *) PG_GETARG_POINTER(1);
458 float *result = (float *) PG_GETARG_POINTER(2);
459 NDBOX *ud;
460 double tmp1,
461 tmp2;
463 ud = cube_union_v0(DatumGetNDBOX(origentry->key),
464 DatumGetNDBOX(newentry->key));
465 rt_cube_size(ud, &tmp1);
466 rt_cube_size(DatumGetNDBOX(origentry->key), &tmp2);
467 *result = (float) (tmp1 - tmp2);
470 * fprintf(stderr, "penalty\n"); fprintf(stderr, "\t%g\n", *result);
472 PG_RETURN_FLOAT8(*result);
478 ** The GiST PickSplit method for boxes
479 ** We use Guttman's poly time split algorithm
481 Datum
482 g_cube_picksplit(PG_FUNCTION_ARGS)
484 GistEntryVector *entryvec = (GistEntryVector *) PG_GETARG_POINTER(0);
485 GIST_SPLITVEC *v = (GIST_SPLITVEC *) PG_GETARG_POINTER(1);
486 OffsetNumber i,
488 NDBOX *datum_alpha,
489 *datum_beta;
490 NDBOX *datum_l,
491 *datum_r;
492 NDBOX *union_d,
493 *union_dl,
494 *union_dr;
495 NDBOX *inter_d;
496 bool firsttime;
497 double size_alpha,
498 size_beta,
499 size_union,
500 size_inter;
501 double size_waste,
502 waste;
503 double size_l,
504 size_r;
505 int nbytes;
506 OffsetNumber seed_1 = 1,
507 seed_2 = 2;
508 OffsetNumber *left,
509 *right;
510 OffsetNumber maxoff;
513 * fprintf(stderr, "picksplit\n");
515 maxoff = entryvec->n - 2;
516 nbytes = (maxoff + 2) * sizeof(OffsetNumber);
517 v->spl_left = (OffsetNumber *) palloc(nbytes);
518 v->spl_right = (OffsetNumber *) palloc(nbytes);
520 firsttime = true;
521 waste = 0.0;
523 for (i = FirstOffsetNumber; i < maxoff; i = OffsetNumberNext(i))
525 datum_alpha = DatumGetNDBOX(entryvec->vector[i].key);
526 for (j = OffsetNumberNext(i); j <= maxoff; j = OffsetNumberNext(j))
528 datum_beta = DatumGetNDBOX(entryvec->vector[j].key);
530 /* compute the wasted space by unioning these guys */
531 /* size_waste = size_union - size_inter; */
532 union_d = cube_union_v0(datum_alpha, datum_beta);
533 rt_cube_size(union_d, &size_union);
534 inter_d = DatumGetNDBOX(DirectFunctionCall2(cube_inter,
535 entryvec->vector[i].key, entryvec->vector[j].key));
536 rt_cube_size(inter_d, &size_inter);
537 size_waste = size_union - size_inter;
540 * are these a more promising split than what we've already seen?
543 if (size_waste > waste || firsttime)
545 waste = size_waste;
546 seed_1 = i;
547 seed_2 = j;
548 firsttime = false;
553 left = v->spl_left;
554 v->spl_nleft = 0;
555 right = v->spl_right;
556 v->spl_nright = 0;
558 datum_alpha = DatumGetNDBOX(entryvec->vector[seed_1].key);
559 datum_l = cube_union_v0(datum_alpha, datum_alpha);
560 rt_cube_size(datum_l, &size_l);
561 datum_beta = DatumGetNDBOX(entryvec->vector[seed_2].key);
562 datum_r = cube_union_v0(datum_beta, datum_beta);
563 rt_cube_size(datum_r, &size_r);
566 * Now split up the regions between the two seeds. An important property
567 * of this split algorithm is that the split vector v has the indices of
568 * items to be split in order in its left and right vectors. We exploit
569 * this property by doing a merge in the code that actually splits the
570 * page.
572 * For efficiency, we also place the new index tuple in this loop. This is
573 * handled at the very end, when we have placed all the existing tuples
574 * and i == maxoff + 1.
577 maxoff = OffsetNumberNext(maxoff);
578 for (i = FirstOffsetNumber; i <= maxoff; i = OffsetNumberNext(i))
581 * If we've already decided where to place this item, just put it on
582 * the right list. Otherwise, we need to figure out which page needs
583 * the least enlargement in order to store the item.
586 if (i == seed_1)
588 *left++ = i;
589 v->spl_nleft++;
590 continue;
592 else if (i == seed_2)
594 *right++ = i;
595 v->spl_nright++;
596 continue;
599 /* okay, which page needs least enlargement? */
600 datum_alpha = DatumGetNDBOX(entryvec->vector[i].key);
601 union_dl = cube_union_v0(datum_l, datum_alpha);
602 union_dr = cube_union_v0(datum_r, datum_alpha);
603 rt_cube_size(union_dl, &size_alpha);
604 rt_cube_size(union_dr, &size_beta);
606 /* pick which page to add it to */
607 if (size_alpha - size_l < size_beta - size_r)
609 datum_l = union_dl;
610 size_l = size_alpha;
611 *left++ = i;
612 v->spl_nleft++;
614 else
616 datum_r = union_dr;
617 size_r = size_alpha;
618 *right++ = i;
619 v->spl_nright++;
622 *left = *right = FirstOffsetNumber; /* sentinel value, see dosplit() */
624 v->spl_ldatum = PointerGetDatum(datum_l);
625 v->spl_rdatum = PointerGetDatum(datum_r);
627 PG_RETURN_POINTER(v);
631 ** Equality method
633 Datum
634 g_cube_same(PG_FUNCTION_ARGS)
636 NDBOX *b1 = PG_GETARG_NDBOX(0);
637 NDBOX *b2 = PG_GETARG_NDBOX(1);
638 bool *result = (bool *) PG_GETARG_POINTER(2);
640 if (cube_cmp_v0(b1, b2) == 0)
641 *result = TRUE;
642 else
643 *result = FALSE;
646 * fprintf(stderr, "same: %s\n", (*result ? "TRUE" : "FALSE" ));
648 PG_RETURN_NDBOX(result);
652 ** SUPPORT ROUTINES
654 bool
655 g_cube_leaf_consistent(NDBOX * key,
656 NDBOX * query,
657 StrategyNumber strategy)
659 bool retval;
662 * fprintf(stderr, "leaf_consistent, %d\n", strategy);
664 switch (strategy)
666 case RTOverlapStrategyNumber:
667 retval = (bool) cube_overlap_v0(key, query);
668 break;
669 case RTSameStrategyNumber:
670 retval = (bool) (cube_cmp_v0(key, query) == 0);
671 break;
672 case RTContainsStrategyNumber:
673 case RTOldContainsStrategyNumber:
674 retval = (bool) cube_contains_v0(key, query);
675 break;
676 case RTContainedByStrategyNumber:
677 case RTOldContainedByStrategyNumber:
678 retval = (bool) cube_contains_v0(query, key);
679 break;
680 default:
681 retval = FALSE;
683 return (retval);
686 bool
687 g_cube_internal_consistent(NDBOX * key,
688 NDBOX * query,
689 StrategyNumber strategy)
691 bool retval;
694 * fprintf(stderr, "internal_consistent, %d\n", strategy);
696 switch (strategy)
698 case RTOverlapStrategyNumber:
699 retval = (bool) cube_overlap_v0(key, query);
700 break;
701 case RTSameStrategyNumber:
702 case RTContainsStrategyNumber:
703 case RTOldContainsStrategyNumber:
704 retval = (bool) cube_contains_v0(key, query);
705 break;
706 case RTContainedByStrategyNumber:
707 case RTOldContainedByStrategyNumber:
708 retval = (bool) cube_overlap_v0(key, query);
709 break;
710 default:
711 retval = FALSE;
713 return (retval);
716 NDBOX *
717 g_cube_binary_union(NDBOX * r1, NDBOX * r2, int *sizep)
719 NDBOX *retval;
721 retval = cube_union_v0(r1, r2);
722 *sizep = VARSIZE(retval);
724 return (retval);
728 /* cube_union_v0 */
729 NDBOX *
730 cube_union_v0(NDBOX * a, NDBOX * b)
732 int i;
733 NDBOX *result;
735 if (a->dim >= b->dim)
737 result = palloc0(VARSIZE(a));
738 SET_VARSIZE(result, VARSIZE(a));
739 result->dim = a->dim;
741 else
743 result = palloc0(VARSIZE(b));
744 SET_VARSIZE(result, VARSIZE(b));
745 result->dim = b->dim;
748 /* swap the box pointers if needed */
749 if (a->dim < b->dim)
751 NDBOX *tmp = b;
753 b = a;
754 a = tmp;
758 * use the potentially smaller of the two boxes (b) to fill in the result,
759 * padding absent dimensions with zeroes
761 for (i = 0; i < b->dim; i++)
763 result->x[i] = Min(b->x[i], b->x[i + b->dim]);
764 result->x[i + a->dim] = Max(b->x[i], b->x[i + b->dim]);
766 for (i = b->dim; i < a->dim; i++)
768 result->x[i] = 0;
769 result->x[i + a->dim] = 0;
772 /* compute the union */
773 for (i = 0; i < a->dim; i++)
775 result->x[i] =
776 Min(Min(a->x[i], a->x[i + a->dim]), result->x[i]);
777 result->x[i + a->dim] = Max(Max(a->x[i],
778 a->x[i + a->dim]), result->x[i + a->dim]);
781 return (result);
784 Datum
785 cube_union(PG_FUNCTION_ARGS)
787 NDBOX *a = PG_GETARG_NDBOX(0);
788 NDBOX *b = PG_GETARG_NDBOX(1);
789 NDBOX *res;
791 res = cube_union_v0(a, b);
793 PG_FREE_IF_COPY(a, 0);
794 PG_FREE_IF_COPY(b, 1);
795 PG_RETURN_NDBOX(res);
798 /* cube_inter */
799 Datum
800 cube_inter(PG_FUNCTION_ARGS)
802 NDBOX *a = PG_GETARG_NDBOX(0);
803 NDBOX *b = PG_GETARG_NDBOX(1);
804 NDBOX *result;
805 bool swapped = false;
806 int i;
808 if (a->dim >= b->dim)
810 result = palloc0(VARSIZE(a));
811 SET_VARSIZE(result, VARSIZE(a));
812 result->dim = a->dim;
814 else
816 result = palloc0(VARSIZE(b));
817 SET_VARSIZE(result, VARSIZE(b));
818 result->dim = b->dim;
821 /* swap the box pointers if needed */
822 if (a->dim < b->dim)
824 NDBOX *tmp = b;
826 b = a;
827 a = tmp;
828 swapped = true;
832 * use the potentially smaller of the two boxes (b) to fill in the
833 * result, padding absent dimensions with zeroes
835 for (i = 0; i < b->dim; i++)
837 result->x[i] = Min(b->x[i], b->x[i + b->dim]);
838 result->x[i + a->dim] = Max(b->x[i], b->x[i + b->dim]);
840 for (i = b->dim; i < a->dim; i++)
842 result->x[i] = 0;
843 result->x[i + a->dim] = 0;
846 /* compute the intersection */
847 for (i = 0; i < a->dim; i++)
849 result->x[i] =
850 Max(Min(a->x[i], a->x[i + a->dim]), result->x[i]);
851 result->x[i + a->dim] = Min(Max(a->x[i],
852 a->x[i + a->dim]), result->x[i + a->dim]);
855 if (swapped)
857 PG_FREE_IF_COPY(b, 0);
858 PG_FREE_IF_COPY(a, 1);
860 else
862 PG_FREE_IF_COPY(a, 0);
863 PG_FREE_IF_COPY(b, 1);
867 * Is it OK to return a non-null intersection for non-overlapping boxes?
869 PG_RETURN_NDBOX(result);
872 /* cube_size */
873 Datum
874 cube_size(PG_FUNCTION_ARGS)
876 NDBOX *a = PG_GETARG_NDBOX(0);
877 double result;
878 int i,
881 result = 1.0;
882 for (i = 0, j = a->dim; i < a->dim; i++, j++)
883 result = result * Abs((a->x[j] - a->x[i]));
885 PG_FREE_IF_COPY(a, 0);
886 PG_RETURN_FLOAT8(result);
889 void
890 rt_cube_size(NDBOX * a, double *size)
892 int i,
895 if (a == (NDBOX *) NULL)
896 *size = 0.0;
897 else
899 *size = 1.0;
900 for (i = 0, j = a->dim; i < a->dim; i++, j++)
901 *size = (*size) * Abs((a->x[j] - a->x[i]));
903 return;
906 /* make up a metric in which one box will be 'lower' than the other
907 -- this can be useful for sorting and to determine uniqueness */
908 int32
909 cube_cmp_v0(NDBOX * a, NDBOX * b)
911 int i;
912 int dim;
914 dim = Min(a->dim, b->dim);
916 /* compare the common dimensions */
917 for (i = 0; i < dim; i++)
919 if (Min(a->x[i], a->x[a->dim + i]) >
920 Min(b->x[i], b->x[b->dim + i]))
921 return 1;
922 if (Min(a->x[i], a->x[a->dim + i]) <
923 Min(b->x[i], b->x[b->dim + i]))
924 return -1;
926 for (i = 0; i < dim; i++)
928 if (Max(a->x[i], a->x[a->dim + i]) >
929 Max(b->x[i], b->x[b->dim + i]))
930 return 1;
931 if (Max(a->x[i], a->x[a->dim + i]) <
932 Max(b->x[i], b->x[b->dim + i]))
933 return -1;
936 /* compare extra dimensions to zero */
937 if (a->dim > b->dim)
939 for (i = dim; i < a->dim; i++)
941 if (Min(a->x[i], a->x[a->dim + i]) > 0)
942 return 1;
943 if (Min(a->x[i], a->x[a->dim + i]) < 0)
944 return -1;
946 for (i = dim; i < a->dim; i++)
948 if (Max(a->x[i], a->x[a->dim + i]) > 0)
949 return 1;
950 if (Max(a->x[i], a->x[a->dim + i]) < 0)
951 return -1;
955 * if all common dimensions are equal, the cube with more dimensions
956 * wins
958 return 1;
960 if (a->dim < b->dim)
962 for (i = dim; i < b->dim; i++)
964 if (Min(b->x[i], b->x[b->dim + i]) > 0)
965 return -1;
966 if (Min(b->x[i], b->x[b->dim + i]) < 0)
967 return 1;
969 for (i = dim; i < b->dim; i++)
971 if (Max(b->x[i], b->x[b->dim + i]) > 0)
972 return -1;
973 if (Max(b->x[i], b->x[b->dim + i]) < 0)
974 return 1;
978 * if all common dimensions are equal, the cube with more dimensions
979 * wins
981 return -1;
984 /* They're really equal */
985 return 0;
988 Datum
989 cube_cmp(PG_FUNCTION_ARGS)
991 NDBOX *a = PG_GETARG_NDBOX(0),
992 *b = PG_GETARG_NDBOX(1);
993 int32 res;
995 res = cube_cmp_v0(a, b);
997 PG_FREE_IF_COPY(a, 0);
998 PG_FREE_IF_COPY(b, 1);
999 PG_RETURN_INT32(res);
1003 Datum
1004 cube_eq(PG_FUNCTION_ARGS)
1006 NDBOX *a = PG_GETARG_NDBOX(0),
1007 *b = PG_GETARG_NDBOX(1);
1008 int32 res;
1010 res = cube_cmp_v0(a, b);
1012 PG_FREE_IF_COPY(a, 0);
1013 PG_FREE_IF_COPY(b, 1);
1014 PG_RETURN_BOOL(res == 0);
1018 Datum
1019 cube_ne(PG_FUNCTION_ARGS)
1021 NDBOX *a = PG_GETARG_NDBOX(0),
1022 *b = PG_GETARG_NDBOX(1);
1023 int32 res;
1025 res = cube_cmp_v0(a, b);
1027 PG_FREE_IF_COPY(a, 0);
1028 PG_FREE_IF_COPY(b, 1);
1029 PG_RETURN_BOOL(res != 0);
1033 Datum
1034 cube_lt(PG_FUNCTION_ARGS)
1036 NDBOX *a = PG_GETARG_NDBOX(0),
1037 *b = PG_GETARG_NDBOX(1);
1038 int32 res;
1040 res = cube_cmp_v0(a, b);
1042 PG_FREE_IF_COPY(a, 0);
1043 PG_FREE_IF_COPY(b, 1);
1044 PG_RETURN_BOOL(res < 0);
1048 Datum
1049 cube_gt(PG_FUNCTION_ARGS)
1051 NDBOX *a = PG_GETARG_NDBOX(0),
1052 *b = PG_GETARG_NDBOX(1);
1053 int32 res;
1055 res = cube_cmp_v0(a, b);
1057 PG_FREE_IF_COPY(a, 0);
1058 PG_FREE_IF_COPY(b, 1);
1059 PG_RETURN_BOOL(res > 0);
1063 Datum
1064 cube_le(PG_FUNCTION_ARGS)
1066 NDBOX *a = PG_GETARG_NDBOX(0),
1067 *b = PG_GETARG_NDBOX(1);
1068 int32 res;
1070 res = cube_cmp_v0(a, b);
1072 PG_FREE_IF_COPY(a, 0);
1073 PG_FREE_IF_COPY(b, 1);
1074 PG_RETURN_BOOL(res <= 0);
1078 Datum
1079 cube_ge(PG_FUNCTION_ARGS)
1081 NDBOX *a = PG_GETARG_NDBOX(0),
1082 *b = PG_GETARG_NDBOX(1);
1083 int32 res;
1085 res = cube_cmp_v0(a, b);
1087 PG_FREE_IF_COPY(a, 0);
1088 PG_FREE_IF_COPY(b, 1);
1089 PG_RETURN_BOOL(res >= 0);
1093 /* Contains */
1094 /* Box(A) CONTAINS Box(B) IFF pt(A) < pt(B) */
1095 bool
1096 cube_contains_v0(NDBOX * a, NDBOX * b)
1098 int i;
1100 if ((a == NULL) || (b == NULL))
1101 return (FALSE);
1103 if (a->dim < b->dim)
1106 * the further comparisons will make sense if the excess dimensions of
1107 * (b) were zeroes Since both UL and UR coordinates must be zero, we
1108 * can check them all without worrying about which is which.
1110 for (i = a->dim; i < b->dim; i++)
1112 if (b->x[i] != 0)
1113 return (FALSE);
1114 if (b->x[i + b->dim] != 0)
1115 return (FALSE);
1119 /* Can't care less about the excess dimensions of (a), if any */
1120 for (i = 0; i < Min(a->dim, b->dim); i++)
1122 if (Min(a->x[i], a->x[a->dim + i]) >
1123 Min(b->x[i], b->x[b->dim + i]))
1124 return (FALSE);
1125 if (Max(a->x[i], a->x[a->dim + i]) <
1126 Max(b->x[i], b->x[b->dim + i]))
1127 return (FALSE);
1130 return (TRUE);
1133 Datum
1134 cube_contains(PG_FUNCTION_ARGS)
1136 NDBOX *a = PG_GETARG_NDBOX(0),
1137 *b = PG_GETARG_NDBOX(1);
1138 bool res;
1140 res = cube_contains_v0(a, b);
1142 PG_FREE_IF_COPY(a, 0);
1143 PG_FREE_IF_COPY(b, 1);
1144 PG_RETURN_BOOL(res);
1147 /* Contained */
1148 /* Box(A) Contained by Box(B) IFF Box(B) Contains Box(A) */
1149 Datum
1150 cube_contained(PG_FUNCTION_ARGS)
1152 NDBOX *a = PG_GETARG_NDBOX(0),
1153 *b = PG_GETARG_NDBOX(1);
1154 bool res;
1156 res = cube_contains_v0(b, a);
1158 PG_FREE_IF_COPY(a, 0);
1159 PG_FREE_IF_COPY(b, 1);
1160 PG_RETURN_BOOL(res);
1163 /* Overlap */
1164 /* Box(A) Overlap Box(B) IFF (pt(a)LL < pt(B)UR) && (pt(b)LL < pt(a)UR) */
1165 bool
1166 cube_overlap_v0(NDBOX * a, NDBOX * b)
1168 int i;
1171 * This *very bad* error was found in the source: if ( (a==NULL) ||
1172 * (b=NULL) ) return(FALSE);
1174 if ((a == NULL) || (b == NULL))
1175 return (FALSE);
1177 /* swap the box pointers if needed */
1178 if (a->dim < b->dim)
1180 NDBOX *tmp = b;
1182 b = a;
1183 a = tmp;
1186 /* compare within the dimensions of (b) */
1187 for (i = 0; i < b->dim; i++)
1189 if (Min(a->x[i], a->x[a->dim + i]) >
1190 Max(b->x[i], b->x[b->dim + i]))
1191 return (FALSE);
1192 if (Max(a->x[i], a->x[a->dim + i]) <
1193 Min(b->x[i], b->x[b->dim + i]))
1194 return (FALSE);
1197 /* compare to zero those dimensions in (a) absent in (b) */
1198 for (i = b->dim; i < a->dim; i++)
1200 if (Min(a->x[i], a->x[a->dim + i]) > 0)
1201 return (FALSE);
1202 if (Max(a->x[i], a->x[a->dim + i]) < 0)
1203 return (FALSE);
1206 return (TRUE);
1210 Datum
1211 cube_overlap(PG_FUNCTION_ARGS)
1213 NDBOX *a = PG_GETARG_NDBOX(0),
1214 *b = PG_GETARG_NDBOX(1);
1215 bool res;
1217 res = cube_overlap_v0(a, b);
1219 PG_FREE_IF_COPY(a, 0);
1220 PG_FREE_IF_COPY(b, 1);
1221 PG_RETURN_BOOL(res);
1225 /* Distance */
1226 /* The distance is computed as a per axis sum of the squared distances
1227 between 1D projections of the boxes onto Cartesian axes. Assuming zero
1228 distance between overlapping projections, this metric coincides with the
1229 "common sense" geometric distance */
1230 Datum
1231 cube_distance(PG_FUNCTION_ARGS)
1233 NDBOX *a = PG_GETARG_NDBOX(0),
1234 *b = PG_GETARG_NDBOX(1);
1235 bool swapped = false;
1236 double d,
1237 distance;
1238 int i;
1240 /* swap the box pointers if needed */
1241 if (a->dim < b->dim)
1243 NDBOX *tmp = b;
1245 b = a;
1246 a = tmp;
1247 swapped = true;
1250 distance = 0.0;
1251 /* compute within the dimensions of (b) */
1252 for (i = 0; i < b->dim; i++)
1254 d = distance_1D(a->x[i], a->x[i + a->dim], b->x[i], b->x[i + b->dim]);
1255 distance += d * d;
1258 /* compute distance to zero for those dimensions in (a) absent in (b) */
1259 for (i = b->dim; i < a->dim; i++)
1261 d = distance_1D(a->x[i], a->x[i + a->dim], 0.0, 0.0);
1262 distance += d * d;
1265 if (swapped)
1267 PG_FREE_IF_COPY(b, 0);
1268 PG_FREE_IF_COPY(a, 1);
1270 else
1272 PG_FREE_IF_COPY(a, 0);
1273 PG_FREE_IF_COPY(b, 1);
1276 PG_RETURN_FLOAT8(sqrt(distance));
1279 static double
1280 distance_1D(double a1, double a2, double b1, double b2)
1282 /* interval (a) is entirely on the left of (b) */
1283 if ((a1 <= b1) && (a2 <= b1) && (a1 <= b2) && (a2 <= b2))
1284 return (Min(b1, b2) - Max(a1, a2));
1286 /* interval (a) is entirely on the right of (b) */
1287 if ((a1 > b1) && (a2 > b1) && (a1 > b2) && (a2 > b2))
1288 return (Min(a1, a2) - Max(b1, b2));
1290 /* the rest are all sorts of intersections */
1291 return (0.0);
1294 /* Test if a box is also a point */
1295 Datum
1296 cube_is_point(PG_FUNCTION_ARGS)
1298 NDBOX *a = PG_GETARG_NDBOX(0);
1299 int i,
1302 for (i = 0, j = a->dim; i < a->dim; i++, j++)
1304 if (a->x[i] != a->x[j])
1305 PG_RETURN_BOOL(FALSE);
1308 PG_FREE_IF_COPY(a, 0);
1309 PG_RETURN_BOOL(TRUE);
1312 /* Return dimensions in use in the data structure */
1313 Datum
1314 cube_dim(PG_FUNCTION_ARGS)
1316 NDBOX *c = PG_GETARG_NDBOX(0);
1317 int dim = c->dim;
1319 PG_FREE_IF_COPY(c, 0);
1320 PG_RETURN_INT32(dim);
1323 /* Return a specific normalized LL coordinate */
1324 Datum
1325 cube_ll_coord(PG_FUNCTION_ARGS)
1327 NDBOX *c = PG_GETARG_NDBOX(0);
1328 int n = PG_GETARG_INT16(1);
1329 double result;
1331 if (c->dim >= n && n > 0)
1332 result = Min(c->x[n - 1], c->x[c->dim + n - 1]);
1333 else
1334 result = 0;
1336 PG_FREE_IF_COPY(c, 0);
1337 PG_RETURN_FLOAT8(result);
1340 /* Return a specific normalized UR coordinate */
1341 Datum
1342 cube_ur_coord(PG_FUNCTION_ARGS)
1344 NDBOX *c = PG_GETARG_NDBOX(0);
1345 int n = PG_GETARG_INT16(1);
1346 double result;
1348 if (c->dim >= n && n > 0)
1349 result = Max(c->x[n - 1], c->x[c->dim + n - 1]);
1350 else
1351 result = 0;
1353 PG_FREE_IF_COPY(c, 0);
1354 PG_RETURN_FLOAT8(result);
1357 /* Increase or decrease box size by a radius in at least n dimensions. */
1358 Datum
1359 cube_enlarge(PG_FUNCTION_ARGS)
1361 NDBOX *a = PG_GETARG_NDBOX(0);
1362 double r = PG_GETARG_FLOAT8(1);
1363 int4 n = PG_GETARG_INT32(2);
1364 NDBOX *result;
1365 int dim = 0;
1366 int size;
1367 int i,
1371 if (n > CUBE_MAX_DIM)
1372 n = CUBE_MAX_DIM;
1373 if (r > 0 && n > 0)
1374 dim = n;
1375 if (a->dim > dim)
1376 dim = a->dim;
1377 size = offsetof(NDBOX, x[0]) + sizeof(double) * dim * 2;
1378 result = (NDBOX *) palloc0(size);
1379 SET_VARSIZE(result, size);
1380 result->dim = dim;
1381 for (i = 0, j = dim, k = a->dim; i < a->dim; i++, j++, k++)
1383 if (a->x[i] >= a->x[k])
1385 result->x[i] = a->x[k] - r;
1386 result->x[j] = a->x[i] + r;
1388 else
1390 result->x[i] = a->x[i] - r;
1391 result->x[j] = a->x[k] + r;
1393 if (result->x[i] > result->x[j])
1395 result->x[i] = (result->x[i] + result->x[j]) / 2;
1396 result->x[j] = result->x[i];
1399 /* dim > a->dim only if r > 0 */
1400 for (; i < dim; i++, j++)
1402 result->x[i] = -r;
1403 result->x[j] = r;
1406 PG_FREE_IF_COPY(a, 0);
1407 PG_RETURN_NDBOX(result);
1410 /* Create a one dimensional box with identical upper and lower coordinates */
1411 Datum
1412 cube_f8(PG_FUNCTION_ARGS)
1414 double x = PG_GETARG_FLOAT8(0);
1415 NDBOX *result;
1416 int size;
1418 size = offsetof(NDBOX, x[0]) + sizeof(double) * 2;
1419 result = (NDBOX *) palloc0(size);
1420 SET_VARSIZE(result, size);
1421 result->dim = 1;
1422 result->x[0] = result->x[1] = x;
1424 PG_RETURN_NDBOX(result);
1427 /* Create a one dimensional box */
1428 Datum
1429 cube_f8_f8(PG_FUNCTION_ARGS)
1431 double x0 = PG_GETARG_FLOAT8(0);
1432 double x1 = PG_GETARG_FLOAT8(1);
1433 NDBOX *result;
1434 int size;
1436 size = offsetof(NDBOX, x[0]) + sizeof(double) * 2;
1437 result = (NDBOX *) palloc0(size);
1438 SET_VARSIZE(result, size);
1439 result->dim = 1;
1440 result->x[0] = x0;
1441 result->x[1] = x1;
1443 PG_RETURN_NDBOX(result);
1446 /* Add a dimension to an existing cube with the same values for the new
1447 coordinate */
1448 Datum
1449 cube_c_f8(PG_FUNCTION_ARGS)
1451 NDBOX *c = PG_GETARG_NDBOX(0);
1452 double x = PG_GETARG_FLOAT8(1);
1453 NDBOX *result;
1454 int size;
1455 int i;
1457 size = offsetof(NDBOX, x[0]) + sizeof(double) * (c->dim + 1) *2;
1458 result = (NDBOX *) palloc0(size);
1459 SET_VARSIZE(result, size);
1460 result->dim = c->dim + 1;
1461 for (i = 0; i < c->dim; i++)
1463 result->x[i] = c->x[i];
1464 result->x[result->dim + i] = c->x[c->dim + i];
1466 result->x[result->dim - 1] = x;
1467 result->x[2 * result->dim - 1] = x;
1469 PG_FREE_IF_COPY(c, 0);
1470 PG_RETURN_NDBOX(result);
1473 /* Add a dimension to an existing cube */
1474 Datum
1475 cube_c_f8_f8(PG_FUNCTION_ARGS)
1477 NDBOX *c = PG_GETARG_NDBOX(0);
1478 double x1 = PG_GETARG_FLOAT8(1);
1479 double x2 = PG_GETARG_FLOAT8(2);
1480 NDBOX *result;
1481 int size;
1482 int i;
1484 size = offsetof(NDBOX, x[0]) + sizeof(double) * (c->dim + 1) *2;
1485 result = (NDBOX *) palloc0(size);
1486 SET_VARSIZE(result, size);
1487 result->dim = c->dim + 1;
1488 for (i = 0; i < c->dim; i++)
1490 result->x[i] = c->x[i];
1491 result->x[result->dim + i] = c->x[c->dim + i];
1493 result->x[result->dim - 1] = x1;
1494 result->x[2 * result->dim - 1] = x2;
1496 PG_FREE_IF_COPY(c, 0);
1497 PG_RETURN_NDBOX(result);