1 /******************************************************************************
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 ******************************************************************************/
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"
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
);
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))] */
159 cube_in(PG_FUNCTION_ARGS
)
161 char *str
= PG_GETARG_CSTRING(0);
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
179 cube_a_f8_f8(PG_FUNCTION_ARGS
)
181 ArrayType
*ur
= PG_GETARG_ARRAYTYPE_P(0);
182 ArrayType
*ll
= PG_GETARG_ARRAYTYPE_P(1);
190 if (ARR_HASNULL(ur
) || ARR_HASNULL(ll
))
192 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR
),
193 errmsg("cannot work with arrays containing NULLs")));
196 if (ARRNELEMS(ll
) != dim
)
198 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR
),
199 errmsg("UR and LL arrays must be of same length")));
204 size
= offsetof(NDBOX
, x
[0]) + sizeof(double) * 2 * dim
;
205 result
= (NDBOX
*) palloc0(size
);
206 SET_VARSIZE(result
, size
);
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[]
222 cube_a_f8(PG_FUNCTION_ARGS
)
224 ArrayType
*ur
= PG_GETARG_ARRAYTYPE_P(0);
233 (errcode(ERRCODE_ARRAY_ELEMENT_ERROR
),
234 errmsg("cannot work with arrays containing NULLs")));
240 size
= offsetof(NDBOX
, x
[0]) + sizeof(double) * 2 * dim
;
241 result
= (NDBOX
*) palloc0(size
);
242 SET_VARSIZE(result
, size
);
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
);
255 cube_subset(PG_FUNCTION_ARGS
)
257 NDBOX
*c
= PG_GETARG_NDBOX(0);
258 ArrayType
*idx
= PG_GETARG_ARRAYTYPE_P(1);
265 if (ARR_HASNULL(idx
))
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
);
278 for (i
= 0; i
< dim
; i
++)
280 if ((dx
[i
] <= 0) || (dx
[i
] > c
->dim
))
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
);
296 cube_out(PG_FUNCTION_ARGS
)
298 NDBOX
*cube
= PG_GETARG_NDBOX(0);
305 initStringInfo(&buf
);
308 * Get the number of digits to display.
310 ndig
= DBL_DIG
+ extra_float_digits
;
315 * while printing the first (LL) corner, check if it is equal to the
318 appendStringInfoChar(&buf
, '(');
319 for (i
= 0; i
< dim
; i
++)
322 appendStringInfo(&buf
, ", ");
323 appendStringInfo(&buf
, "%.*g", ndig
, cube
->x
[i
]);
324 if (cube
->x
[i
] != cube
->x
[i
+ dim
])
327 appendStringInfoChar(&buf
, ')');
331 appendStringInfo(&buf
, ",(");
332 for (i
= 0; i
< dim
; i
++)
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 /*****************************************************************************
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.
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);
366 /* All cases served by this function are exact */
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
),
377 res
= g_cube_internal_consistent(DatumGetNDBOX(entry
->key
),
380 PG_FREE_IF_COPY(query
, 1);
386 ** The GiST Union method for boxes
387 ** returns the minimal bounding box that encloses all the entries in entryvec
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
;
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
),
416 PG_RETURN_POINTER(out
);
420 ** GiST Compress and Decompress methods for boxes
421 ** do not do anything.
425 g_cube_compress(PG_FUNCTION_ARGS
)
427 PG_RETURN_DATUM(PG_GETARG_DATUM(0));
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
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);
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
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);
506 OffsetNumber seed_1
= 1,
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
);
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
)
555 right
= v
->spl_right
;
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
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.
592 else if (i
== seed_2
)
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
)
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
);
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)
646 * fprintf(stderr, "same: %s\n", (*result ? "TRUE" : "FALSE" ));
648 PG_RETURN_NDBOX(result
);
655 g_cube_leaf_consistent(NDBOX
* key
,
657 StrategyNumber strategy
)
662 * fprintf(stderr, "leaf_consistent, %d\n", strategy);
666 case RTOverlapStrategyNumber
:
667 retval
= (bool) cube_overlap_v0(key
, query
);
669 case RTSameStrategyNumber
:
670 retval
= (bool) (cube_cmp_v0(key
, query
) == 0);
672 case RTContainsStrategyNumber
:
673 case RTOldContainsStrategyNumber
:
674 retval
= (bool) cube_contains_v0(key
, query
);
676 case RTContainedByStrategyNumber
:
677 case RTOldContainedByStrategyNumber
:
678 retval
= (bool) cube_contains_v0(query
, key
);
687 g_cube_internal_consistent(NDBOX
* key
,
689 StrategyNumber strategy
)
694 * fprintf(stderr, "internal_consistent, %d\n", strategy);
698 case RTOverlapStrategyNumber
:
699 retval
= (bool) cube_overlap_v0(key
, query
);
701 case RTSameStrategyNumber
:
702 case RTContainsStrategyNumber
:
703 case RTOldContainsStrategyNumber
:
704 retval
= (bool) cube_contains_v0(key
, query
);
706 case RTContainedByStrategyNumber
:
707 case RTOldContainedByStrategyNumber
:
708 retval
= (bool) cube_overlap_v0(key
, query
);
717 g_cube_binary_union(NDBOX
* r1
, NDBOX
* r2
, int *sizep
)
721 retval
= cube_union_v0(r1
, r2
);
722 *sizep
= VARSIZE(retval
);
730 cube_union_v0(NDBOX
* a
, NDBOX
* b
)
735 if (a
->dim
>= b
->dim
)
737 result
= palloc0(VARSIZE(a
));
738 SET_VARSIZE(result
, VARSIZE(a
));
739 result
->dim
= a
->dim
;
743 result
= palloc0(VARSIZE(b
));
744 SET_VARSIZE(result
, VARSIZE(b
));
745 result
->dim
= b
->dim
;
748 /* swap the box pointers if needed */
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
++)
769 result
->x
[i
+ a
->dim
] = 0;
772 /* compute the union */
773 for (i
= 0; i
< a
->dim
; 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
]);
785 cube_union(PG_FUNCTION_ARGS
)
787 NDBOX
*a
= PG_GETARG_NDBOX(0);
788 NDBOX
*b
= PG_GETARG_NDBOX(1);
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
);
800 cube_inter(PG_FUNCTION_ARGS
)
802 NDBOX
*a
= PG_GETARG_NDBOX(0);
803 NDBOX
*b
= PG_GETARG_NDBOX(1);
805 bool swapped
= false;
808 if (a
->dim
>= b
->dim
)
810 result
= palloc0(VARSIZE(a
));
811 SET_VARSIZE(result
, VARSIZE(a
));
812 result
->dim
= a
->dim
;
816 result
= palloc0(VARSIZE(b
));
817 SET_VARSIZE(result
, VARSIZE(b
));
818 result
->dim
= b
->dim
;
821 /* swap the box pointers if needed */
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
++)
843 result
->x
[i
+ a
->dim
] = 0;
846 /* compute the intersection */
847 for (i
= 0; i
< a
->dim
; 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
]);
857 PG_FREE_IF_COPY(b
, 0);
858 PG_FREE_IF_COPY(a
, 1);
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
);
874 cube_size(PG_FUNCTION_ARGS
)
876 NDBOX
*a
= PG_GETARG_NDBOX(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
);
890 rt_cube_size(NDBOX
* a
, double *size
)
895 if (a
== (NDBOX
*) NULL
)
900 for (i
= 0, j
= a
->dim
; i
< a
->dim
; i
++, j
++)
901 *size
= (*size
) * Abs((a
->x
[j
] - a
->x
[i
]));
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 */
909 cube_cmp_v0(NDBOX
* a
, NDBOX
* b
)
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
]))
922 if (Min(a
->x
[i
], a
->x
[a
->dim
+ i
]) <
923 Min(b
->x
[i
], b
->x
[b
->dim
+ i
]))
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
]))
931 if (Max(a
->x
[i
], a
->x
[a
->dim
+ i
]) <
932 Max(b
->x
[i
], b
->x
[b
->dim
+ i
]))
936 /* compare extra dimensions to zero */
939 for (i
= dim
; i
< a
->dim
; i
++)
941 if (Min(a
->x
[i
], a
->x
[a
->dim
+ i
]) > 0)
943 if (Min(a
->x
[i
], a
->x
[a
->dim
+ i
]) < 0)
946 for (i
= dim
; i
< a
->dim
; i
++)
948 if (Max(a
->x
[i
], a
->x
[a
->dim
+ i
]) > 0)
950 if (Max(a
->x
[i
], a
->x
[a
->dim
+ i
]) < 0)
955 * if all common dimensions are equal, the cube with more dimensions
962 for (i
= dim
; i
< b
->dim
; i
++)
964 if (Min(b
->x
[i
], b
->x
[b
->dim
+ i
]) > 0)
966 if (Min(b
->x
[i
], b
->x
[b
->dim
+ i
]) < 0)
969 for (i
= dim
; i
< b
->dim
; i
++)
971 if (Max(b
->x
[i
], b
->x
[b
->dim
+ i
]) > 0)
973 if (Max(b
->x
[i
], b
->x
[b
->dim
+ i
]) < 0)
978 * if all common dimensions are equal, the cube with more dimensions
984 /* They're really equal */
989 cube_cmp(PG_FUNCTION_ARGS
)
991 NDBOX
*a
= PG_GETARG_NDBOX(0),
992 *b
= PG_GETARG_NDBOX(1);
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
);
1004 cube_eq(PG_FUNCTION_ARGS
)
1006 NDBOX
*a
= PG_GETARG_NDBOX(0),
1007 *b
= PG_GETARG_NDBOX(1);
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);
1019 cube_ne(PG_FUNCTION_ARGS
)
1021 NDBOX
*a
= PG_GETARG_NDBOX(0),
1022 *b
= PG_GETARG_NDBOX(1);
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);
1034 cube_lt(PG_FUNCTION_ARGS
)
1036 NDBOX
*a
= PG_GETARG_NDBOX(0),
1037 *b
= PG_GETARG_NDBOX(1);
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);
1049 cube_gt(PG_FUNCTION_ARGS
)
1051 NDBOX
*a
= PG_GETARG_NDBOX(0),
1052 *b
= PG_GETARG_NDBOX(1);
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);
1064 cube_le(PG_FUNCTION_ARGS
)
1066 NDBOX
*a
= PG_GETARG_NDBOX(0),
1067 *b
= PG_GETARG_NDBOX(1);
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);
1079 cube_ge(PG_FUNCTION_ARGS
)
1081 NDBOX
*a
= PG_GETARG_NDBOX(0),
1082 *b
= PG_GETARG_NDBOX(1);
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);
1094 /* Box(A) CONTAINS Box(B) IFF pt(A) < pt(B) */
1096 cube_contains_v0(NDBOX
* a
, NDBOX
* b
)
1100 if ((a
== NULL
) || (b
== NULL
))
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
++)
1114 if (b
->x
[i
+ b
->dim
] != 0)
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
]))
1125 if (Max(a
->x
[i
], a
->x
[a
->dim
+ i
]) <
1126 Max(b
->x
[i
], b
->x
[b
->dim
+ i
]))
1134 cube_contains(PG_FUNCTION_ARGS
)
1136 NDBOX
*a
= PG_GETARG_NDBOX(0),
1137 *b
= PG_GETARG_NDBOX(1);
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
);
1148 /* Box(A) Contained by Box(B) IFF Box(B) Contains Box(A) */
1150 cube_contained(PG_FUNCTION_ARGS
)
1152 NDBOX
*a
= PG_GETARG_NDBOX(0),
1153 *b
= PG_GETARG_NDBOX(1);
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
);
1164 /* Box(A) Overlap Box(B) IFF (pt(a)LL < pt(B)UR) && (pt(b)LL < pt(a)UR) */
1166 cube_overlap_v0(NDBOX
* a
, NDBOX
* b
)
1171 * This *very bad* error was found in the source: if ( (a==NULL) ||
1172 * (b=NULL) ) return(FALSE);
1174 if ((a
== NULL
) || (b
== NULL
))
1177 /* swap the box pointers if needed */
1178 if (a
->dim
< b
->dim
)
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
]))
1192 if (Max(a
->x
[i
], a
->x
[a
->dim
+ i
]) <
1193 Min(b
->x
[i
], b
->x
[b
->dim
+ i
]))
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)
1202 if (Max(a
->x
[i
], a
->x
[a
->dim
+ i
]) < 0)
1211 cube_overlap(PG_FUNCTION_ARGS
)
1213 NDBOX
*a
= PG_GETARG_NDBOX(0),
1214 *b
= PG_GETARG_NDBOX(1);
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
);
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 */
1231 cube_distance(PG_FUNCTION_ARGS
)
1233 NDBOX
*a
= PG_GETARG_NDBOX(0),
1234 *b
= PG_GETARG_NDBOX(1);
1235 bool swapped
= false;
1240 /* swap the box pointers if needed */
1241 if (a
->dim
< b
->dim
)
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
]);
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);
1267 PG_FREE_IF_COPY(b
, 0);
1268 PG_FREE_IF_COPY(a
, 1);
1272 PG_FREE_IF_COPY(a
, 0);
1273 PG_FREE_IF_COPY(b
, 1);
1276 PG_RETURN_FLOAT8(sqrt(distance
));
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 */
1294 /* Test if a box is also a point */
1296 cube_is_point(PG_FUNCTION_ARGS
)
1298 NDBOX
*a
= PG_GETARG_NDBOX(0);
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 */
1314 cube_dim(PG_FUNCTION_ARGS
)
1316 NDBOX
*c
= PG_GETARG_NDBOX(0);
1319 PG_FREE_IF_COPY(c
, 0);
1320 PG_RETURN_INT32(dim
);
1323 /* Return a specific normalized LL coordinate */
1325 cube_ll_coord(PG_FUNCTION_ARGS
)
1327 NDBOX
*c
= PG_GETARG_NDBOX(0);
1328 int n
= PG_GETARG_INT16(1);
1331 if (c
->dim
>= n
&& n
> 0)
1332 result
= Min(c
->x
[n
- 1], c
->x
[c
->dim
+ n
- 1]);
1336 PG_FREE_IF_COPY(c
, 0);
1337 PG_RETURN_FLOAT8(result
);
1340 /* Return a specific normalized UR coordinate */
1342 cube_ur_coord(PG_FUNCTION_ARGS
)
1344 NDBOX
*c
= PG_GETARG_NDBOX(0);
1345 int n
= PG_GETARG_INT16(1);
1348 if (c
->dim
>= n
&& n
> 0)
1349 result
= Max(c
->x
[n
- 1], c
->x
[c
->dim
+ n
- 1]);
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. */
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);
1371 if (n
> CUBE_MAX_DIM
)
1377 size
= offsetof(NDBOX
, x
[0]) + sizeof(double) * dim
* 2;
1378 result
= (NDBOX
*) palloc0(size
);
1379 SET_VARSIZE(result
, size
);
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
;
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
++)
1406 PG_FREE_IF_COPY(a
, 0);
1407 PG_RETURN_NDBOX(result
);
1410 /* Create a one dimensional box with identical upper and lower coordinates */
1412 cube_f8(PG_FUNCTION_ARGS
)
1414 double x
= PG_GETARG_FLOAT8(0);
1418 size
= offsetof(NDBOX
, x
[0]) + sizeof(double) * 2;
1419 result
= (NDBOX
*) palloc0(size
);
1420 SET_VARSIZE(result
, size
);
1422 result
->x
[0] = result
->x
[1] = x
;
1424 PG_RETURN_NDBOX(result
);
1427 /* Create a one dimensional box */
1429 cube_f8_f8(PG_FUNCTION_ARGS
)
1431 double x0
= PG_GETARG_FLOAT8(0);
1432 double x1
= PG_GETARG_FLOAT8(1);
1436 size
= offsetof(NDBOX
, x
[0]) + sizeof(double) * 2;
1437 result
= (NDBOX
*) palloc0(size
);
1438 SET_VARSIZE(result
, size
);
1443 PG_RETURN_NDBOX(result
);
1446 /* Add a dimension to an existing cube with the same values for the new
1449 cube_c_f8(PG_FUNCTION_ARGS
)
1451 NDBOX
*c
= PG_GETARG_NDBOX(0);
1452 double x
= PG_GETARG_FLOAT8(1);
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 */
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);
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
);